@ferricstore/ferricstore 0.11.11 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +19 -1
  2. package/dist/durability-DlDCsdlo.d.cts +16 -0
  3. package/dist/durability-DplL0SbW.d.ts +16 -0
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +5 -162
  7. package/dist/index.d.ts +5 -162
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal-lEEDZpPH.d.cts +4 -0
  11. package/dist/internal-lEEDZpPH.d.ts +4 -0
  12. package/dist/langgraph.cjs +1548 -0
  13. package/dist/langgraph.cjs.map +1 -0
  14. package/dist/langgraph.d.cts +158 -0
  15. package/dist/langgraph.d.ts +158 -0
  16. package/dist/langgraph.js +1522 -0
  17. package/dist/langgraph.js.map +1 -0
  18. package/dist/openai-agents.cjs +717 -0
  19. package/dist/openai-agents.cjs.map +1 -0
  20. package/dist/openai-agents.d.cts +50 -0
  21. package/dist/openai-agents.d.ts +50 -0
  22. package/dist/openai-agents.js +692 -0
  23. package/dist/openai-agents.js.map +1 -0
  24. package/dist/outcomes-BbFDp3AH.d.ts +160 -0
  25. package/dist/outcomes-DmBwnq0Y.d.cts +160 -0
  26. package/docs/agent-api/assets/hierarchy.js +1 -0
  27. package/docs/agent-api/assets/highlight.css +92 -0
  28. package/docs/agent-api/assets/icons.js +18 -0
  29. package/docs/agent-api/assets/icons.svg +1 -0
  30. package/docs/agent-api/assets/main.js +60 -0
  31. package/docs/agent-api/assets/navigation.js +1 -0
  32. package/docs/agent-api/assets/search.js +1 -0
  33. package/docs/agent-api/assets/style.css +1648 -0
  34. package/docs/agent-api/classes/langgraph.FerricStoreSaver.html +297 -0
  35. package/docs/agent-api/classes/langgraph.FerricStoreStore.html +298 -0
  36. package/docs/agent-api/classes/langgraph.LangGraphFlow.html +190 -0
  37. package/docs/agent-api/classes/langgraph.LangGraphFlowContext.html +158 -0
  38. package/docs/agent-api/classes/langgraph.LangGraphFlowRun.html +133 -0
  39. package/docs/agent-api/classes/openai-agents.FerricStoreSession.html +241 -0
  40. package/docs/agent-api/hierarchy.html +44 -0
  41. package/docs/agent-api/index.html +142 -0
  42. package/docs/agent-api/interfaces/langgraph.FerricFlowHandlerContext.html +94 -0
  43. package/docs/agent-api/interfaces/langgraph.FerricStoreCommandClient.html +77 -0
  44. package/docs/agent-api/interfaces/langgraph.FerricStoreLockOptions.html +81 -0
  45. package/docs/agent-api/interfaces/langgraph.FerricStoreSaverOptions.html +106 -0
  46. package/docs/agent-api/interfaces/langgraph.FerricStoreStoreOptions.html +98 -0
  47. package/docs/agent-api/interfaces/langgraph.InvokableLangGraph.html +83 -0
  48. package/docs/agent-api/interfaces/langgraph.LangGraphFlowOptions.html +116 -0
  49. package/docs/agent-api/interfaces/openai-agents.FerricStoreSessionOptions.html +114 -0
  50. package/docs/agent-api/interfaces/openai-agents.Session.html +208 -0
  51. package/docs/agent-api/interfaces/openai-agents.SessionHistoryRewriteAwareSession.html +230 -0
  52. package/docs/agent-api/interfaces/openai-agents.SessionHistoryTransactionAwareSession.html +237 -0
  53. package/docs/agent-api/modules/langgraph.html +44 -0
  54. package/docs/agent-api/modules/openai-agents.html +44 -0
  55. package/docs/agent-api/modules.html +35 -0
  56. package/docs/agent-api/types/langgraph.LangGraphChannelVersions.html +33 -0
  57. package/docs/agent-api/types/langgraph.LangGraphInvocationConfig.html +33 -0
  58. package/docs/agent-api/types/langgraph.LangGraphOutcomeMapper.html +50 -0
  59. package/docs/agent-api/types/langgraph.LangGraphPendingWrite.html +33 -0
  60. package/docs/agent-api/types/openai-agents.AgentInputItem.html +35 -0
  61. package/docs/agent-frameworks.md +176 -0
  62. package/docs/api/assets/highlight.css +12 -12
  63. package/docs/api/classes/ClaimHydrationError.html +2 -2
  64. package/docs/api/classes/ConnectionClosedError.html +2 -2
  65. package/docs/api/classes/FerricStoreError.html +2 -2
  66. package/docs/api/classes/FlowAlreadyExistsError.html +2 -2
  67. package/docs/api/classes/FlowBatchError.html +2 -2
  68. package/docs/api/classes/FlowNotFoundError.html +2 -2
  69. package/docs/api/classes/FlowQueryError.html +2 -2
  70. package/docs/api/classes/FlowWrongStateError.html +2 -2
  71. package/docs/api/classes/HTTPTransportError.html +2 -2
  72. package/docs/api/classes/InvalidCommandError.html +2 -2
  73. package/docs/api/classes/LeaseRenewalError.html +2 -2
  74. package/docs/api/classes/LockHeldError.html +2 -2
  75. package/docs/api/classes/LockNotOwnedError.html +2 -2
  76. package/docs/api/classes/OverloadedError.html +2 -2
  77. package/docs/api/classes/QueueCompletionError.html +2 -2
  78. package/docs/api/classes/RequestTimeoutError.html +2 -2
  79. package/docs/api/classes/RerouteError.html +2 -2
  80. package/docs/api/classes/StaleLeaseError.html +2 -2
  81. package/docs/api/classes/StalePolicyGenerationError.html +2 -2
  82. package/docs/api/index.html +37 -24
  83. package/docs/api/media/agent-frameworks.md +176 -0
  84. package/docs/api/media/langgraph.ts +23 -0
  85. package/docs/api/media/openai-agents-session.ts +13 -0
  86. package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
  87. package/package.json +43 -4
@@ -19,8 +19,19 @@
19
19
  </code><button type="button">Copy</button></pre>
20
20
 
21
21
  <p>Requires Node.js 22.22 or newer. The SDK ships ESM and CommonJS builds and is tested with Node 22, 24, and 26.</p>
22
+ <p>Agent framework adapters are optional:</p>
23
+ <pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">install</span><span class="hl-1"> </span><span class="hl-2">@ferricstore/ferricstore</span><span class="hl-1"> </span><span class="hl-2">@langchain/langgraph</span><span class="hl-1"> </span><span class="hl-2">@langchain/core</span><br/><span class="hl-3"># or</span><br/><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">install</span><span class="hl-1"> </span><span class="hl-2">@ferricstore/ferricstore</span><span class="hl-1"> </span><span class="hl-2">@openai/agents</span>
24
+ </code><button type="button">Copy</button></pre>
25
+
26
+ <p>Use <code>@ferricstore/ferricstore/langgraph</code> for a LangGraph.js checkpointer,
27
+ long-term <code>BaseStore</code>, and FerricFlow handler bridge. Use
28
+ <code>@ferricstore/ferricstore/openai-agents</code> for an atomic, idempotent OpenAI
29
+ Agents SDK <code>Session</code>. See <a href="media/agent-frameworks.md">docs/agent-frameworks.md</a>.
30
+ The generated
31
+ <a href="https://unpkg.com/@ferricstore/ferricstore/docs/agent-api/modules.html">agent framework API reference</a>
32
+ covers every public adapter class and option.</p>
22
33
  <h2 id="compatibility" class="tsd-anchor-link">Compatibility<a href="#compatibility" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
23
- <p>TypeScript SDK <code>0.11.11</code> requires FerricStore server <code>0.11.4</code> or newer. With
34
+ <p>TypeScript SDK <code>0.12.0</code> requires FerricStore server <code>0.11.4</code> or newer. With
24
35
  FerricStore 0.11.11 it negotiates compact Stream mode 34 for homogeneous auto-ID
25
36
  <code>XADD</code> pipelines and compact Pub/Sub mode 35 for homogeneous <code>PUBLISH</code>
26
37
  pipelines. Native wire protocol v1 and the generic fallback are unchanged.
@@ -28,21 +39,21 @@ Capabilities and response-size limits are negotiated
28
39
  per connection from the HELLO-shaped startup response rather than inferred from
29
40
  a server version table.</p>
30
41
  <p>ESM:</p>
31
- <pre><code class="ts"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-4">JsonCodec</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span>
42
+ <pre><code class="ts"><span class="hl-4">import</span><span class="hl-1"> { </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-5">JsonCodec</span><span class="hl-1"> } </span><span class="hl-4">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span>
32
43
  </code><button type="button">Copy</button></pre>
33
44
 
34
45
  <p>CommonJS:</p>
35
- <pre><code class="js"><span class="hl-5">const</span><span class="hl-1"> { </span><span class="hl-6">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-6">JsonCodec</span><span class="hl-1"> } = </span><span class="hl-0">require</span><span class="hl-1">(</span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">);</span>
46
+ <pre><code class="js"><span class="hl-6">const</span><span class="hl-1"> { </span><span class="hl-7">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-7">JsonCodec</span><span class="hl-1"> } = </span><span class="hl-0">require</span><span class="hl-1">(</span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">);</span>
36
47
  </code><button type="button">Copy</button></pre>
37
48
 
38
49
  <h2 id="run-ferricstore-locally" class="tsd-anchor-link">Run FerricStore Locally<a href="#run-ferricstore-locally" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
39
- <pre><code class="bash"><span class="hl-0">docker</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-5">-p</span><span class="hl-1"> </span><span class="hl-2">6388:6388</span><span class="hl-1"> </span><span class="hl-7">\</span><br/><span class="hl-1"> </span><span class="hl-5">-e</span><span class="hl-1"> </span><span class="hl-2">FERRICSTORE_PROTECTED_MODE=</span><span class="hl-5">false</span><span class="hl-1"> </span><span class="hl-7">\</span><br/><span class="hl-1"> </span><span class="hl-5">-v</span><span class="hl-1"> </span><span class="hl-2">ferricstore_data:/data</span><span class="hl-1"> </span><span class="hl-7">\</span><br/><span class="hl-1"> </span><span class="hl-2">quay.io/ferricstore/ferricstore:0.11.11@sha256:d9f488539f0d6c1a513d2315e7a9c2947cc795b393f3774c9de8ba5e5b5c21b5</span>
50
+ <pre><code class="bash"><span class="hl-0">docker</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-6">-p</span><span class="hl-1"> </span><span class="hl-2">6388:6388</span><span class="hl-1"> </span><span class="hl-8">\</span><br/><span class="hl-1"> </span><span class="hl-6">-e</span><span class="hl-1"> </span><span class="hl-2">FERRICSTORE_PROTECTED_MODE=</span><span class="hl-6">false</span><span class="hl-1"> </span><span class="hl-8">\</span><br/><span class="hl-1"> </span><span class="hl-6">-v</span><span class="hl-1"> </span><span class="hl-2">ferricstore_data:/data</span><span class="hl-1"> </span><span class="hl-8">\</span><br/><span class="hl-1"> </span><span class="hl-2">quay.io/ferricstore/ferricstore:0.11.11@sha256:d9f488539f0d6c1a513d2315e7a9c2947cc795b393f3774c9de8ba5e5b5c21b5</span>
40
51
  </code><button type="button">Copy</button></pre>
41
52
 
42
53
  <h2 id="query-durable-runs" class="tsd-anchor-link">Query durable runs<a href="#query-durable-runs" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
43
54
  <p>Use parameterized FQL for bounded, partition-scoped reads. Cursors are opaque
44
55
  and must be reused with the same query and parameters.</p>
45
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">client</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">);</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">query</span><span class="hl-1"> = </span><span class="hl-2">`FROM runs</span><br/><span class="hl-2">WHERE partition_key = @partition AND type = @type AND state = @state</span><br/><span class="hl-2">ORDER BY updated_at_ms ASC LIMIT 25 RETURN RECORDS`</span><span class="hl-1">;</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">params</span><span class="hl-1"> = { </span><span class="hl-4">partition:</span><span class="hl-1"> </span><span class="hl-2">&quot;partition-a&quot;</span><span class="hl-1">, </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;invoice&quot;</span><span class="hl-1">, </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;queued&quot;</span><span class="hl-1"> };</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">result</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-0">query</span><span class="hl-1">(</span><span class="hl-4">query</span><span class="hl-1">, </span><span class="hl-4">params</span><span class="hl-1">);</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">plan</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-0">explain</span><span class="hl-1">(</span><span class="hl-4">query</span><span class="hl-1">, </span><span class="hl-4">params</span><span class="hl-1">);</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">indexes</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-0">queryIndexes</span><span class="hl-1">();</span>
56
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">client</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">);</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">query</span><span class="hl-1"> = </span><span class="hl-2">`FROM runs</span><br/><span class="hl-2">WHERE partition_key = @partition AND type = @type AND state = @state</span><br/><span class="hl-2">ORDER BY updated_at_ms ASC LIMIT 25 RETURN RECORDS`</span><span class="hl-1">;</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">params</span><span class="hl-1"> = { </span><span class="hl-5">partition:</span><span class="hl-1"> </span><span class="hl-2">&quot;partition-a&quot;</span><span class="hl-1">, </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;invoice&quot;</span><span class="hl-1">, </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;queued&quot;</span><span class="hl-1"> };</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">result</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-0">query</span><span class="hl-1">(</span><span class="hl-5">query</span><span class="hl-1">, </span><span class="hl-5">params</span><span class="hl-1">);</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">plan</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-0">explain</span><span class="hl-1">(</span><span class="hl-5">query</span><span class="hl-1">, </span><span class="hl-5">params</span><span class="hl-1">);</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">indexes</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-0">queryIndexes</span><span class="hl-1">();</span>
46
57
  </code><button type="button">Copy</button></pre>
47
58
 
48
59
  <p>Each index reports <code>coveringFields</code>, which identifies the built-in and dynamic
@@ -57,14 +68,14 @@ complete public record. Projection runs after authorization, authoritative
57
68
  recheck, ordering, and cursor calculation: it reduces retained result data,
58
69
  encoding, network, and client decoding work, but not index scans or hydration.</p>
59
70
  <p>Use the source-aware builder to avoid hand-quoting result selectors:</p>
60
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">projected</span><span class="hl-1"> = </span><span class="hl-0">projectFlowQuery</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;FROM runs WHERE partition_key = @partition AND run_id = @run&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;record&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">FlowProjection</span><span class="hl-1">.</span><span class="hl-4">run</span><span class="hl-1">.</span><span class="hl-4">id</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">FlowProjection</span><span class="hl-1">.</span><span class="hl-4">run</span><span class="hl-1">.</span><span class="hl-4">state</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">FlowProjection</span><span class="hl-1">.</span><span class="hl-4">run</span><span class="hl-1">.</span><span class="hl-0">attribute</span><span class="hl-1">(</span><span class="hl-2">&quot;customer&quot;</span><span class="hl-1">)</span><br/><span class="hl-1">);</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">result</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-0">query</span><span class="hl-1">(</span><span class="hl-4">projected</span><span class="hl-1">, { </span><span class="hl-4">partition:</span><span class="hl-1"> </span><span class="hl-2">&quot;partition-a&quot;</span><span class="hl-1">, </span><span class="hl-4">run:</span><span class="hl-1"> </span><span class="hl-2">&quot;run-1&quot;</span><span class="hl-1"> });</span>
71
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">projected</span><span class="hl-1"> = </span><span class="hl-0">projectFlowQuery</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;FROM runs WHERE partition_key = @partition AND run_id = @run&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;record&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">FlowProjection</span><span class="hl-1">.</span><span class="hl-5">run</span><span class="hl-1">.</span><span class="hl-5">id</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">FlowProjection</span><span class="hl-1">.</span><span class="hl-5">run</span><span class="hl-1">.</span><span class="hl-5">state</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">FlowProjection</span><span class="hl-1">.</span><span class="hl-5">run</span><span class="hl-1">.</span><span class="hl-0">attribute</span><span class="hl-1">(</span><span class="hl-2">&quot;customer&quot;</span><span class="hl-1">)</span><br/><span class="hl-1">);</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">result</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-0">query</span><span class="hl-1">(</span><span class="hl-5">projected</span><span class="hl-1">, { </span><span class="hl-5">partition:</span><span class="hl-1"> </span><span class="hl-2">&quot;partition-a&quot;</span><span class="hl-1">, </span><span class="hl-5">run:</span><span class="hl-1"> </span><span class="hl-2">&quot;run-1&quot;</span><span class="hl-1"> });</span>
61
72
  </code><button type="button">Copy</button></pre>
62
73
 
63
74
  <h2 id="http-transport" class="tsd-anchor-link">HTTP transport<a href="#http-transport" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
64
75
  <p><code>fromUrl</code> accepts <code>http://</code> and <code>https://</code> without changing the command API.
65
76
  HTTP/1.1 uses a persistent keep-alive pool. Set <code>http2: true</code> to use one
66
77
  multiplexed HTTP/2 session per origin:</p>
67
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">client</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;https://ferricstore-http.example.com&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">httpOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">username:</span><span class="hl-1"> </span><span class="hl-2">&quot;default&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">password</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">http2:</span><span class="hl-1"> </span><span class="hl-5">true</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-0">ping</span><span class="hl-1">();</span>
78
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">client</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;https://ferricstore-http.example.com&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">httpOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">username:</span><span class="hl-1"> </span><span class="hl-2">&quot;default&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">password</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">http2:</span><span class="hl-1"> </span><span class="hl-6">true</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-0">ping</span><span class="hl-1">();</span>
68
79
  </code><button type="button">Copy</button></pre>
69
80
 
70
81
  <p>Use <code>bearerToken</code> for Bearer authentication. Basic username/password
@@ -86,7 +97,7 @@ trust. Use <code>ferric://</code> or <code>ferrics://</code> whenever connection
86
97
  required.</p>
87
98
  <p>Run the complete HTTP-compatible integration surface through a real TLS
88
99
  listener with ACL authentication using:</p>
89
- <pre><code class="bash"><span class="hl-4">FERRICSTORE_IMAGE</span><span class="hl-1">=</span><span class="hl-2">quay.io/ferricstore/ferricstore:0.11.11@sha256:d9f488539f0d6c1a513d2315e7a9c2947cc795b393f3774c9de8ba5e5b5c21b5</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1"> </span><span class="hl-2">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:http</span>
100
+ <pre><code class="bash"><span class="hl-5">FERRICSTORE_IMAGE</span><span class="hl-1">=</span><span class="hl-2">quay.io/ferricstore/ferricstore:0.11.11@sha256:d9f488539f0d6c1a513d2315e7a9c2947cc795b393f3774c9de8ba5e5b5c21b5</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1"> </span><span class="hl-2">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:http</span>
90
101
  </code><button type="button">Copy</button></pre>
91
102
 
92
103
  <p>The runner creates a private CA, verifies that unauthenticated access and a
@@ -103,7 +114,7 @@ fan-out; atomic multi-key commands are never split client-side. Pass
103
114
  <code>{ ordered: true }</code> as the second argument to <code>client.pipeline()</code> when later
104
115
  commands depend on earlier ones and the transport may need an individual or
105
116
  cross-route fallback.</p>
106
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrls</span><span class="hl-1">(</span><br/><span class="hl-1"> [</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs0.example.com:6388&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs1.example.com:6388&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs2.example.com:6388&quot;</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">codec:</span><span class="hl-1"> </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">(),</span><br/><span class="hl-1"> </span><span class="hl-4">nativeOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-8">// Use &quot;any&quot; only inside a trusted private network.</span><br/><span class="hl-1"> </span><span class="hl-4">endpointPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;seed_hosts&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-8">// Bound client-side route fan-out; freed slots refill immediately.</span><br/><span class="hl-1"> </span><span class="hl-4">topologyConcurrency:</span><span class="hl-1"> </span><span class="hl-9">16</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">warmConnections:</span><span class="hl-1"> </span><span class="hl-5">true</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">refreshTopology</span><span class="hl-1">();</span><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">route</span><span class="hl-1">(</span><span class="hl-2">&quot;tenant-a:order-1&quot;</span><span class="hl-1">));</span>
117
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrls</span><span class="hl-1">(</span><br/><span class="hl-1"> [</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs0.example.com:6388&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs1.example.com:6388&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;ferric://fs2.example.com:6388&quot;</span><br/><span class="hl-1"> ],</span><br/><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">codec:</span><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">(),</span><br/><span class="hl-1"> </span><span class="hl-5">nativeOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">// Use &quot;any&quot; only inside a trusted private network.</span><br/><span class="hl-1"> </span><span class="hl-5">endpointPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;seed_hosts&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-3">// Bound client-side route fan-out; freed slots refill immediately.</span><br/><span class="hl-1"> </span><span class="hl-5">topologyConcurrency:</span><span class="hl-1"> </span><span class="hl-9">16</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">warmConnections:</span><span class="hl-1"> </span><span class="hl-6">true</span><br/><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">refreshTopology</span><span class="hl-1">();</span><br/><span class="hl-5">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">route</span><span class="hl-1">(</span><span class="hl-2">&quot;tenant-a:order-1&quot;</span><span class="hl-1">));</span>
107
118
  </code><button type="button">Copy</button></pre>
108
119
 
109
120
  <p>Learned topology endpoints are checked before connection. The default
@@ -160,7 +171,7 @@ subscriptions instead.</p>
160
171
  <p>The default integration suite targets one local development server. Real HA,
161
172
  TLS, and authentication deployments can be verified with the opt-in deployment
162
173
  suite:</p>
163
- <pre><code class="bash"><span class="hl-4">FERRICSTORE_HA_URLS</span><span class="hl-1">=</span><span class="hl-2">ferric://fs0:6388,ferric://fs1:6388</span><span class="hl-1"> </span><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span><br/><br/><span class="hl-4">FERRICSTORE_TLS_URL</span><span class="hl-1">=</span><span class="hl-2">ferrics://fs0:6389</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1">FERRICSTORE_TLS_CA_FILE=/path/to/ca.pem </span><span class="hl-7">\</span><br/><span class="hl-1">npm </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span><br/><br/><span class="hl-4">FERRICSTORE_AUTH_URL</span><span class="hl-1">=</span><span class="hl-2">ferric://app:secret@fs0:6388</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1">npm </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span>
174
+ <pre><code class="bash"><span class="hl-5">FERRICSTORE_HA_URLS</span><span class="hl-1">=</span><span class="hl-2">ferric://fs0:6388,ferric://fs1:6388</span><span class="hl-1"> </span><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span><br/><br/><span class="hl-5">FERRICSTORE_TLS_URL</span><span class="hl-1">=</span><span class="hl-2">ferrics://fs0:6389</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1">FERRICSTORE_TLS_CA_FILE=/path/to/ca.pem </span><span class="hl-8">\</span><br/><span class="hl-1">npm </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span><br/><br/><span class="hl-5">FERRICSTORE_AUTH_URL</span><span class="hl-1">=</span><span class="hl-2">ferric://app:secret@fs0:6388</span><span class="hl-1"> </span><span class="hl-0">\</span><br/><span class="hl-1">npm </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration:deployment</span>
164
175
  </code><button type="button">Copy</button></pre>
165
176
 
166
177
  <p>The HA fixture must advertise at least two reachable leader endpoints. The auth
@@ -170,7 +181,7 @@ rejects its plaintext listener. HA TLS/auth options are available through
170
181
  <code>FERRICSTORE_HA_TLS_CA_FILE</code>, <code>FERRICSTORE_HA_TLS_SERVERNAME</code>,
171
182
  <code>FERRICSTORE_HA_USERNAME</code>, and <code>FERRICSTORE_HA_PASSWORD</code>.</p>
172
183
  <p>You can also keep one primary URL and add seeds:</p>
173
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://fs0.example.com:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">nativeOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">haRouting:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">seeds:</span><span class="hl-1"> [</span><span class="hl-2">&quot;ferric://fs1.example.com:6388&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;ferric://fs2.example.com:6388&quot;</span><span class="hl-1">]</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
184
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://fs0.example.com:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">nativeOptions:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">haRouting:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">seeds:</span><span class="hl-1"> [</span><span class="hl-2">&quot;ferric://fs1.example.com:6388&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;ferric://fs2.example.com:6388&quot;</span><span class="hl-1">]</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
174
185
  </code><button type="button">Copy</button></pre>
175
186
 
176
187
  <p>Native connections honor the flow-control windows advertised by the
@@ -192,11 +203,11 @@ bounded client queue. Set <code>nativeOptions.maxQueuedWriteBytes</code> to cont
192
203
  queue (default 64 MiB, or <code>0</code> to reject subsequent writes immediately). Healthy
193
204
  socket writes still go directly to <code>socket.write</code> without entering the queue.</p>
194
205
  <h2 id="durable-queue" class="tsd-anchor-link">Durable Queue<a href="#durable-queue" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
195
- <pre><code class="ts"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-4">JsonCodec</span><span class="hl-1">, </span><span class="hl-4">QueueClient</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">codec:</span><span class="hl-1"> </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">emails</span><span class="hl-1"> = </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">QueueClient</span><span class="hl-1">(</span><span class="hl-4">flow</span><span class="hl-1">).</span><span class="hl-0">queue</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">emails</span><span class="hl-1">.</span><span class="hl-0">enqueue</span><span class="hl-1">(</span><span class="hl-2">&quot;email-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">idempotent:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payload:</span><span class="hl-1"> { </span><span class="hl-4">template:</span><span class="hl-1"> </span><span class="hl-2">&quot;welcome&quot;</span><span class="hl-1">, </span><span class="hl-4">userId:</span><span class="hl-1"> </span><span class="hl-2">&quot;user-1&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">emails</span><span class="hl-1">.</span><span class="hl-0">enqueueMany</span><span class="hl-1">([{ </span><span class="hl-4">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;email-2&quot;</span><span class="hl-1">, </span><span class="hl-4">payload:</span><span class="hl-1"> { </span><span class="hl-4">template:</span><span class="hl-1"> </span><span class="hl-2">&quot;receipt&quot;</span><span class="hl-1"> } }], {</span><br/><span class="hl-1"> </span><span class="hl-4">autoPartitionBatchSize:</span><span class="hl-1"> </span><span class="hl-9">1_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">autoPartitionConcurrency:</span><span class="hl-1"> </span><span class="hl-9">8</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">emails</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({ </span><span class="hl-4">batchSize:</span><span class="hl-1"> </span><span class="hl-9">100</span><span class="hl-1">, </span><span class="hl-4">concurrency:</span><span class="hl-1"> </span><span class="hl-9">16</span><span class="hl-1">, </span><span class="hl-4">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;email-worker-1&quot;</span><span class="hl-1"> }).</span><span class="hl-0">run</span><span class="hl-1">(</span><span class="hl-5">async</span><span class="hl-1"> (</span><span class="hl-4">job</span><span class="hl-1">) </span><span class="hl-5">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">id</span><span class="hl-1">, </span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">payload</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-3">return</span><span class="hl-1"> { </span><span class="hl-4">sent:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1"> };</span><br/><span class="hl-1">});</span>
206
+ <pre><code class="ts"><span class="hl-4">import</span><span class="hl-1"> { </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-5">JsonCodec</span><span class="hl-1">, </span><span class="hl-5">QueueClient</span><span class="hl-1"> } </span><span class="hl-4">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">codec:</span><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">emails</span><span class="hl-1"> = </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">QueueClient</span><span class="hl-1">(</span><span class="hl-5">flow</span><span class="hl-1">).</span><span class="hl-0">queue</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">emails</span><span class="hl-1">.</span><span class="hl-0">enqueue</span><span class="hl-1">(</span><span class="hl-2">&quot;email-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">idempotent:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payload:</span><span class="hl-1"> { </span><span class="hl-5">template:</span><span class="hl-1"> </span><span class="hl-2">&quot;welcome&quot;</span><span class="hl-1">, </span><span class="hl-5">userId:</span><span class="hl-1"> </span><span class="hl-2">&quot;user-1&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">emails</span><span class="hl-1">.</span><span class="hl-0">enqueueMany</span><span class="hl-1">([{ </span><span class="hl-5">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;email-2&quot;</span><span class="hl-1">, </span><span class="hl-5">payload:</span><span class="hl-1"> { </span><span class="hl-5">template:</span><span class="hl-1"> </span><span class="hl-2">&quot;receipt&quot;</span><span class="hl-1"> } }], {</span><br/><span class="hl-1"> </span><span class="hl-5">autoPartitionBatchSize:</span><span class="hl-1"> </span><span class="hl-9">1_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">autoPartitionConcurrency:</span><span class="hl-1"> </span><span class="hl-9">8</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">emails</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({ </span><span class="hl-5">batchSize:</span><span class="hl-1"> </span><span class="hl-9">100</span><span class="hl-1">, </span><span class="hl-5">concurrency:</span><span class="hl-1"> </span><span class="hl-9">16</span><span class="hl-1">, </span><span class="hl-5">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;email-worker-1&quot;</span><span class="hl-1"> }).</span><span class="hl-0">run</span><span class="hl-1">(</span><span class="hl-6">async</span><span class="hl-1"> (</span><span class="hl-5">job</span><span class="hl-1">) </span><span class="hl-6">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">id</span><span class="hl-1">, </span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">payload</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-4">return</span><span class="hl-1"> { </span><span class="hl-5">sent:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1"> };</span><br/><span class="hl-1">});</span>
196
207
  </code><button type="button">Copy</button></pre>
197
208
 
198
209
  <h2 id="explicit-workflow" class="tsd-anchor-link">Explicit Workflow<a href="#explicit-workflow" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
199
- <pre><code class="ts"><span class="hl-3">import</span><span class="hl-1"> { </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-4">JsonCodec</span><span class="hl-1">, </span><span class="hl-4">WorkflowClient</span><span class="hl-1">, </span><span class="hl-4">complete</span><span class="hl-1">, </span><span class="hl-4">transition</span><span class="hl-1"> } </span><span class="hl-3">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">codec:</span><span class="hl-1"> </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">order</span><span class="hl-1"> = </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">WorkflowClient</span><span class="hl-1">(</span><span class="hl-4">flow</span><span class="hl-1">).</span><span class="hl-0">workflow</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-4">initialState:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">order</span><span class="hl-1">.</span><span class="hl-0">state</span><span class="hl-1">(</span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-5">async</span><span class="hl-1"> (</span><span class="hl-4">ctx</span><span class="hl-1">) </span><span class="hl-5">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-0">chargeCard</span><span class="hl-1">(</span><span class="hl-4">ctx</span><span class="hl-1">.</span><span class="hl-4">payload</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-3">return</span><span class="hl-1"> </span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">);</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">order</span><span class="hl-1">.</span><span class="hl-0">state</span><span class="hl-1">(</span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">, </span><span class="hl-5">async</span><span class="hl-1"> (</span><span class="hl-4">ctx</span><span class="hl-1">) </span><span class="hl-5">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-0">sendReceipt</span><span class="hl-1">(</span><span class="hl-4">ctx</span><span class="hl-1">.</span><span class="hl-4">id</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-3">return</span><span class="hl-1"> </span><span class="hl-0">complete</span><span class="hl-1">({ </span><span class="hl-4">result:</span><span class="hl-1"> { </span><span class="hl-4">ok:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1"> } });</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">order</span><span class="hl-1">.</span><span class="hl-0">start</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">idempotent:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payload:</span><span class="hl-1"> { </span><span class="hl-4">amount:</span><span class="hl-1"> </span><span class="hl-9">42</span><span class="hl-1">, </span><span class="hl-4">userId:</span><span class="hl-1"> </span><span class="hl-2">&quot;user-1&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">order</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-4">batchSize:</span><span class="hl-1"> </span><span class="hl-9">50</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">concurrency:</span><span class="hl-1"> </span><span class="hl-9">8</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">states:</span><span class="hl-1"> [</span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-4">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;order-worker-1&quot;</span><br/><span class="hl-1">}).</span><span class="hl-0">run</span><span class="hl-1">();</span>
210
+ <pre><code class="ts"><span class="hl-4">import</span><span class="hl-1"> { </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">, </span><span class="hl-5">JsonCodec</span><span class="hl-1">, </span><span class="hl-5">WorkflowClient</span><span class="hl-1">, </span><span class="hl-5">complete</span><span class="hl-1">, </span><span class="hl-5">transition</span><span class="hl-1"> } </span><span class="hl-4">from</span><span class="hl-1"> </span><span class="hl-2">&quot;@ferricstore/ferricstore&quot;</span><span class="hl-1">;</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">codec:</span><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">order</span><span class="hl-1"> = </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">WorkflowClient</span><span class="hl-1">(</span><span class="hl-5">flow</span><span class="hl-1">).</span><span class="hl-0">workflow</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-5">initialState:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-5">order</span><span class="hl-1">.</span><span class="hl-0">state</span><span class="hl-1">(</span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-6">async</span><span class="hl-1"> (</span><span class="hl-5">ctx</span><span class="hl-1">) </span><span class="hl-6">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-0">chargeCard</span><span class="hl-1">(</span><span class="hl-5">ctx</span><span class="hl-1">.</span><span class="hl-5">payload</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-4">return</span><span class="hl-1"> </span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">);</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-5">order</span><span class="hl-1">.</span><span class="hl-0">state</span><span class="hl-1">(</span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">, </span><span class="hl-6">async</span><span class="hl-1"> (</span><span class="hl-5">ctx</span><span class="hl-1">) </span><span class="hl-6">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-0">sendReceipt</span><span class="hl-1">(</span><span class="hl-5">ctx</span><span class="hl-1">.</span><span class="hl-5">id</span><span class="hl-1">);</span><br/><span class="hl-1"> </span><span class="hl-4">return</span><span class="hl-1"> </span><span class="hl-0">complete</span><span class="hl-1">({ </span><span class="hl-5">result:</span><span class="hl-1"> { </span><span class="hl-5">ok:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1"> } });</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">order</span><span class="hl-1">.</span><span class="hl-0">start</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">idempotent:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payload:</span><span class="hl-1"> { </span><span class="hl-5">amount:</span><span class="hl-1"> </span><span class="hl-9">42</span><span class="hl-1">, </span><span class="hl-5">userId:</span><span class="hl-1"> </span><span class="hl-2">&quot;user-1&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">order</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({</span><br/><span class="hl-1"> </span><span class="hl-5">batchSize:</span><span class="hl-1"> </span><span class="hl-9">50</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">concurrency:</span><span class="hl-1"> </span><span class="hl-9">8</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">states:</span><span class="hl-1"> [</span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-5">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;order-worker-1&quot;</span><br/><span class="hl-1">}).</span><span class="hl-0">run</span><span class="hl-1">();</span>
200
211
  </code><button type="button">Copy</button></pre>
201
212
 
202
213
  <p>Handlers return explicit durable outcomes:</p>
@@ -242,12 +253,12 @@ unchanged; all fenced mutation APIs accept the exported <code>FencingToken</code
242
253
  and native compact claim and batch paths preserve its signed 64-bit value
243
254
  exactly.</p>
244
255
  <h2 id="low-level-flow-commands" class="tsd-anchor-link">Low-Level Flow Commands<a href="#low-level-flow-commands" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
245
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">create</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payload:</span><span class="hl-1"> </span><span class="hl-4">Buffer</span><span class="hl-1">.</span><span class="hl-0">from</span><span class="hl-1">(</span><span class="hl-2">&quot;order payload&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-4">idempotent:</span><span class="hl-1"> </span><span class="hl-5">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">jobs</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">claimDue</span><span class="hl-1">(</span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;worker-1&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">leaseMs:</span><span class="hl-1"> </span><span class="hl-9">30_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">limit:</span><span class="hl-1"> </span><span class="hl-9">10</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payload:</span><span class="hl-1"> </span><span class="hl-5">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">for</span><span class="hl-1"> (</span><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">job</span><span class="hl-1"> </span><span class="hl-5">of</span><span class="hl-1"> </span><span class="hl-4">jobs</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">id</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">fromState:</span><span class="hl-1"> </span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">state</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">toState:</span><span class="hl-1"> </span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">leaseToken:</span><span class="hl-1"> </span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">leaseToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">fencingToken:</span><span class="hl-1"> </span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">fencingToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">partitionKey:</span><span class="hl-1"> </span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">partitionKey</span><br/><span class="hl-1"> });</span><br/><span class="hl-1">}</span>
256
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">create</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payload:</span><span class="hl-1"> </span><span class="hl-5">Buffer</span><span class="hl-1">.</span><span class="hl-0">from</span><span class="hl-1">(</span><span class="hl-2">&quot;order payload&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">idempotent:</span><span class="hl-1"> </span><span class="hl-6">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">jobs</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">claimDue</span><span class="hl-1">(</span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;worker-1&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">leaseMs:</span><span class="hl-1"> </span><span class="hl-9">30_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">limit:</span><span class="hl-1"> </span><span class="hl-9">10</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payload:</span><span class="hl-1"> </span><span class="hl-6">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">for</span><span class="hl-1"> (</span><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">job</span><span class="hl-1"> </span><span class="hl-6">of</span><span class="hl-1"> </span><span class="hl-5">jobs</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">id</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">fromState:</span><span class="hl-1"> </span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">state</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">toState:</span><span class="hl-1"> </span><span class="hl-2">&quot;charged&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">leaseToken:</span><span class="hl-1"> </span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">leaseToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">fencingToken:</span><span class="hl-1"> </span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">fencingToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">partitionKey:</span><span class="hl-1"> </span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">partitionKey</span><br/><span class="hl-1"> });</span><br/><span class="hl-1">}</span>
246
257
  </code><button type="button">Copy</button></pre>
247
258
 
248
259
  <p>Flow attributes can be returned without hydrating each record and can be
249
260
  updated atomically with the fenced state mutation:</p>
250
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">attributed</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">claimDue</span><span class="hl-1">(</span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">includeAttributes:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">jobOnly:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;worker-1&quot;</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-4">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-4">id</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">attributesDelete:</span><span class="hl-1"> [</span><span class="hl-2">&quot;temporary&quot;</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-4">attributesMerge:</span><span class="hl-1"> { </span><span class="hl-4">processor:</span><span class="hl-1"> </span><span class="hl-2">&quot;payments-v2&quot;</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-4">fencingToken:</span><span class="hl-1"> </span><span class="hl-4">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-4">fencingToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">fromState:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">leaseToken:</span><span class="hl-1"> </span><span class="hl-4">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-4">leaseToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">toState:</span><span class="hl-1"> </span><span class="hl-2">&quot;charged&quot;</span><br/><span class="hl-1">});</span>
261
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">attributed</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">claimDue</span><span class="hl-1">(</span><span class="hl-2">&quot;order&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">includeAttributes:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">jobOnly:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">worker:</span><span class="hl-1"> </span><span class="hl-2">&quot;worker-1&quot;</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">transition</span><span class="hl-1">(</span><span class="hl-5">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-5">id</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">attributesDelete:</span><span class="hl-1"> [</span><span class="hl-2">&quot;temporary&quot;</span><span class="hl-1">],</span><br/><span class="hl-1"> </span><span class="hl-5">attributesMerge:</span><span class="hl-1"> { </span><span class="hl-5">processor:</span><span class="hl-1"> </span><span class="hl-2">&quot;payments-v2&quot;</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-5">fencingToken:</span><span class="hl-1"> </span><span class="hl-5">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-5">fencingToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">fromState:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">leaseToken:</span><span class="hl-1"> </span><span class="hl-5">attributed</span><span class="hl-1">[</span><span class="hl-9">0</span><span class="hl-1">]!.</span><span class="hl-5">leaseToken</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">toState:</span><span class="hl-1"> </span><span class="hl-2">&quot;charged&quot;</span><br/><span class="hl-1">});</span>
251
262
  </code><button type="button">Copy</button></pre>
252
263
 
253
264
  <p>The low-level client also exposes the fused <code>startAndClaim</code>, <code>stepContinue</code>,
@@ -255,13 +266,13 @@ and <code>runStepsMany</code> operations, schedule administration, Flow statisti
255
266
  attribute queries, effects, approvals, circuits, budgets, and distributed
256
267
  limits. History supports the complete server filter surface, including event,
257
268
  time, and version bounds plus cold/consistent reads and payload hydration:</p>
258
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">events</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">history</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">consistentProjection:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">fromVersion:</span><span class="hl-1"> </span><span class="hl-9">2</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">includeCold:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payloadMaxBytes:</span><span class="hl-1"> </span><span class="hl-9">64_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">toVersion:</span><span class="hl-1"> </span><span class="hl-9">8</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">values:</span><span class="hl-1"> </span><span class="hl-5">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">scheduleCreate</span><span class="hl-1">(</span><span class="hl-2">&quot;orders-every-five-minutes&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">cron:</span><span class="hl-1"> </span><span class="hl-2">&quot;*/5 * * * *&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">kind:</span><span class="hl-1"> </span><span class="hl-2">&quot;cron&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">target:</span><span class="hl-1"> { </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-4">timezone:</span><span class="hl-1"> </span><span class="hl-2">&quot;UTC&quot;</span><br/><span class="hl-1">});</span>
269
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">events</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">history</span><span class="hl-1">(</span><span class="hl-2">&quot;order-1&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">consistentProjection:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">fromVersion:</span><span class="hl-1"> </span><span class="hl-9">2</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">includeCold:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payloadMaxBytes:</span><span class="hl-1"> </span><span class="hl-9">64_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">toVersion:</span><span class="hl-1"> </span><span class="hl-9">8</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">values:</span><span class="hl-1"> </span><span class="hl-6">true</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">scheduleCreate</span><span class="hl-1">(</span><span class="hl-2">&quot;orders-every-five-minutes&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">cron:</span><span class="hl-1"> </span><span class="hl-2">&quot;*/5 * * * *&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">kind:</span><span class="hl-1"> </span><span class="hl-2">&quot;cron&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">target:</span><span class="hl-1"> { </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;order&quot;</span><span class="hl-1"> },</span><br/><span class="hl-1"> </span><span class="hl-5">timezone:</span><span class="hl-1"> </span><span class="hl-2">&quot;UTC&quot;</span><br/><span class="hl-1">});</span>
259
270
  </code><button type="button">Copy</button></pre>
260
271
 
261
272
  <p>Overdue interval schedules use bounded <code>fire_once</code> catch-up. Recovery creates
262
273
  one target, coalesces additional elapsed periods in constant time, and sets the
263
274
  next run one full interval after recovery:</p>
264
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">schedule</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">scheduleCreate</span><span class="hl-1">(</span><span class="hl-2">&quot;billing-sweep&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">catchupPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;fire_once&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">everyMs:</span><span class="hl-1"> </span><span class="hl-9">60_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">kind:</span><span class="hl-1"> </span><span class="hl-2">&quot;interval&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">overlapPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;queue_after_previous&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">target:</span><span class="hl-1"> { </span><span class="hl-4">id_prefix:</span><span class="hl-1"> </span><span class="hl-2">&quot;billing-sweep&quot;</span><span class="hl-1">, </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;billing&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
275
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">schedule</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">scheduleCreate</span><span class="hl-1">(</span><span class="hl-2">&quot;billing-sweep&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">catchupPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;fire_once&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">everyMs:</span><span class="hl-1"> </span><span class="hl-9">60_000</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">kind:</span><span class="hl-1"> </span><span class="hl-2">&quot;interval&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">overlapPolicy:</span><span class="hl-1"> </span><span class="hl-2">&quot;queue_after_previous&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">target:</span><span class="hl-1"> { </span><span class="hl-5">id_prefix:</span><span class="hl-1"> </span><span class="hl-2">&quot;billing-sweep&quot;</span><span class="hl-1">, </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;billing&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
265
276
  </code><button type="button">Copy</button></pre>
266
277
 
267
278
  <p><code>ScheduleRecord</code> exposes the complete recurrence configuration through
@@ -288,7 +299,7 @@ When planning fails, <code>state</code> is <code>&quot;failed&quot;</code>, <cod
288
299
  <code>&quot;planning_failed&quot;</code>, and <code>last_planning_error</code> contains the actionable error.
289
300
  <code>scheduleDelete()</code> resolves to <code>undefined</code> only after an <code>OK</code> server reply.</p>
290
301
  <p>FIFO Flow state policy is opt-in per state:</p>
291
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">policy</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">states:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">queued:</span><span class="hl-1"> { </span><span class="hl-4">mode:</span><span class="hl-1"> </span><span class="hl-2">&quot;fifo&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-8">// Direct writes deep-patch by default. Fence concurrent editors with generation CAS.</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">updated</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">expectedGeneration:</span><span class="hl-1"> </span><span class="hl-4">policy</span><span class="hl-1">.</span><span class="hl-4">generation</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">maxActiveMs:</span><span class="hl-1"> </span><span class="hl-9">300_000</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-8">// Full replacement is explicit on the client API.</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">expectedGeneration:</span><span class="hl-1"> </span><span class="hl-4">updated</span><span class="hl-1">.</span><span class="hl-4">generation</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">replace:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">states:</span><span class="hl-1"> { </span><span class="hl-4">queued:</span><span class="hl-1"> { </span><span class="hl-4">mode:</span><span class="hl-1"> </span><span class="hl-2">&quot;fifo&quot;</span><span class="hl-1"> } }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">flow</span><span class="hl-1">.</span><span class="hl-0">create</span><span class="hl-1">(</span><span class="hl-2">&quot;email-3&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">partitionKey:</span><span class="hl-1"> </span><span class="hl-2">&quot;tenant-a:email&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">payload:</span><span class="hl-1"> </span><span class="hl-4">Buffer</span><span class="hl-1">.</span><span class="hl-0">from</span><span class="hl-1">(</span><span class="hl-2">&quot;welcome&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-4">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;queued&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;email&quot;</span><br/><span class="hl-1">});</span>
302
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">policy</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">states:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">queued:</span><span class="hl-1"> { </span><span class="hl-5">mode:</span><span class="hl-1"> </span><span class="hl-2">&quot;fifo&quot;</span><span class="hl-1"> }</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">// Direct writes deep-patch by default. Fence concurrent editors with generation CAS.</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">updated</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">expectedGeneration:</span><span class="hl-1"> </span><span class="hl-5">policy</span><span class="hl-1">.</span><span class="hl-5">generation</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">maxActiveMs:</span><span class="hl-1"> </span><span class="hl-9">300_000</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">// Full replacement is explicit on the client API.</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">installPolicy</span><span class="hl-1">(</span><span class="hl-2">&quot;email&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">expectedGeneration:</span><span class="hl-1"> </span><span class="hl-5">updated</span><span class="hl-1">.</span><span class="hl-5">generation</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">replace:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">states:</span><span class="hl-1"> { </span><span class="hl-5">queued:</span><span class="hl-1"> { </span><span class="hl-5">mode:</span><span class="hl-1"> </span><span class="hl-2">&quot;fifo&quot;</span><span class="hl-1"> } }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">flow</span><span class="hl-1">.</span><span class="hl-0">create</span><span class="hl-1">(</span><span class="hl-2">&quot;email-3&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">partitionKey:</span><span class="hl-1"> </span><span class="hl-2">&quot;tenant-a:email&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">payload:</span><span class="hl-1"> </span><span class="hl-5">Buffer</span><span class="hl-1">.</span><span class="hl-0">from</span><span class="hl-1">(</span><span class="hl-2">&quot;welcome&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">state:</span><span class="hl-1"> </span><span class="hl-2">&quot;queued&quot;</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;email&quot;</span><br/><span class="hl-1">});</span>
292
303
  </code><button type="button">Copy</button></pre>
293
304
 
294
305
  <p>FIFO states require a <code>partitionKey</code>; priority is for parallel states.
@@ -298,7 +309,7 @@ FIFO ordering is enforced by the server per <code>(type, state, partitionKey)</c
298
309
  concurrency remains available across different partitions.</p>
299
310
  <h2 id="ferricstore-kv-and-data-structures" class="tsd-anchor-link">FerricStore KV And Data Structures<a href="#ferricstore-kv-and-data-structures" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
300
311
  <p>The same client exposes typed helpers for FerricStore's Redis-compatible store commands:</p>
301
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">client</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">codec:</span><span class="hl-1"> </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">, { </span><span class="hl-4">name:</span><span class="hl-1"> </span><span class="hl-2">&quot;Ada&quot;</span><span class="hl-1"> }, { </span><span class="hl-4">px:</span><span class="hl-1"> </span><span class="hl-9">60_000</span><span class="hl-1"> });</span><br/><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">user</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">kv</span><span class="hl-1">.</span><span class="hl-0">get</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">hash</span><span class="hl-1">.</span><span class="hl-0">hset</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1:profile&quot;</span><span class="hl-1">, { </span><span class="hl-4">email:</span><span class="hl-1"> </span><span class="hl-2">&quot;ada@example.com&quot;</span><span class="hl-1"> });</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">lists</span><span class="hl-1">.</span><span class="hl-0">lpush</span><span class="hl-1">(</span><span class="hl-2">&quot;jobs&quot;</span><span class="hl-1">, { </span><span class="hl-4">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;job-1&quot;</span><span class="hl-1"> });</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">sets</span><span class="hl-1">.</span><span class="hl-0">sadd</span><span class="hl-1">(</span><span class="hl-2">&quot;seen-users&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">zset</span><span class="hl-1">.</span><span class="hl-0">zadd</span><span class="hl-1">(</span><span class="hl-2">&quot;leaderboard&quot;</span><span class="hl-1">, [{ </span><span class="hl-4">score:</span><span class="hl-1"> </span><span class="hl-9">42</span><span class="hl-1">, </span><span class="hl-4">member:</span><span class="hl-1"> </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1"> }]);</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">stream</span><span class="hl-1">.</span><span class="hl-0">xadd</span><span class="hl-1">(</span><span class="hl-2">&quot;events&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;*&quot;</span><span class="hl-1">, { </span><span class="hl-4">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-4">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1"> });</span><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">bloom</span><span class="hl-1">.</span><span class="hl-0">add</span><span class="hl-1">(</span><span class="hl-2">&quot;seen-filter&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span>
312
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">client</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">codec:</span><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">, { </span><span class="hl-5">name:</span><span class="hl-1"> </span><span class="hl-2">&quot;Ada&quot;</span><span class="hl-1"> }, { </span><span class="hl-5">px:</span><span class="hl-1"> </span><span class="hl-9">60_000</span><span class="hl-1"> });</span><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">user</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">kv</span><span class="hl-1">.</span><span class="hl-0">get</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">hash</span><span class="hl-1">.</span><span class="hl-0">hset</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1:profile&quot;</span><span class="hl-1">, { </span><span class="hl-5">email:</span><span class="hl-1"> </span><span class="hl-2">&quot;ada@example.com&quot;</span><span class="hl-1"> });</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">lists</span><span class="hl-1">.</span><span class="hl-0">lpush</span><span class="hl-1">(</span><span class="hl-2">&quot;jobs&quot;</span><span class="hl-1">, { </span><span class="hl-5">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;job-1&quot;</span><span class="hl-1"> });</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">sets</span><span class="hl-1">.</span><span class="hl-0">sadd</span><span class="hl-1">(</span><span class="hl-2">&quot;seen-users&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">zset</span><span class="hl-1">.</span><span class="hl-0">zadd</span><span class="hl-1">(</span><span class="hl-2">&quot;leaderboard&quot;</span><span class="hl-1">, [{ </span><span class="hl-5">score:</span><span class="hl-1"> </span><span class="hl-9">42</span><span class="hl-1">, </span><span class="hl-5">member:</span><span class="hl-1"> </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1"> }]);</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">stream</span><span class="hl-1">.</span><span class="hl-0">xadd</span><span class="hl-1">(</span><span class="hl-2">&quot;events&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;*&quot;</span><span class="hl-1">, { </span><span class="hl-5">type:</span><span class="hl-1"> </span><span class="hl-2">&quot;created&quot;</span><span class="hl-1">, </span><span class="hl-5">id:</span><span class="hl-1"> </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1"> });</span><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">bloom</span><span class="hl-1">.</span><span class="hl-0">add</span><span class="hl-1">(</span><span class="hl-2">&quot;seen-filter&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">);</span>
302
313
  </code><button type="button">Copy</button></pre>
303
314
 
304
315
  <p>Large unambiguous scalar batches accept an array without spreading, for example
@@ -328,12 +339,12 @@ apply mutations.</p>
328
339
  <h2 id="auto-batching" class="tsd-anchor-link">Auto-Batching<a href="#auto-batching" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
329
340
  <p>The default client is latency-first: each SDK call sends its own native request.</p>
330
341
  <p>For high-throughput services that issue many independent calls concurrently, enable SDK auto-batching:</p>
331
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">client</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">autoBatch:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">enabled:</span><span class="hl-1"> </span><span class="hl-5">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">maxCommands:</span><span class="hl-1"> </span><span class="hl-9">512</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-4">maxDelayMs:</span><span class="hl-1"> </span><span class="hl-9">0</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-10">Promise</span><span class="hl-1">.</span><span class="hl-0">all</span><span class="hl-1">([</span><br/><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;a&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;1&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;b&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;2&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-4">client</span><span class="hl-1">.</span><span class="hl-4">hash</span><span class="hl-1">.</span><span class="hl-0">hset</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">, { </span><span class="hl-4">email:</span><span class="hl-1"> </span><span class="hl-2">&quot;ada@example.com&quot;</span><span class="hl-1"> })</span><br/><span class="hl-1">]);</span>
342
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">client</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">autoBatch:</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-5">enabled:</span><span class="hl-1"> </span><span class="hl-6">true</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">maxCommands:</span><span class="hl-1"> </span><span class="hl-9">512</span><span class="hl-1">,</span><br/><span class="hl-1"> </span><span class="hl-5">maxDelayMs:</span><span class="hl-1"> </span><span class="hl-9">0</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span><br/><br/><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-10">Promise</span><span class="hl-1">.</span><span class="hl-0">all</span><span class="hl-1">([</span><br/><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;a&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;1&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">kv</span><span class="hl-1">.</span><span class="hl-0">set</span><span class="hl-1">(</span><span class="hl-2">&quot;b&quot;</span><span class="hl-1">, </span><span class="hl-2">&quot;2&quot;</span><span class="hl-1">),</span><br/><span class="hl-1"> </span><span class="hl-5">client</span><span class="hl-1">.</span><span class="hl-5">hash</span><span class="hl-1">.</span><span class="hl-0">hset</span><span class="hl-1">(</span><span class="hl-2">&quot;user:1&quot;</span><span class="hl-1">, { </span><span class="hl-5">email:</span><span class="hl-1"> </span><span class="hl-2">&quot;ada@example.com&quot;</span><span class="hl-1"> })</span><br/><span class="hl-1">]);</span>
332
343
  </code><button type="button">Copy</button></pre>
333
344
 
334
345
  <p>Auto-batching groups eligible concurrent commands into native <code>PIPELINE</code> frames and resolves each original promise independently. Across frames and individual-request fallbacks, same-key write dependencies retain invocation order, while read-only and disjoint-key work remains concurrent. Commands whose direct native representation uses a custom binary body are safely wrapped as typed <code>COMMAND_EXEC</code> pipeline items, preserving one pipeline request instead of issuing each command separately. Blocking/session and control commands such as <code>FLOW.CLAIM_DUE</code>, <code>BLPOP</code>, <code>XREAD</code>, <code>AUTH</code>, <code>PING</code>, <code>OPTIONS</code>, and <code>QUIT</code> bypass auto-batching. Explicit pipelines issue unsupported or connection-blocking items individually; blocking fallbacks and state-changing controls are sequenced with dependent data commands. Other fallbacks remain concurrent unless <code>client.pipeline(commands, { ordered: true })</code> is requested. Individual fallbacks continuously refill a bounded pool instead of starting every request at once; the default limit is 64 and <code>fallbackConcurrency</code> on the pipeline options can tune it per call. Native pipeline paths are unchanged. Reconnecting and topology executors reject connection-local mutations before dispatch, and an uncertain native pipeline is never replayed automatically.</p>
335
346
  <p>Queue workers are latency-first by default. For high-throughput queue workers, use one profile flag:</p>
336
- <pre><code class="ts"><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">emails</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({ </span><span class="hl-4">profile:</span><span class="hl-1"> </span><span class="hl-2">&quot;throughput&quot;</span><span class="hl-1">, </span><span class="hl-4">concurrency:</span><span class="hl-1"> </span><span class="hl-9">32</span><span class="hl-1"> }).</span><span class="hl-0">run</span><span class="hl-1">(</span><span class="hl-5">async</span><span class="hl-1"> (</span><span class="hl-4">job</span><span class="hl-1">) </span><span class="hl-5">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-0">sendEmail</span><span class="hl-1">(</span><span class="hl-4">job</span><span class="hl-1">.</span><span class="hl-4">id</span><span class="hl-1">);</span><br/><span class="hl-1">});</span>
347
+ <pre><code class="ts"><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">emails</span><span class="hl-1">.</span><span class="hl-0">worker</span><span class="hl-1">({ </span><span class="hl-5">profile:</span><span class="hl-1"> </span><span class="hl-2">&quot;throughput&quot;</span><span class="hl-1">, </span><span class="hl-5">concurrency:</span><span class="hl-1"> </span><span class="hl-9">32</span><span class="hl-1"> }).</span><span class="hl-0">run</span><span class="hl-1">(</span><span class="hl-6">async</span><span class="hl-1"> (</span><span class="hl-5">job</span><span class="hl-1">) </span><span class="hl-6">=&gt;</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-0">sendEmail</span><span class="hl-1">(</span><span class="hl-5">job</span><span class="hl-1">.</span><span class="hl-5">id</span><span class="hl-1">);</span><br/><span class="hl-1">});</span>
337
348
  </code><button type="button">Copy</button></pre>
338
349
 
339
350
  <p>The throughput profile uses compact claims, a larger batch ceiling, and concurrent completion batching. Per-job claim credit follows currently available <code>concurrency</code> (or its <code>workers</code> alias), capped by <code>batchSize</code>; explicit worker options override profile defaults.</p>
@@ -346,10 +357,12 @@ apply mutations.</p>
346
357
  <li><a href="media/signals.ts">signals.ts</a></li>
347
358
  <li><a href="media/value-refs.ts">value-refs.ts</a></li>
348
359
  <li><a href="media/kv-store.ts">kv-store.ts</a></li>
360
+ <li><a href="media/langgraph.ts">langgraph.ts</a></li>
361
+ <li><a href="media/openai-agents-session.ts">openai-agents-session.ts</a></li>
349
362
  </ul>
350
363
  <h2 id="codecs" class="tsd-anchor-link">Codecs<a href="#codecs" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
351
364
  <p><code>RawCodec</code> is the default and works with <code>Buffer</code>, <code>Uint8Array</code>, and strings. Use <code>JsonCodec</code> for language-neutral structured payloads and results.</p>
352
- <pre><code class="ts"><span class="hl-5">const</span><span class="hl-1"> </span><span class="hl-6">flow</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-4">codec:</span><span class="hl-1"> </span><span class="hl-5">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span>
365
+ <pre><code class="ts"><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">flow</span><span class="hl-1"> = </span><span class="hl-4">await</span><span class="hl-1"> </span><span class="hl-5">FerricStoreClient</span><span class="hl-1">.</span><span class="hl-0">fromUrl</span><span class="hl-1">(</span><span class="hl-2">&quot;ferric://127.0.0.1:6388&quot;</span><span class="hl-1">, {</span><br/><span class="hl-1"> </span><span class="hl-5">codec:</span><span class="hl-1"> </span><span class="hl-6">new</span><span class="hl-1"> </span><span class="hl-0">JsonCodec</span><span class="hl-1">()</span><br/><span class="hl-1">});</span>
353
366
  </code><button type="button">Copy</button></pre>
354
367
 
355
368
  <h2 id="design-notes" class="tsd-anchor-link">Design Notes<a href="#design-notes" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
@@ -367,13 +380,13 @@ apply mutations.</p>
367
380
 
368
381
  <p><code>npm run check</code> runs strict TypeScript, ESLint, Vitest, and the package build.</p>
369
382
  <p>Use Docker Compose for local integration testing:</p>
370
- <pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">integration:up</span><br/><span class="hl-4">FERRICSTORE_INTEGRATION</span><span class="hl-1">=</span><span class="hl-2">1</span><span class="hl-1"> </span><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration</span><br/><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">integration:down</span>
383
+ <pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">integration:up</span><br/><span class="hl-5">FERRICSTORE_INTEGRATION</span><span class="hl-1">=</span><span class="hl-2">1</span><span class="hl-1"> </span><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">test:integration</span><br/><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">integration:down</span>
371
384
  </code><button type="button">Copy</button></pre>
372
385
 
373
386
  <p>Benchmark raw FQL and the record convenience layer against a live server. The default
374
387
  comparison interleaves both paths and fails if either performs more than one <code>FLOW.QUERY</code>
375
388
  or any <code>FLOW.GET</code> hydration per operation:</p>
376
- <pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">build</span><br/><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">bench:flow-query</span><span class="hl-1"> </span><span class="hl-5">--</span><span class="hl-1"> </span><span class="hl-5">--requests</span><span class="hl-1"> </span><span class="hl-9">500</span><span class="hl-1"> </span><span class="hl-5">--concurrency</span><span class="hl-1"> </span><span class="hl-9">2</span><span class="hl-1"> </span><span class="hl-5">--rows</span><span class="hl-1"> </span><span class="hl-9">100</span><span class="hl-1"> </span><span class="hl-5">--pretty</span>
389
+ <pre><code class="bash"><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">build</span><br/><span class="hl-0">npm</span><span class="hl-1"> </span><span class="hl-2">run</span><span class="hl-1"> </span><span class="hl-2">bench:flow-query</span><span class="hl-1"> </span><span class="hl-6">--</span><span class="hl-1"> </span><span class="hl-6">--requests</span><span class="hl-1"> </span><span class="hl-9">500</span><span class="hl-1"> </span><span class="hl-6">--concurrency</span><span class="hl-1"> </span><span class="hl-9">2</span><span class="hl-1"> </span><span class="hl-6">--rows</span><span class="hl-1"> </span><span class="hl-9">100</span><span class="hl-1"> </span><span class="hl-6">--pretty</span>
377
390
  </code><button type="button">Copy</button></pre>
378
391
 
379
392
  <p>Generate API docs with:</p>