Skip to content

feat: add pluggable cookie jar primitives#11

Merged
medz merged 3 commits into
mainfrom
feat/pluggable-cookie-jar-primitives
Jun 21, 2026
Merged

feat: add pluggable cookie jar primitives#11
medz merged 3 commits into
mainfrom
feat/pluggable-cookie-jar-primitives

Conversation

@medz

@medz medz commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

  • add CookiePolicy, CookieStore, MemoryCookieStore, CookieJar, and CookieKey for pluggable cookie-jar behavior
  • extend StoredCookie with public rehydration metadata, creation time, last access time, and metadata copy support
  • document the jar API and cover replacement, expiry, host/domain/secure matching, header serialization, sorting, and custom stores

Closes #9.

Validation

  • dart analyze
  • dart test
  • dart pub publish --dry-run

Summary by CodeRabbit

  • New Features

    • Introduced pluggable cookie jar primitives: CookiePolicy for cookie normalization, CookieStore interface for custom persistence, MemoryCookieStore for in-memory storage, and CookieJar for managing policies and storage.
    • Added CookieKey value object for cookie identity.
    • Enhanced cookies with creation and access time tracking.
  • Documentation

    • Updated README with CookieJar usage examples and component documentation.
    • Added CHANGELOG entries documenting new features.

@medz medz added the enhancement New feature or request label Jun 21, 2026
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@medz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 27 minutes and 31 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ba144bc7-b313-4d7b-ae3f-e286d8bb3063

📥 Commits

Reviewing files that changed from the base of the PR and between 39548c6 and a349521.

📒 Files selected for processing (3)
  • lib/src/cookie_jar.dart
  • lib/src/cookie_key.dart
  • test/cookie_jar_test.dart
📝 Walkthrough

Walkthrough

Adds CookieKey (identity value object), extends StoredCookie with creationTime/lastAccessTime timestamps, and introduces CookiePolicy, CookieStore, MemoryCookieStore, and CookieJar as a layered RFC cookie store abstraction. Both types are exported from the package barrel, documented in README and CHANGELOG, and covered by a new test suite.

Changes

Pluggable Cookie Jar Primitives

Layer / File(s) Summary
StoredCookie timestamp metadata
lib/src/stored_cookie.dart, test/stored_cookie_test.dart
StoredCookie constructor, fromCookie, and copyWith are expanded to require and carry creationTime and lastAccessTime; tests assert initialization equality and copyWith preservation/update semantics.
CookieKey identity value object
lib/src/cookie_key.dart
New immutable CookieKey with name, domain, path, a fromStoredCookie factory, and value-based ==/hashCode/toString for use as a map key in cookie stores.
CookiePolicy, CookieStore, and MemoryCookieStore
lib/src/cookie_jar.dart
CookiePolicy normalizes Set-Cookie/Cookie inputs, matches cookies to URIs, sorts by path length then creation time, and serializes header values. CookieStore defines the async persistence interface. MemoryCookieStore implements it with an in-memory CookieKey-keyed map.
CookieJar orchestration and helpers
lib/src/cookie_jar.dart
CookieJar wires policy and store: save/saveCookies normalize and persist with key-based replacement; loadStored/load evict expired cookies and update last-access time; header returns a serialized Cookie header or null. Private helpers _loadByKey and _storeNormalized manage expiry-aware map construction and conditional upsert/delete.
Public exports, README, and CHANGELOG
lib/ocookie.dart, README.md, CHANGELOG.md
Barrel exports cookie_jar.dart and cookie_key.dart; README gains a CookieJar async usage example and Utils descriptions; CHANGELOG documents the new primitives.
CookieJar and CookiePolicy tests
test/cookie_jar_test.dart
Full async test suite for CookieJar (header serialization, replacement, path duplication, host/domain/secure matching, expiry eviction, saveCookies, pluggable store verification) and a CookiePolicy normalization/matching/ordering test; includes _RecordingCookieStore test double.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant CookieJar
  participant CookiePolicy
  participant CookieStore

  rect rgba(70, 130, 180, 0.5)
    Note over App,CookieStore: Save Set-Cookie response headers
    App->>CookieJar: save(loginUri, setCookieValues)
    CookieJar->>CookieStore: loadCandidates(loginUri)
    CookieStore-->>CookieJar: existing StoredCookies
    CookieJar->>CookiePolicy: normalizeSetCookie(value, requestUri)
    CookiePolicy-->>CookieJar: StoredCookie
    CookieJar->>CookieStore: upsert or delete by CookieKey
  end

  rect rgba(34, 139, 34, 0.5)
    Note over App,CookieStore: Build Cookie request header
    App->>CookieJar: header(profileUri)
    CookieJar->>CookieStore: loadCandidates(profileUri)
    CookieStore-->>CookieJar: candidate StoredCookies
    CookieJar->>CookiePolicy: matches(cookie, uri, now)
    CookieJar->>CookiePolicy: sortForHeader(matched)
    CookieJar->>CookiePolicy: toRequestHeaderValue(sorted)
    CookieJar-->>App: serialized Cookie header or null
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #9 (Add pluggable cookie jar primitives): This PR directly implements the proposed CookiePolicy, CookieStore, CookieJar, MemoryCookieStore, and CookieKey shape described in the issue, including all the specified behaviors (host-only, domain matching, path ordering, expiry eviction, secure constraint, pluggable store boundary).

Possibly related PRs

  • odroe/ocookie#10: Introduced StoredCookie normalization and matching primitives that this PR extends with creationTime/lastAccessTime and layers CookiePolicy/CookieJar on top of.

Poem

🐇 Hop hop, a jar full of crumbs,
Each cookie stored where RFC hums.
CookieKey names the treat by name/path/domain,
CookiePolicy matches—no cookie's alone.
The jar saves, evicts, and sorts with care,
A pluggable store for cookies to share! 🍪

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add pluggable cookie jar primitives' directly and clearly summarizes the main change introduced in the PR.
Linked Issues check ✅ Passed The PR fully addresses issue #9 by implementing all requested coding requirements: CookiePolicy with RFC matching logic, pluggable CookieStore interface, MemoryCookieStore in-memory implementation, CookieJar coordination layer, CookieKey identifiers, StoredCookie time metadata, and comprehensive test coverage validating expected behaviors.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue #9 objectives: CHANGELOG and README documentation updates, public API exports, new cookie jar primitives, and tests—no unrelated or out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/pluggable-cookie-jar-primitives

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39548c650a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/src/cookie_jar.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
lib/src/cookie_key.dart (1)

29-36: 💤 Low value

Remove redundant runtimeType check in equality operator.

Since CookieKey is declared as final class (line 4), it cannot be subclassed. The runtimeType == other.runtimeType check on line 33 is therefore redundant—the other is CookieKey check on line 32 already guarantees the type.

♻️ Simplify the equality operator
 `@override`
 bool operator ==(Object other) =>
     identical(this, other) ||
     other is CookieKey &&
-        runtimeType == other.runtimeType &&
         name == other.name &&
         domain == other.domain &&
         path == other.path;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/cookie_key.dart` around lines 29 - 36, The equality operator in the
CookieKey class contains a redundant runtimeType check. Since CookieKey is
declared as a final class (line 4), it cannot be subclassed, making the
runtimeType == other.runtimeType check on line 33 unnecessary because the other
is CookieKey check on line 32 already guarantees the type is correct. Remove the
runtimeType == other.runtimeType && portion from the operator == implementation
to simplify the equality comparison while maintaining the same logic.
lib/src/cookie_jar.dart (1)

202-213: 💤 Low value

Redundant sorting in header() path.

loadStored() returns cookies already sorted by policy.sortForHeader() (line 189), but toRequestHeaderValue() sorts them again internally (line 69). Consider adding a join-only helper to avoid the redundant O(n log n) work on each header() call:

♻️ Suggested refactor
+  /// Joins pre-sorted cookies without re-sorting.
+  String _joinForHeader(
+    Iterable<StoredCookie> cookies, {
+    CookieCodec? encode,
+  }) {
+    return cookies
+        .map((cookie) => policy.toRequestCookie(cookie, encode: encode))
+        .join('; ');
+  }
+
   Future<String?> header(
     Uri uri, {
     DateTime? now,
     CookieCodec? encode,
   }) async {
     final cookies = await loadStored(uri, now: now);
     if (cookies.isEmpty) {
       return null;
     }

-    return policy.toRequestHeaderValue(cookies, encode: encode);
+    return _joinForHeader(cookies, encode: encode);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/cookie_jar.dart` around lines 202 - 213, The `header()` method is
performing redundant sorting on cookies because `loadStored()` returns cookies
already sorted by `policy.sortForHeader()`, yet `toRequestHeaderValue()`
internally sorts them again. Create a join-only helper method (or add an
optional parameter to `toRequestHeaderValue()`) that skips the sorting step and
simply joins the already-sorted cookies. Update the `header()` method to use
this new join-only helper instead of calling `toRequestHeaderValue()`,
eliminating the unnecessary O(n log n) sorting operation on each `header()`
call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/src/cookie_jar.dart`:
- Around line 202-213: The `header()` method is performing redundant sorting on
cookies because `loadStored()` returns cookies already sorted by
`policy.sortForHeader()`, yet `toRequestHeaderValue()` internally sorts them
again. Create a join-only helper method (or add an optional parameter to
`toRequestHeaderValue()`) that skips the sorting step and simply joins the
already-sorted cookies. Update the `header()` method to use this new join-only
helper instead of calling `toRequestHeaderValue()`, eliminating the unnecessary
O(n log n) sorting operation on each `header()` call.

In `@lib/src/cookie_key.dart`:
- Around line 29-36: The equality operator in the CookieKey class contains a
redundant runtimeType check. Since CookieKey is declared as a final class (line
4), it cannot be subclassed, making the runtimeType == other.runtimeType check
on line 33 unnecessary because the other is CookieKey check on line 32 already
guarantees the type is correct. Remove the runtimeType == other.runtimeType &&
portion from the operator == implementation to simplify the equality comparison
while maintaining the same logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ef8b670-87de-4100-a5e1-38e1f59dfce6

📥 Commits

Reviewing files that changed from the base of the PR and between d1e2c8d and 39548c6.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • lib/ocookie.dart
  • lib/src/cookie_jar.dart
  • lib/src/cookie_key.dart
  • lib/src/stored_cookie.dart
  • test/cookie_jar_test.dart
  • test/stored_cookie_test.dart

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8eb46ef25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/src/cookie_jar.dart
Comment thread lib/src/cookie_key.dart
@medz medz merged commit 4c424c1 into main Jun 21, 2026
2 checks passed
@medz medz deleted the feat/pluggable-cookie-jar-primitives branch June 21, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add pluggable cookie jar primitives

1 participant