experimental-a2 0.14.1 → 0.16.0

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 (103) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/actor-client.d.ts +1 -1
  3. package/dist/{actor-BfQSE0KC.d.ts → actor-ohPC-81x.d.ts} +5 -5
  4. package/dist/{actor-BfQSE0KC.d.ts.map → actor-ohPC-81x.d.ts.map} +1 -1
  5. package/dist/actor-react.d.ts +3 -3
  6. package/dist/actor.d.ts +1 -1
  7. package/dist/actor.js +1 -1
  8. package/dist/ai-CrLYwNEx.js +2431 -0
  9. package/dist/ai-CrLYwNEx.js.map +1 -0
  10. package/dist/{ai-Cai-lCbj.d.ts → ai-HJ9fHfYI.d.ts} +72 -13
  11. package/dist/ai-HJ9fHfYI.d.ts.map +1 -0
  12. package/dist/ai-server.d.ts +4 -5
  13. package/dist/ai-server.d.ts.map +1 -1
  14. package/dist/ai-server.js +755 -290
  15. package/dist/ai-server.js.map +1 -1
  16. package/dist/ai.d.ts +2 -2
  17. package/dist/ai.js +1 -1999
  18. package/dist/client-BYzHjkwU.js.map +1 -1
  19. package/dist/{client-BAEABRZB.d.ts → client-CzyacQpJ.d.ts} +9 -9
  20. package/dist/client-CzyacQpJ.d.ts.map +1 -0
  21. package/dist/client.d.ts +1 -1
  22. package/dist/idempotent-id-BRJVeylj.js +13 -0
  23. package/dist/idempotent-id-BRJVeylj.js.map +1 -0
  24. package/dist/index.d.ts +8 -4
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2 -1
  27. package/dist/otel.d.ts +1 -1
  28. package/dist/react.d.ts +13 -13
  29. package/dist/react.d.ts.map +1 -1
  30. package/dist/react.js.map +1 -1
  31. package/dist/reducer-DEMjEY_O.js.map +1 -1
  32. package/dist/{reducer-BcS9VDKC.d.ts → reducer-otzHjuJj.d.ts} +3 -3
  33. package/dist/{reducer-BcS9VDKC.d.ts.map → reducer-otzHjuJj.d.ts.map} +1 -1
  34. package/dist/scheduler-qstash.d.ts +2 -2
  35. package/dist/scheduler-qstash.js +1 -1
  36. package/dist/scheduler-vercel.d.ts +2 -2
  37. package/dist/scheduler-vercel.js +1 -1
  38. package/dist/{server-CjJSGcF7.js → server-BD5ckxZb.js} +39 -26
  39. package/dist/server-BD5ckxZb.js.map +1 -0
  40. package/dist/{server-Bp5Nd1pF.d.ts → server-DbD7IJVK.d.ts} +5 -5
  41. package/dist/{server-Bp5Nd1pF.d.ts.map → server-DbD7IJVK.d.ts.map} +1 -1
  42. package/dist/server.d.ts +3 -3
  43. package/dist/server.js +1 -1
  44. package/dist/{store-D_yhNdPz.d.ts → store-DyZM6fS5.d.ts} +3 -2
  45. package/dist/{store-D_yhNdPz.d.ts.map → store-DyZM6fS5.d.ts.map} +1 -1
  46. package/dist/store-N8PXxDAS.js.map +1 -1
  47. package/dist/store-memory.d.ts +1 -1
  48. package/dist/store-postgres.d.ts +1 -1
  49. package/dist/store-redis-http.d.ts +1 -1
  50. package/dist/store-redis-http.js +1 -1
  51. package/dist/{store-redis-notify-D2EI6gwX.js → store-redis-notify-BVLUfI3j.js} +14 -13
  52. package/dist/store-redis-notify-BVLUfI3j.js.map +1 -0
  53. package/dist/store-redis.d.ts +1 -1
  54. package/dist/store-redis.js +1 -1
  55. package/dist/store-sqlite.d.ts +1 -1
  56. package/dist/{telemetry-CpeclqB2.d.ts → telemetry-B5jzpy6y.d.ts} +2 -2
  57. package/dist/telemetry-B5jzpy6y.d.ts.map +1 -0
  58. package/docs/concepts/04-state.mdx +9 -4
  59. package/docs/guides/06-ai-agents.mdx +134 -43
  60. package/docs/reference/01-api.mdx +129 -27
  61. package/examples/playground/app/agent/[agentId]/agent-client.tsx +20 -28
  62. package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +123 -0
  63. package/examples/playground/app/agent/[agentId]/agent-queue.tsx +37 -43
  64. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +44 -24
  65. package/examples/playground/app/agent/compaction-timeline.test.ts +47 -7
  66. package/examples/playground/app/agent/compaction-timeline.ts +13 -3
  67. package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -5
  68. package/examples/playground/app/chat/[chatId]/session.ts +17 -2
  69. package/examples/playground/app/chat/model.test.ts +4 -4
  70. package/examples/playground/app/chat/model.ts +18 -6
  71. package/examples/playground/app/chat/server.ts +6 -3
  72. package/examples/playground/package.json +3 -1
  73. package/package.json +1 -1
  74. package/src/ai-client-state.ts +0 -2
  75. package/src/ai-context-schema.ts +296 -0
  76. package/src/ai-context.ts +680 -0
  77. package/src/ai-control-server.ts +160 -131
  78. package/src/ai-control-state.ts +6 -0
  79. package/src/ai-control.ts +6 -7
  80. package/src/ai-coordinator.ts +92 -38
  81. package/src/ai-id.ts +6 -0
  82. package/src/ai-input-tokens.ts +75 -0
  83. package/src/ai-message-projection.ts +348 -0
  84. package/src/ai-server.ts +232 -334
  85. package/src/ai-stored-state.ts +484 -0
  86. package/src/ai.ts +252 -418
  87. package/src/client.ts +21 -15
  88. package/src/idempotent-id.ts +11 -0
  89. package/src/index.ts +1 -0
  90. package/src/react.ts +25 -19
  91. package/src/reducer.ts +2 -1
  92. package/src/server.ts +73 -56
  93. package/src/store-redis-core.ts +15 -17
  94. package/src/store.ts +1 -0
  95. package/src/telemetry.ts +2 -1
  96. package/dist/ai-Cai-lCbj.d.ts.map +0 -1
  97. package/dist/ai-control-CcD4hh3y.js +0 -119
  98. package/dist/ai-control-CcD4hh3y.js.map +0 -1
  99. package/dist/ai.js.map +0 -1
  100. package/dist/client-BAEABRZB.d.ts.map +0 -1
  101. package/dist/server-CjJSGcF7.js.map +0 -1
  102. package/dist/store-redis-notify-D2EI6gwX.js.map +0 -1
  103. package/dist/telemetry-CpeclqB2.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-CzyacQpJ.d.ts","names":[],"sources":["../src/client-errors.ts","../src/client.ts"],"mappings":";;;KAEY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;;cAGW,sBAAsB;WACxB;WACA;WACA;WACA;WACA;EAET,YACE,MAAM,aACN,iBACA,UAAU;;;;KCkCF,mBAAmB;EAC7B;EACA,OAAO;EACP;;KAGU;;;;;;;KASA;EACN;;EAEA;;EAEA;;EAEA,OAAO;;EAEP;EAAkB;EAAoB,OAAO;;EAC7C;EAAgB;;EAChB;;;;;;;;;;KAUM,WAAW,UAAU,aAAa,QAAQ,cAAc;WACzD,WAAW,QAAQ,cAAc;;KAGhC;;;;;;EAMV;;EAEA;;;KAIU;;EAEV;;EAEA;;EAEA;;;KAIU,gBACV,UAAU,WACV,GACA,UAAU,eAAe,wBACvB,aACF;;EAGE,UAAU,YAAY;;;EAIxB,OAAO;;;;;EAKP,QAAQ,cAAc;;;EAGtB;;EAEA,SAAS;EACT,YAAY;;;;;;;KAQF,sBAAsB,UAAU;;;;;;;;;;EAU1C,YAAY,QAAQ,cAAc;;KAGxB,cACV,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI,KACF,aAAa,GAAG,sBAAsB;WAC/B;EACT,UAAU;EACV,eAAe,gBAAgB,GAAG,GAAG;;;;;;;;;;EAUrC,QAAQ,QAAQ,YAAY,OAAO,WAAW;;;;;;;;;;;EAW9C,YAAY,UAAU,qBAAqB,QAAQ,cAAc;;;;;;;;;;EAUjE;;;EAGA;;;;;;EAMA;;KAGU,eAAe,UAAU,WAAW;EAC9C,eAAe;EACf;;EAEA,gBAAgB,cAAc;;;EAG9B;;KAGU,SACV,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI;EAEJ,QACE,mBACA,UAAU,eAAe,GAAG,KAC3B,cAAc,GAAG,GAAG,GAAG;;;;;;;;;KAUhB,wBAAwB;KAExB,YACR;EACE;EAAc,MAAM;EAAY,QAAQ;;EAExC;EAAY,KAAK;EAAY;;EAE7B;EAAY;EAAa;;;;;;;KAOnB;WACD;EACT,KAAK;EACL,MAAM,eAAe;EACrB,iBAAiB,cAAc;EAC/B,iBACE,iBACA,WAAW;IAAS;;EAEtB,iBACE,eACA,WAAW;IAAS;IAAc;;EAEpC,iBAAiB,eAAe;;KAGtB,kCAAkC,gBAAgB;KAElD,oBACV,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI;EAEJ,SAAS,QAAQ,GAAG,GAAG,GAAG;;EAE1B,KAAK;;EAEL,YAAY;;EAEZ,eAAe,WAAW;;;EAG1B,YAAY;;;EAGZ;;;;;;;;EAQA;;iBAm9Bc,aACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI,GACJ,SAAS,oBAAoB,GAAG,GAAG,GAAG,KAAK,SAAS,GAAG,GAAG,GAAG"}
package/dist/client.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as createClient, a as Connection, c as HistoryState, d as ReconnectPolicy, f as SessionClient, g as SessionUrl, h as SessionSnapshot, i as ClientWebSocketConstructor, l as LoadHistoryOptions, m as SessionOptions, n as ClientApi, o as ConnectionStatus, p as SessionClientPresence, r as ClientWebSocket, s as CreateClientOptions, t as A2Client, u as PushResult, v as A2ClientError, y as A2ClientErrorOptions } from "./client-BAEABRZB.js";
1
+ import { _ as createClient, a as Connection, c as HistoryState, d as ReconnectPolicy, f as SessionClient, g as SessionUrl, h as SessionSnapshot, i as ClientWebSocketConstructor, l as LoadHistoryOptions, m as SessionOptions, n as ClientApi, o as ConnectionStatus, p as SessionClientPresence, r as ClientWebSocket, s as CreateClientOptions, t as A2Client, u as PushResult, v as A2ClientError, y as A2ClientErrorOptions } from "./client-CzyacQpJ.js";
2
2
  export { A2Client, A2ClientError, type A2ClientErrorOptions, ClientApi, ClientWebSocket, ClientWebSocketConstructor, Connection, ConnectionStatus, CreateClientOptions, HistoryState, LoadHistoryOptions, PushResult, ReconnectPolicy, SessionClient, SessionClientPresence, SessionOptions, SessionSnapshot, SessionUrl, createClient };
@@ -0,0 +1,13 @@
1
+ //#region src/idempotent-id.ts
2
+ async function idempotentId(...parts) {
3
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(parts)));
4
+ const bytes = new Uint8Array(digest).slice(0, 16);
5
+ bytes[6] = bytes[6] & 15 | 128;
6
+ bytes[8] = bytes[8] & 63 | 128;
7
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
8
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
9
+ }
10
+ //#endregion
11
+ export { idempotentId as t };
12
+
13
+ //# sourceMappingURL=idempotent-id-BRJVeylj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotent-id-BRJVeylj.js","names":[],"sources":["../src/idempotent-id.ts"],"sourcesContent":["export async function idempotentId(...parts: string[]): Promise<string> {\n const digest = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(JSON.stringify(parts)),\n )\n const bytes = new Uint8Array(digest).slice(0, 16)\n bytes[6] = (bytes[6]! & 0x0f) | 0x80\n bytes[8] = (bytes[8]! & 0x3f) | 0x80\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n"],"mappings":";AAAA,eAAsB,aAAa,GAAG,OAAkC;CACtE,MAAM,SAAS,MAAM,OAAO,OAAO,OACjC,WACA,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAChD;CACA,MAAM,QAAQ,IAAI,WAAW,MAAM,CAAC,CAAC,MAAM,GAAG,EAAE;CAChD,MAAM,KAAM,MAAM,KAAM,KAAQ;CAChC,MAAM,KAAM,MAAM,KAAM,KAAQ;CAChC,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;CAC7E,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE;AAC7G"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- import { a as ContractEvent, c as PresenceDefs, d as PresenceSnapshot, f as ReducerOptions, h as StandardSchemaV1, i as Contract, l as PresenceMap, m as contract, n as ReducerBuilder, o as EventBatchFactory, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-BcS9VDKC.js";
2
- import { i as Event } from "./store-D_yhNdPz.js";
1
+ import { a as ContractEvent, c as PresenceDefs, d as PresenceSnapshot, f as ReducerOptions, h as StandardSchemaV1, i as Contract, l as PresenceMap, m as contract, n as ReducerBuilder, o as EventBatchFactory, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-otzHjuJj.js";
2
+ import { i as Event } from "./store-DyZM6fS5.js";
3
3
  import { i as NonRetriableError, n as A2ErrorCode, r as A2ServerErrorCode, t as A2Error } from "./errors-DvhSXnxk.js";
4
- import { i as A2Telemetry, n as A2SpanHandle, r as A2SpanName, t as A2AttributeValue } from "./telemetry-CpeclqB2.js";
5
- export { type A2AttributeValue, A2Error, type A2ErrorCode, type A2ServerErrorCode, type A2SpanHandle, type A2SpanName, type A2Telemetry, type AppendInput, type Contract, type ContractEvent, type Event, type EventBatchFactory, type EventDefs, NonRetriableError, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceSnapshot, type Reducer, type ReducerBuilder, type ReducerOptions, type StandardSchemaV1, contract };
4
+ import { i as A2Telemetry, n as A2SpanHandle, r as A2SpanName, t as A2AttributeValue } from "./telemetry-B5jzpy6y.js";
5
+ //#region src/idempotent-id.d.ts
6
+ declare function idempotentId(...parts: string[]): Promise<string>;
7
+ //#endregion
8
+ export { type A2AttributeValue, A2Error, type A2ErrorCode, type A2ServerErrorCode, type A2SpanHandle, type A2SpanName, type A2Telemetry, type AppendInput, type Contract, type ContractEvent, type Event, type EventBatchFactory, type EventDefs, NonRetriableError, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceSnapshot, type Reducer, type ReducerBuilder, type ReducerOptions, type StandardSchemaV1, contract, idempotentId };
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/idempotent-id.ts"],"mappings":";;;;;iBAAsB,gBAAgB,kBAAkB"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { t as contract } from "./contract-CKRg_E4q.js";
2
+ import { t as idempotentId } from "./idempotent-id-BRJVeylj.js";
2
3
  import { n as NonRetriableError, t as A2Error } from "./errors-DCk6ch5n.js";
3
- export { A2Error, NonRetriableError, contract };
4
+ export { A2Error, NonRetriableError, contract, idempotentId };
package/dist/otel.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as A2Telemetry } from "./telemetry-CpeclqB2.js";
1
+ import { i as A2Telemetry } from "./telemetry-B5jzpy6y.js";
2
2
  import { Tracer } from "@opentelemetry/api";
3
3
  //#region src/otel.d.ts
4
4
  type OtelOptions = {
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as ContractEvent, c as PresenceDefs, l as PresenceMap, p as WithPresence, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-BcS9VDKC.js";
2
- import { a as Connection, c as HistoryState, l as LoadHistoryOptions, t as A2Client, u as PushResult } from "./client-BAEABRZB.js";
1
+ import { a as ContractEvent, c as PresenceDefs, l as PresenceMap, p as WithPresence, r as AppendInput, s as EventDefs, t as Reducer, u as PresencePatch } from "./reducer-otzHjuJj.js";
2
+ import { a as Connection, c as HistoryState, l as LoadHistoryOptions, t as A2Client, u as PushResult } from "./client-CzyacQpJ.js";
3
3
  import { ReactElement, ReactNode } from "react";
4
4
  //#region src/react.d.ts
5
5
  type UseSessionOptions<S> = {
@@ -92,20 +92,20 @@ type UseSessionResult<D extends EventDefs, S, P extends PresenceDefs = Record<ne
92
92
  /** Backscroll progress: `complete` means the feed reaches index 1. */
93
93
  history: HistoryState;
94
94
  };
95
- type A2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
95
+ type A2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S> = {
96
96
  SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement;
97
- useSession: () => UseSessionResult<D, S, P>;
97
+ useSession: () => UseSessionResult<D, V, P>;
98
98
  };
99
- type BoundA2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
99
+ type BoundA2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S> = {
100
100
  SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement;
101
- useSession: () => UseSessionResult<D, S, P>;
101
+ useSession: () => UseSessionResult<D, V, P>;
102
102
  };
103
- declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(options: {
104
- client: A2Client<D, S, P>;
105
- }): BoundA2React<D, S, P>;
106
- declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(options: {
107
- reducer: Reducer<D, S, P>;
108
- }): A2React<D, S, P>;
103
+ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(options: {
104
+ client: A2Client<D, S, P, V>;
105
+ }): BoundA2React<D, S, P, V>;
106
+ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(options: {
107
+ reducer: Reducer<D, S, P, V>;
108
+ }): A2React<D, S, P, V>;
109
109
  /**
110
110
  * The standalone, provider-less hook — for apps whose components reach
111
111
  * sessions ad hoc (a sidebar of channel sessions, a user session read
@@ -116,7 +116,7 @@ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Re
116
116
  * stream is held, and your data layer's loading state is the pending
117
117
  * state.
118
118
  */
119
- declare function useSession<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>>(client: A2Client<D, S, P>, sessionId: string, options?: UseSessionOptions<S>): UseSessionResult<D, S, P>;
119
+ declare function useSession<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>, V = S>(client: A2Client<D, S, P, V>, sessionId: string, options?: UseSessionOptions<S>): UseSessionResult<D, V, P>;
120
120
  //#endregion
121
121
  export { A2React, BoundA2React, BoundSessionProviderProps, SessionProviderProps, UseSessionOptions, UseSessionPresence, UseSessionResult, createReact, useSession };
122
122
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;;KAoDY,kBAAkB;;;;EAI5B;;;;;;;;;;;;;;;;;EAiBA;IAAY,OAAO;IAAG;;;KAGZ,qBAAqB,UAAU,WAAW;;EAEpD;;EAEA;;EAEA,cAAc;;EAEd;;EAEA,gBAAgB,cAAc;;;EAG9B;EACA,WAAW;;KAGD,0BAA0B,UAAU,WAAW,KAAK,KAC9D,qBAAqB,GAAG;;;;;;KASd,mBAAmB,UAAU;;EAEvC,UAAU,YAAY;;;;;;;;EAQtB,cAAc,QAAQ,cAAc;;KAG1B,iBACV,UAAU,WACV,GACA,UAAU,eAAe,wBACvB,aAAa,GAAG,mBAAmB;;EAErC,OAAO;;EAEP,QAAQ,cAAc;;EAEtB;;;;;;EAMA,YAAY;;;;;;EAMZ,UAAU,QAAQ,YAAY,SAAS,WAAW;;EAElD;;;;;;EAMA,cAAc,UAAU,uBAAuB,QAAQ,cAAc;;EAErE,SAAS;;KAGC,QACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,qBAAqB,GAAG,OAAO;EACxD,kBAAkB,iBAAiB,GAAG,GAAG;;KAG/B,aACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,0BAA0B,GAAG,OAAO;EAC7D,kBAAkB,iBAAiB,GAAG,GAAG;;iBAG3B,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,QAAQ,SAAS,GAAG,GAAG;IAAO,aAAa,GAAG,GAAG;iBAC9C,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,SAAS,QAAQ,GAAG,GAAG;IAAO,QAAQ,GAAG,GAAG;;;;;;;;;;;iBAgKzC,WACd,UAAU,WACV,GACA,UAAU,eAAe,sBAEzB,QAAQ,SAAS,GAAG,GAAG,IACvB,mBACA,UAAU,kBAAkB,KAC3B,iBAAiB,GAAG,GAAG"}
1
+ {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;;KAoDY,kBAAkB;;;;EAI5B;;;;;;;;;;;;;;;;;EAiBA;IAAY,OAAO;IAAG;;;KAGZ,qBAAqB,UAAU,WAAW;;EAEpD;;EAEA;;EAEA,cAAc;;EAEd;;EAEA,gBAAgB,cAAc;;;EAG9B;EACA,WAAW;;KAGD,0BAA0B,UAAU,WAAW,KAAK,KAC9D,qBAAqB,GAAG;;;;;;KASd,mBAAmB,UAAU;;EAEvC,UAAU,YAAY;;;;;;;;EAQtB,cAAc,QAAQ,cAAc;;KAG1B,iBACV,UAAU,WACV,GACA,UAAU,eAAe,wBACvB,aAAa,GAAG,mBAAmB;;EAErC,OAAO;;EAEP,QAAQ,cAAc;;EAEtB;;;;;;EAMA,YAAY;;;;;;EAMZ,UAAU,QAAQ,YAAY,SAAS,WAAW;;EAElD;;;;;;EAMA,cAAc,UAAU,uBAAuB,QAAQ,cAAc;;EAErE,SAAS;;KAGC,QACV,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI;EAEJ,kBAAkB,OAAO,qBAAqB,GAAG,OAAO;EACxD,kBAAkB,iBAAiB,GAAG,GAAG;;KAG/B,aACV,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI;EAEJ,kBAAkB,OAAO,0BAA0B,GAAG,OAAO;EAC7D,kBAAkB,iBAAiB,GAAG,GAAG;;iBAG3B,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI,GACJ;EAAW,QAAQ,SAAS,GAAG,GAAG,GAAG;IAAO,aAAa,GAAG,GAAG,GAAG;iBACpD,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI,GACJ;EAAW,SAAS,QAAQ,GAAG,GAAG,GAAG;IAAO,QAAQ,GAAG,GAAG,GAAG;;;;;;;;;;;iBAiK/C,WACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB,IAAI,GAEJ,QAAQ,SAAS,GAAG,GAAG,GAAG,IAC1B,mBACA,UAAU,kBAAkB,KAC3B,iBAAiB,GAAG,GAAG"}
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["'use client'\n/**\n * experimental-a2/react — React bindings over experimental-a2/client.\n *\n * `createReact({ client })` is a factory (like createContext): call it\n * once in a `'use client'` module with a shared A2 client and export the\n * bound pair. Server components import `SessionProvider` from that module\n * as a client reference; client components import `useSession` from the\n * same file. The shared client also makes imperative pushes and provider\n * renders resolve the same live session. Everything is typed by the\n * reducer value — no type arguments, and nothing here ever touches the\n * machine module.\n *\n * The standalone `useSession(client, sessionId, options?)` is the\n * provider-less primitive for client-first apps: identity-mapped\n * handles, one leased stream per session, and an app-owned hydration\n * input — the hook holds the stream until the app hands it a\n * `{ state, index }` fold.\n */\n\nimport {\n createContext,\n createElement,\n useContext,\n useEffect,\n useMemo,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport {\n createClient,\n type A2Client,\n type Connection,\n type HistoryState,\n type LoadHistoryOptions,\n type PushResult,\n type SessionClient,\n type SessionClientPresence,\n type SessionSnapshot,\n} from './client.ts'\nimport type {\n AppendInput,\n ContractEvent,\n EventDefs,\n PresenceDefs,\n PresenceMap,\n PresencePatch,\n WithPresence,\n} from './contract.ts'\nimport type { Reducer } from './reducer.ts'\n\nexport type UseSessionOptions<S> = {\n /** Overrides the client's `participant` for this session — for apps\n * whose identity is only known at mount time. Bound once per\n * session handle. */\n participant?: string\n /**\n * The server fold this session mounts from: the `{ state, index }`\n * pair `session.state(reducer)` returns, fetched by the app through\n * its own route. Atomic on purpose — the fold and its frontier\n * travel together or not at all. While `undefined` (the app's fetch\n * has not landed), the hook holds the stream; when the value\n * arrives, the session hydrates through the ordinary re-hydration\n * seam and the stream connects at that frontier. To fold from the log's start\n * deliberately, hand the fold's true starting point:\n * `{ state: reducer.initialState, index: 0 }` — state at index 0 is\n * the reducer's seed by definition, so the explicit replay needs no\n * special vocabulary. There is no pending/ready echo on the result:\n * whether the fold has been handed over is this option — the\n * caller's own input — so the data layer's loading state IS the\n * pending state.\n */\n hydrate?: { state: S; index: number } | undefined\n}\n\nexport type SessionProviderProps<D extends EventDefs, S> = {\n /** Which session to subscribe to. */\n sessionId: string\n /** Base path of the route exposing GET (stream) and POST (push). */\n api: string\n /** The server-rendered fold — the first paint, no client JS needed. */\n initialState: S\n /** The fold's frontier — the stream resumes exactly there. */\n initialIndex: number\n /** Server-rendered event history through `initialIndex`. */\n initialEvents?: ContractEvent<D>[]\n /** Overrides the client's `participant` for this session — for apps\n * whose identity is only known at mount time. */\n participant?: string\n children?: ReactNode\n}\n\nexport type BoundSessionProviderProps<D extends EventDefs, S> = Omit<\n SessionProviderProps<D, S>,\n 'api'\n>\n\n/**\n * The presence members of the hook result — intersected in via\n * `WithPresence`, so they exist exactly when the contract declares\n * presence fields (the reducer is the client's typed handle on it).\n */\nexport type UseSessionPresence<P extends PresenceDefs> = {\n /** The replicated presence map, this client included (local echo). */\n presence: PresenceMap<P>\n /**\n * Fire-and-forget: validated locally, echoed into the map\n * immediately, coalesced on the wire to a fixed cadence (a leading\n * send immediately, then at most one merged send per interval;\n * later values win per field), stamped with the frontier at send\n * time as `seen`. Requires the provider's `participant`.\n */\n setPresence: (values: PresencePatch<P>['values']) => void\n}\n\nexport type UseSessionResult<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = WithPresence<P, UseSessionPresence<P>> & {\n /** The live view, folded through the shared reducer. */\n state: S\n /** The raw event feed — observed, seeded, or backscrolled. */\n events: ContractEvent<D>[]\n /** The stream frontier — `lastSeenIndex` for cancellation. */\n index: number\n /**\n * The connection, as a discriminated union — `error` exists only\n * while disconnected; \"reconnecting…\" is\n * `status === 'connecting' && reconnects > 0`.\n */\n connection: Connection\n /**\n * Typed optimistic append. Awaiting it gives the server ack;\n * `.confirmed` resolves when the live stream has delivered the batch\n * back. Rejects with the server's A2Error codes.\n */\n push: (...events: AppendInput<D>[]) => PushResult<D>\n /** Restart the live subscription without resetting its state or cursor. */\n reconnect: () => void\n /**\n * Backscroll: fetch a bounded slice of older events into `events`.\n * Defaults page backward 50 at a time from the oldest loaded event.\n * Display data only — `state` and the optimistic overlay never move.\n */\n loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>\n /** Backscroll progress: `complete` means the feed reaches index 1. */\n history: HistoryState\n}\n\nexport type A2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, S, P>\n}\n\nexport type BoundA2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, S, P>\n}\n\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(options: { client: A2Client<D, S, P> }): BoundA2React<D, S, P>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(options: { reducer: Reducer<D, S, P> }): A2React<D, S, P>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(\n options: { client: A2Client<D, S, P> } | { reducer: Reducer<D, S, P> },\n): BoundA2React<D, S, P> | A2React<D, S, P> {\n const Context = createContext<SessionClient<D, S, P> | null>(null)\n const resolveSession: (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => SessionClient<D, S, P> =\n 'client' in options\n ? (\n sessionId,\n _api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ) =>\n options.client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n : (() => {\n const clients = new Map<string, A2Client<D, S, P>>()\n return (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => {\n if (api === undefined) {\n throw new TypeError(\n 'SessionProvider requires api when createReact binds a reducer',\n )\n }\n let client = clients.get(api)\n if (!client) {\n client = createClient({ reducer: options.reducer, api })\n clients.set(api, client)\n }\n return client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n }\n })()\n\n function SessionProvider(\n props: SessionProviderProps<D, S> | BoundSessionProviderProps<D, S>,\n ): ReactElement {\n const {\n sessionId,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n } = props\n const api = 'api' in props ? props.api : undefined\n const client = useMemo(\n () =>\n resolveSession(\n sessionId,\n api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ),\n [sessionId, api, initialState, initialIndex, initialEvents, participant],\n )\n useSessionLifecycle(client, true)\n return createElement(Context.Provider, { value: client }, props.children)\n }\n\n function useBoundSession(): UseSessionResult<D, S, P> {\n const client = useContext(Context)\n if (!client) {\n throw new Error(\n 'useSession must be rendered inside its matching SessionProvider',\n )\n }\n return useSessionResult(client)\n }\n\n return { SessionProvider, useSession: useBoundSession }\n}\n\n// ── the shared session lifecycle ───────────────────────────────\n\n/**\n * Hold a stream lease while mounted and `live` — the client refcounts\n * leases per handle, so however many providers and hooks mount one\n * session, the first connects and the last release closes\n * (StrictMode-safe: the lease is retaken on remount). A standalone\n * hook without a `hydrate` value passes `live: false` and holds the\n * stream — connecting an unhydrated session would replay the whole\n * log, and the library never picks the expensive path silently.\n */\nfunction useSessionLifecycle<D extends EventDefs, S, P extends PresenceDefs>(\n session: SessionClient<D, S, P>,\n live: boolean,\n): void {\n useEffect(() => {\n if (!live) return\n return session.connect()\n }, [session, live])\n}\n\nfunction useSessionResult<D extends EventDefs, S, P extends PresenceDefs>(\n session: SessionClient<D, S, P>,\n): UseSessionResult<D, S, P> {\n const snapshot: SessionSnapshot<D, S, P> = useSyncExternalStore(\n session.subscribe,\n session.getSnapshot,\n session.getSnapshot,\n )\n return useMemo(() => {\n // Runtime mirror of the WithPresence surface: the client attaches\n // setPresence (and the snapshot carries presence) exactly when the\n // reducer declares presence fields — no runtime-inert members.\n const { setPresence } = session as Partial<SessionClientPresence<P>>\n const { presence } = snapshot as Partial<{ presence: PresenceMap<P> }>\n return {\n state: snapshot.state,\n events: snapshot.events,\n index: snapshot.index,\n connection: snapshot.connection,\n push: session.push,\n reconnect: session.reconnect,\n loadHistory: session.loadHistory,\n history: snapshot.history,\n ...(setPresence && presence ? { presence, setPresence } : {}),\n } as UseSessionResult<D, S, P>\n }, [snapshot, session])\n}\n\n/**\n * The standalone, provider-less hook — for apps whose components reach\n * sessions ad hoc (a sidebar of channel sessions, a user session read\n * from a menu). Takes the shared client and a session id; the handle\n * is identity-mapped, so every hook and provider mounting the same\n * session shares one runtime and one leased stream. Hydration is\n * app-owned: pass `hydrate` when your fetch lands; until then the\n * stream is held, and your data layer's loading state is the pending\n * state.\n */\nexport function useSession<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(\n client: A2Client<D, S, P>,\n sessionId: string,\n options?: UseSessionOptions<S>,\n): UseSessionResult<D, S, P> {\n const { participant, hydrate } = options ?? {}\n const session = useMemo(\n () =>\n client.session(sessionId, {\n ...(hydrate === undefined\n ? {}\n : { initialState: hydrate.state, initialIndex: hydrate.index }),\n ...(participant === undefined ? {} : { participant }),\n }),\n [client, sessionId, hydrate, participant],\n )\n useSessionLifecycle(session, hydrate !== undefined)\n return useSessionResult(session)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmLA,SAAgB,YAKd,SAC0C;CAC1C,MAAM,UAAU,cAA6C,IAAI;CACjE,MAAM,iBAQJ,YAAY,WAEN,WACA,MACA,cACA,cACA,eACA,gBAEA,QAAQ,OAAO,QAAQ,WAAW;EAChC;EACA;EACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;EACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CACrD,CAAC,WACI;EACL,MAAM,0BAAU,IAAI,IAA+B;EACnD,QACE,WACA,KACA,cACA,cACA,eACA,gBACG;GACH,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,UACR,+DACF;GAEF,IAAI,SAAS,QAAQ,IAAI,GAAG;GAC5B,IAAI,CAAC,QAAQ;IACX,SAAS,aAAa;KAAE,SAAS,QAAQ;KAAS;IAAI,CAAC;IACvD,QAAQ,IAAI,KAAK,MAAM;GACzB;GACA,OAAO,OAAO,QAAQ,WAAW;IAC/B;IACA;IACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;IACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;GACrD,CAAC;EACH;CACF,EAAA,CAAG;CAET,SAAS,gBACP,OACc;EACd,MAAM,EACJ,WACA,cACA,cACA,eACA,gBACE;EACJ,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,KAAA;EACzC,MAAM,SAAS,cAEX,eACE,WACA,KACA,cACA,cACA,eACA,WACF,GACF;GAAC;GAAW;GAAK;GAAc;GAAc;GAAe;EAAW,CACzE;EACA,oBAAoB,QAAQ,IAAI;EAChC,OAAO,cAAc,QAAQ,UAAU,EAAE,OAAO,OAAO,GAAG,MAAM,QAAQ;CAC1E;CAEA,SAAS,kBAA6C;EACpD,MAAM,SAAS,WAAW,OAAO;EACjC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,iEACF;EAEF,OAAO,iBAAiB,MAAM;CAChC;CAEA,OAAO;EAAE;EAAiB,YAAY;CAAgB;AACxD;;;;;;;;;;AAaA,SAAS,oBACP,SACA,MACM;CACN,gBAAgB;EACd,IAAI,CAAC,MAAM;EACX,OAAO,QAAQ,QAAQ;CACzB,GAAG,CAAC,SAAS,IAAI,CAAC;AACpB;AAEA,SAAS,iBACP,SAC2B;CAC3B,MAAM,WAAqC,qBACzC,QAAQ,WACR,QAAQ,aACR,QAAQ,WACV;CACA,OAAO,cAAc;EAInB,MAAM,EAAE,gBAAgB;EACxB,MAAM,EAAE,aAAa;EACrB,OAAO;GACL,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,OAAO,SAAS;GAChB,YAAY,SAAS;GACrB,MAAM,QAAQ;GACd,WAAW,QAAQ;GACnB,aAAa,QAAQ;GACrB,SAAS,SAAS;GAClB,GAAI,eAAe,WAAW;IAAE;IAAU;GAAY,IAAI,CAAC;EAC7D;CACF,GAAG,CAAC,UAAU,OAAO,CAAC;AACxB;;;;;;;;;;;AAYA,SAAgB,WAKd,QACA,WACA,SAC2B;CAC3B,MAAM,EAAE,aAAa,YAAY,WAAW,CAAC;CAC7C,MAAM,UAAU,cAEZ,OAAO,QAAQ,WAAW;EACxB,GAAI,YAAY,KAAA,IACZ,CAAC,IACD;GAAE,cAAc,QAAQ;GAAO,cAAc,QAAQ;EAAM;EAC/D,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CACrD,CAAC,GACH;EAAC;EAAQ;EAAW;EAAS;CAAW,CAC1C;CACA,oBAAoB,SAAS,YAAY,KAAA,CAAS;CAClD,OAAO,iBAAiB,OAAO;AACjC"}
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["'use client'\n/**\n * experimental-a2/react — React bindings over experimental-a2/client.\n *\n * `createReact({ client })` is a factory (like createContext): call it\n * once in a `'use client'` module with a shared A2 client and export the\n * bound pair. Server components import `SessionProvider` from that module\n * as a client reference; client components import `useSession` from the\n * same file. The shared client also makes imperative pushes and provider\n * renders resolve the same live session. Everything is typed by the\n * reducer value — no type arguments, and nothing here ever touches the\n * machine module.\n *\n * The standalone `useSession(client, sessionId, options?)` is the\n * provider-less primitive for client-first apps: identity-mapped\n * handles, one leased stream per session, and an app-owned hydration\n * input — the hook holds the stream until the app hands it a\n * `{ state, index }` fold.\n */\n\nimport {\n createContext,\n createElement,\n useContext,\n useEffect,\n useMemo,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport {\n createClient,\n type A2Client,\n type Connection,\n type HistoryState,\n type LoadHistoryOptions,\n type PushResult,\n type SessionClient,\n type SessionClientPresence,\n type SessionSnapshot,\n} from './client.ts'\nimport type {\n AppendInput,\n ContractEvent,\n EventDefs,\n PresenceDefs,\n PresenceMap,\n PresencePatch,\n WithPresence,\n} from './contract.ts'\nimport type { Reducer } from './reducer.ts'\n\nexport type UseSessionOptions<S> = {\n /** Overrides the client's `participant` for this session — for apps\n * whose identity is only known at mount time. Bound once per\n * session handle. */\n participant?: string\n /**\n * The server fold this session mounts from: the `{ state, index }`\n * pair `session.state(reducer)` returns, fetched by the app through\n * its own route. Atomic on purpose — the fold and its frontier\n * travel together or not at all. While `undefined` (the app's fetch\n * has not landed), the hook holds the stream; when the value\n * arrives, the session hydrates through the ordinary re-hydration\n * seam and the stream connects at that frontier. To fold from the log's start\n * deliberately, hand the fold's true starting point:\n * `{ state: reducer.initialState, index: 0 }` — state at index 0 is\n * the reducer's seed by definition, so the explicit replay needs no\n * special vocabulary. There is no pending/ready echo on the result:\n * whether the fold has been handed over is this option — the\n * caller's own input — so the data layer's loading state IS the\n * pending state.\n */\n hydrate?: { state: S; index: number } | undefined\n}\n\nexport type SessionProviderProps<D extends EventDefs, S> = {\n /** Which session to subscribe to. */\n sessionId: string\n /** Base path of the route exposing GET (stream) and POST (push). */\n api: string\n /** The server-rendered fold — the first paint, no client JS needed. */\n initialState: S\n /** The fold's frontier — the stream resumes exactly there. */\n initialIndex: number\n /** Server-rendered event history through `initialIndex`. */\n initialEvents?: ContractEvent<D>[]\n /** Overrides the client's `participant` for this session — for apps\n * whose identity is only known at mount time. */\n participant?: string\n children?: ReactNode\n}\n\nexport type BoundSessionProviderProps<D extends EventDefs, S> = Omit<\n SessionProviderProps<D, S>,\n 'api'\n>\n\n/**\n * The presence members of the hook result — intersected in via\n * `WithPresence`, so they exist exactly when the contract declares\n * presence fields (the reducer is the client's typed handle on it).\n */\nexport type UseSessionPresence<P extends PresenceDefs> = {\n /** The replicated presence map, this client included (local echo). */\n presence: PresenceMap<P>\n /**\n * Fire-and-forget: validated locally, echoed into the map\n * immediately, coalesced on the wire to a fixed cadence (a leading\n * send immediately, then at most one merged send per interval;\n * later values win per field), stamped with the frontier at send\n * time as `seen`. Requires the provider's `participant`.\n */\n setPresence: (values: PresencePatch<P>['values']) => void\n}\n\nexport type UseSessionResult<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = WithPresence<P, UseSessionPresence<P>> & {\n /** The live view, folded through the shared reducer. */\n state: S\n /** The raw event feed — observed, seeded, or backscrolled. */\n events: ContractEvent<D>[]\n /** The stream frontier — `lastSeenIndex` for cancellation. */\n index: number\n /**\n * The connection, as a discriminated union — `error` exists only\n * while disconnected; \"reconnecting…\" is\n * `status === 'connecting' && reconnects > 0`.\n */\n connection: Connection\n /**\n * Typed optimistic append. Awaiting it gives the server ack;\n * `.confirmed` resolves when the live stream has delivered the batch\n * back. Rejects with the server's A2Error codes.\n */\n push: (...events: AppendInput<D>[]) => PushResult<D>\n /** Restart the live subscription without resetting its state or cursor. */\n reconnect: () => void\n /**\n * Backscroll: fetch a bounded slice of older events into `events`.\n * Defaults page backward 50 at a time from the oldest loaded event.\n * Display data only — `state` and the optimistic overlay never move.\n */\n loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>\n /** Backscroll progress: `complete` means the feed reaches index 1. */\n history: HistoryState\n}\n\nexport type A2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n> = {\n SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, V, P>\n}\n\nexport type BoundA2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n> = {\n SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, V, P>\n}\n\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n>(options: { client: A2Client<D, S, P, V> }): BoundA2React<D, S, P, V>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n>(options: { reducer: Reducer<D, S, P, V> }): A2React<D, S, P, V>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n>(\n options: { client: A2Client<D, S, P, V> } | { reducer: Reducer<D, S, P, V> },\n): BoundA2React<D, S, P, V> | A2React<D, S, P, V> {\n const Context = createContext<SessionClient<D, S, P, V> | null>(null)\n const resolveSession: (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => SessionClient<D, S, P, V> =\n 'client' in options\n ? (\n sessionId,\n _api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ) =>\n options.client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n : (() => {\n const clients = new Map<string, A2Client<D, S, P, V>>()\n return (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => {\n if (api === undefined) {\n throw new TypeError(\n 'SessionProvider requires api when createReact binds a reducer',\n )\n }\n let client = clients.get(api)\n if (!client) {\n client = createClient({ reducer: options.reducer, api })\n clients.set(api, client)\n }\n return client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n }\n })()\n\n function SessionProvider(\n props: SessionProviderProps<D, S> | BoundSessionProviderProps<D, S>,\n ): ReactElement {\n const {\n sessionId,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n } = props\n const api = 'api' in props ? props.api : undefined\n const client = useMemo(\n () =>\n resolveSession(\n sessionId,\n api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ),\n [sessionId, api, initialState, initialIndex, initialEvents, participant],\n )\n useSessionLifecycle(client, true)\n return createElement(Context.Provider, { value: client }, props.children)\n }\n\n function useBoundSession(): UseSessionResult<D, V, P> {\n const client = useContext(Context)\n if (!client) {\n throw new Error(\n 'useSession must be rendered inside its matching SessionProvider',\n )\n }\n return useSessionResult(client)\n }\n\n return { SessionProvider, useSession: useBoundSession }\n}\n\n// ── the shared session lifecycle ───────────────────────────────\n\n/**\n * Hold a stream lease while mounted and `live` — the client refcounts\n * leases per handle, so however many providers and hooks mount one\n * session, the first connects and the last release closes\n * (StrictMode-safe: the lease is retaken on remount). A standalone\n * hook without a `hydrate` value passes `live: false` and holds the\n * stream — connecting an unhydrated session would replay the whole\n * log, and the library never picks the expensive path silently.\n */\nfunction useSessionLifecycle<D extends EventDefs, S, P extends PresenceDefs, V>(\n session: SessionClient<D, S, P, V>,\n live: boolean,\n): void {\n useEffect(() => {\n if (!live) return\n return session.connect()\n }, [session, live])\n}\n\nfunction useSessionResult<D extends EventDefs, S, P extends PresenceDefs, V>(\n session: SessionClient<D, S, P, V>,\n): UseSessionResult<D, V, P> {\n const snapshot: SessionSnapshot<D, V, P> = useSyncExternalStore(\n session.subscribe,\n session.getSnapshot,\n session.getSnapshot,\n )\n return useMemo(() => {\n // Runtime mirror of the WithPresence surface: the client attaches\n // setPresence (and the snapshot carries presence) exactly when the\n // reducer declares presence fields — no runtime-inert members.\n const { setPresence } = session as Partial<SessionClientPresence<P>>\n const { presence } = snapshot as Partial<{ presence: PresenceMap<P> }>\n return {\n state: snapshot.state,\n events: snapshot.events,\n index: snapshot.index,\n connection: snapshot.connection,\n push: session.push,\n reconnect: session.reconnect,\n loadHistory: session.loadHistory,\n history: snapshot.history,\n ...(setPresence && presence ? { presence, setPresence } : {}),\n } as UseSessionResult<D, V, P>\n }, [snapshot, session])\n}\n\n/**\n * The standalone, provider-less hook — for apps whose components reach\n * sessions ad hoc (a sidebar of channel sessions, a user session read\n * from a menu). Takes the shared client and a session id; the handle\n * is identity-mapped, so every hook and provider mounting the same\n * session shares one runtime and one leased stream. Hydration is\n * app-owned: pass `hydrate` when your fetch lands; until then the\n * stream is held, and your data layer's loading state is the pending\n * state.\n */\nexport function useSession<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n>(\n client: A2Client<D, S, P, V>,\n sessionId: string,\n options?: UseSessionOptions<S>,\n): UseSessionResult<D, V, P> {\n const { participant, hydrate } = options ?? {}\n const session = useMemo(\n () =>\n client.session(sessionId, {\n ...(hydrate === undefined\n ? {}\n : { initialState: hydrate.state, initialIndex: hydrate.index }),\n ...(participant === undefined ? {} : { participant }),\n }),\n [client, sessionId, hydrate, participant],\n )\n useSessionLifecycle(session, hydrate !== undefined)\n return useSessionResult(session)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuLA,SAAgB,YAMd,SACgD;CAChD,MAAM,UAAU,cAAgD,IAAI;CACpE,MAAM,iBAQJ,YAAY,WAEN,WACA,MACA,cACA,cACA,eACA,gBAEA,QAAQ,OAAO,QAAQ,WAAW;EAChC;EACA;EACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;EACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CACrD,CAAC,WACI;EACL,MAAM,0BAAU,IAAI,IAAkC;EACtD,QACE,WACA,KACA,cACA,cACA,eACA,gBACG;GACH,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,UACR,+DACF;GAEF,IAAI,SAAS,QAAQ,IAAI,GAAG;GAC5B,IAAI,CAAC,QAAQ;IACX,SAAS,aAAa;KAAE,SAAS,QAAQ;KAAS;IAAI,CAAC;IACvD,QAAQ,IAAI,KAAK,MAAM;GACzB;GACA,OAAO,OAAO,QAAQ,WAAW;IAC/B;IACA;IACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;IACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;GACrD,CAAC;EACH;CACF,EAAA,CAAG;CAET,SAAS,gBACP,OACc;EACd,MAAM,EACJ,WACA,cACA,cACA,eACA,gBACE;EACJ,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,KAAA;EACzC,MAAM,SAAS,cAEX,eACE,WACA,KACA,cACA,cACA,eACA,WACF,GACF;GAAC;GAAW;GAAK;GAAc;GAAc;GAAe;EAAW,CACzE;EACA,oBAAoB,QAAQ,IAAI;EAChC,OAAO,cAAc,QAAQ,UAAU,EAAE,OAAO,OAAO,GAAG,MAAM,QAAQ;CAC1E;CAEA,SAAS,kBAA6C;EACpD,MAAM,SAAS,WAAW,OAAO;EACjC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,iEACF;EAEF,OAAO,iBAAiB,MAAM;CAChC;CAEA,OAAO;EAAE;EAAiB,YAAY;CAAgB;AACxD;;;;;;;;;;AAaA,SAAS,oBACP,SACA,MACM;CACN,gBAAgB;EACd,IAAI,CAAC,MAAM;EACX,OAAO,QAAQ,QAAQ;CACzB,GAAG,CAAC,SAAS,IAAI,CAAC;AACpB;AAEA,SAAS,iBACP,SAC2B;CAC3B,MAAM,WAAqC,qBACzC,QAAQ,WACR,QAAQ,aACR,QAAQ,WACV;CACA,OAAO,cAAc;EAInB,MAAM,EAAE,gBAAgB;EACxB,MAAM,EAAE,aAAa;EACrB,OAAO;GACL,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,OAAO,SAAS;GAChB,YAAY,SAAS;GACrB,MAAM,QAAQ;GACd,WAAW,QAAQ;GACnB,aAAa,QAAQ;GACrB,SAAS,SAAS;GAClB,GAAI,eAAe,WAAW;IAAE;IAAU;GAAY,IAAI,CAAC;EAC7D;CACF,GAAG,CAAC,UAAU,OAAO,CAAC;AACxB;;;;;;;;;;;AAYA,SAAgB,WAMd,QACA,WACA,SAC2B;CAC3B,MAAM,EAAE,aAAa,YAAY,WAAW,CAAC;CAC7C,MAAM,UAAU,cAEZ,OAAO,QAAQ,WAAW;EACxB,GAAI,YAAY,KAAA,IACZ,CAAC,IACD;GAAE,cAAc,QAAQ;GAAO,cAAc,QAAQ;EAAM;EAC/D,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CACrD,CAAC,GACH;EAAC;EAAQ;EAAW;EAAS;CAAW,CAC1C;CACA,oBAAoB,SAAS,YAAY,KAAA,CAAS;CAClD,OAAO,iBAAiB,OAAO;AACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"reducer-DEMjEY_O.js","names":[],"sources":["../src/reducer.ts"],"sourcesContent":["import type { ContractEvent, EventDefs, PresenceDefs } from './contract.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\nimport { validateSync } from './validate.ts'\n\nexport const clientStateProjection: unique symbol = Symbol(\n 'a2.clientStateProjection',\n)\n\n/**\n * A pure fold over a session's events — `(state, event) => state` —\n * derived from a contract via `contract.reducer(options).fold(fn)`.\n *\n * The name is the reducer's identity. Cached folds (server snapshots,\n * the browser cache) are keyed by it, which makes it the invalidation\n * knob too: changed the fold's logic? Change the name. Snapshots under\n * the old name are ignored and the next read refolds from raw events.\n * See docs/concepts/04-state.mdx.\n */\nexport type Reducer<\n D extends EventDefs = EventDefs,\n S = unknown,\n P extends PresenceDefs = Record<never, never>,\n> = {\n /** Identity and cache key. Rename when the fold's logic changes. */\n readonly name: string\n /** The contract's vocabulary — types every event in the fold. */\n readonly events: Readonly<D>\n /**\n * The contract's presence vocabulary — carried because the reducer\n * is the client's typed handle on the contract. The fold never sees\n * presence.\n */\n readonly presence: Readonly<P>\n readonly fold: (state: S, event: ContractEvent<D>) => S\n readonly initialState: S\n /** Internal read-side projection. Never used as a fold input or persisted. */\n readonly [clientStateProjection]?: (state: S) => S\n /**\n * When present: `initialState` was validated against it at\n * definition, and any cached fold a2 rehydrates (server snapshots,\n * the browser cache) is checked against it — failing snapshots are\n * discarded and refolded, catching shape drift a stale name can't.\n */\n readonly stateSchema: StandardSchemaV1<unknown, S> | undefined\n}\n\n/** The second step of `contract.reducer` — see the method's doc. */\nexport type ReducerBuilder<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n fold(fold: (state: S, event: ContractEvent<D>) => S): Reducer<D, S, P>\n}\n\n/** Internal — reducers are created through `contract.reducer(...).fold(...)`. */\nexport function makeReducerBuilder<\n D extends EventDefs,\n P extends PresenceDefs,\n Seed,\n S,\n>(\n events: Readonly<D>,\n presence: Readonly<P>,\n options: {\n name: string\n initialState: Seed\n stateSchema?: StandardSchemaV1<Seed, S>\n },\n): ReducerBuilder<D, S, P> {\n const { name } = options\n if (typeof name !== 'string' || name.length === 0) {\n throw new TypeError(\n 'reducer name must be a non-empty string — it identifies the reducer and keys cached snapshots',\n )\n }\n\n // Without a schema, S = Seed (the type parameter default), so the\n // seed is the state. With one, the seed is the schema's input and the\n // validated output (defaults applied) becomes the fold's actual seed.\n let initialState = options.initialState as unknown as S\n if (options.stateSchema) {\n const result = validateSync(\n options.stateSchema,\n options.initialState,\n 'the stateSchema',\n )\n if (result.issues) {\n throw new TypeError(\n `reducer initialState does not match its stateSchema: ${result.issues\n .map((issue) => issue.message)\n .join('; ')}`,\n )\n }\n initialState = result.value as S\n }\n\n return {\n fold(fold) {\n if (typeof fold !== 'function') {\n throw new TypeError('reducer fold must be a function')\n }\n return {\n name,\n events,\n presence,\n fold,\n initialState,\n stateSchema: options.stateSchema as\n StandardSchemaV1<unknown, S> | undefined,\n }\n },\n }\n}\n"],"mappings":";;AAIA,MAAa,wBAAuC,OAClD,0BACF;;AAkDA,SAAgB,mBAMd,QACA,UACA,SAKyB;CACzB,MAAM,EAAE,SAAS;CACjB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,UACR,+FACF;CAMF,IAAI,eAAe,QAAQ;CAC3B,IAAI,QAAQ,aAAa;EACvB,MAAM,SAAS,aACb,QAAQ,aACR,QAAQ,cACR,iBACF;EACA,IAAI,OAAO,QACT,MAAM,IAAI,UACR,wDAAwD,OAAO,OAC5D,KAAK,UAAU,MAAM,OAAO,CAAC,CAC7B,KAAK,IAAI,GACd;EAEF,eAAe,OAAO;CACxB;CAEA,OAAO,EACL,KAAK,MAAM;EACT,IAAI,OAAO,SAAS,YAClB,MAAM,IAAI,UAAU,iCAAiC;EAEvD,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,aAAa,QAAQ;EAEvB;CACF,EACF;AACF"}
1
+ {"version":3,"file":"reducer-DEMjEY_O.js","names":[],"sources":["../src/reducer.ts"],"sourcesContent":["import type { ContractEvent, EventDefs, PresenceDefs } from './contract.ts'\nimport type { StandardSchemaV1 } from './standard-schema.ts'\nimport { validateSync } from './validate.ts'\n\nexport const clientStateProjection: unique symbol = Symbol(\n 'a2.clientStateProjection',\n)\n\n/**\n * A pure fold over a session's events — `(state, event) => state` —\n * derived from a contract via `contract.reducer(options).fold(fn)`.\n *\n * The name is the reducer's identity. Cached folds (server snapshots,\n * the browser cache) are keyed by it, which makes it the invalidation\n * knob too: changed the fold's logic? Change the name. Snapshots under\n * the old name are ignored and the next read refolds from raw events.\n * See docs/concepts/04-state.mdx.\n */\nexport type Reducer<\n D extends EventDefs = EventDefs,\n S = unknown,\n P extends PresenceDefs = Record<never, never>,\n V = S,\n> = {\n /** Identity and cache key. Rename when the fold's logic changes. */\n readonly name: string\n /** The contract's vocabulary — types every event in the fold. */\n readonly events: Readonly<D>\n /**\n * The contract's presence vocabulary — carried because the reducer\n * is the client's typed handle on the contract. The fold never sees\n * presence.\n */\n readonly presence: Readonly<P>\n readonly fold: (state: S, event: ContractEvent<D>) => S\n readonly initialState: S\n /** Internal read-side projection. Never used as a fold input or persisted. */\n readonly [clientStateProjection]?: (state: S) => V\n /**\n * When present: `initialState` was validated against it at\n * definition, and any cached fold a2 rehydrates (server snapshots,\n * the browser cache) is checked against it — failing snapshots are\n * discarded and refolded, catching shape drift a stale name can't.\n */\n readonly stateSchema: StandardSchemaV1<unknown, S> | undefined\n}\n\n/** The second step of `contract.reducer` — see the method's doc. */\nexport type ReducerBuilder<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n fold(fold: (state: S, event: ContractEvent<D>) => S): Reducer<D, S, P>\n}\n\n/** Internal — reducers are created through `contract.reducer(...).fold(...)`. */\nexport function makeReducerBuilder<\n D extends EventDefs,\n P extends PresenceDefs,\n Seed,\n S,\n>(\n events: Readonly<D>,\n presence: Readonly<P>,\n options: {\n name: string\n initialState: Seed\n stateSchema?: StandardSchemaV1<Seed, S>\n },\n): ReducerBuilder<D, S, P> {\n const { name } = options\n if (typeof name !== 'string' || name.length === 0) {\n throw new TypeError(\n 'reducer name must be a non-empty string — it identifies the reducer and keys cached snapshots',\n )\n }\n\n // Without a schema, S = Seed (the type parameter default), so the\n // seed is the state. With one, the seed is the schema's input and the\n // validated output (defaults applied) becomes the fold's actual seed.\n let initialState = options.initialState as unknown as S\n if (options.stateSchema) {\n const result = validateSync(\n options.stateSchema,\n options.initialState,\n 'the stateSchema',\n )\n if (result.issues) {\n throw new TypeError(\n `reducer initialState does not match its stateSchema: ${result.issues\n .map((issue) => issue.message)\n .join('; ')}`,\n )\n }\n initialState = result.value as S\n }\n\n return {\n fold(fold) {\n if (typeof fold !== 'function') {\n throw new TypeError('reducer fold must be a function')\n }\n return {\n name,\n events,\n presence,\n fold,\n initialState,\n stateSchema: options.stateSchema as\n StandardSchemaV1<unknown, S> | undefined,\n }\n },\n }\n}\n"],"mappings":";;AAIA,MAAa,wBAAuC,OAClD,0BACF;;AAmDA,SAAgB,mBAMd,QACA,UACA,SAKyB;CACzB,MAAM,EAAE,SAAS;CACjB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,UACR,+FACF;CAMF,IAAI,eAAe,QAAQ;CAC3B,IAAI,QAAQ,aAAa;EACvB,MAAM,SAAS,aACb,QAAQ,aACR,QAAQ,cACR,iBACF;EACA,IAAI,OAAO,QACT,MAAM,IAAI,UACR,wDAAwD,OAAO,OAC5D,KAAK,UAAU,MAAM,OAAO,CAAC,CAC7B,KAAK,IAAI,GACd;EAEF,eAAe,OAAO;CACxB;CAEA,OAAO,EACL,KAAK,MAAM;EACT,IAAI,OAAO,SAAS,YAClB,MAAM,IAAI,UAAU,iCAAiC;EAEvD,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,aAAa,QAAQ;EAEvB;CACF,EACF;AACF"}
@@ -190,7 +190,7 @@ declare const clientStateProjection: unique symbol;
190
190
  * the old name are ignored and the next read refolds from raw events.
191
191
  * See docs/concepts/04-state.mdx.
192
192
  */
193
- type Reducer<D extends EventDefs = EventDefs, S = unknown, P extends PresenceDefs = Record<never, never>> = {
193
+ type Reducer<D extends EventDefs = EventDefs, S = unknown, P extends PresenceDefs = Record<never, never>, V = S> = {
194
194
  /** Identity and cache key. Rename when the fold's logic changes. */
195
195
  readonly name: string;
196
196
  /** The contract's vocabulary — types every event in the fold. */
@@ -204,7 +204,7 @@ type Reducer<D extends EventDefs = EventDefs, S = unknown, P extends PresenceDef
204
204
  readonly fold: (state: S, event: ContractEvent<D>) => S;
205
205
  readonly initialState: S;
206
206
  /** Internal read-side projection. Never used as a fold input or persisted. */
207
- readonly [clientStateProjection]?: (state: S) => S;
207
+ readonly [clientStateProjection]?: (state: S) => V;
208
208
  /**
209
209
  * When present: `initialState` was validated against it at
210
210
  * definition, and any cached fold a2 rehydrates (server snapshots,
@@ -219,4 +219,4 @@ type ReducerBuilder<D extends EventDefs, S, P extends PresenceDefs = Record<neve
219
219
  };
220
220
  //#endregion
221
221
  export { ContractEvent as a, PresenceDefs as c, PresenceSnapshot as d, ReducerOptions as f, StandardSchemaV1 as h, Contract as i, PresenceMap as l, contract as m, ReducerBuilder as n, EventBatchFactory as o, WithPresence as p, AppendInput as r, EventDefs as s, Reducer as t, PresencePatch as u };
222
- //# sourceMappingURL=reducer-BcS9VDKC.d.ts.map
222
+ //# sourceMappingURL=reducer-otzHjuJj.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reducer-BcS9VDKC.d.ts","names":[],"sources":["../src/standard-schema.ts","../src/contract.ts","../src/reducer.ts"],"mappings":";;;;;;;;UAQiB,iBAAiB,iBAAiB,SAAS;;WAEjD,aAAa,iBAAiB,MAAM,OAAO;;kBAG7B;;mBAEN,MAAM,iBAAiB,SAAS;;aAEtC;;aAEA;;aAEA,WACP,mBACG,OAAO,UAAU,QAAQ,OAAO;;aAE5B,QAAQ,MAAM,OAAO;;;cAIpB,OAAO,UAAU,cAAc,UAAU;;mBAGpC,cAAc;;aAEpB,OAAO;;aAEP;;;mBAIM;;aAEN,QAAQ,cAAc;;;mBAIhB;;aAEN;;aAEA,OAAO,cAAc,cAAc;;;mBAI7B;;aAEN,KAAK;;;mBAIC,MAAM,iBAAiB,SAAS;;aAEtC,OAAO;;aAEP,QAAQ;;;cAIP,WAAW,eAAe,oBAAoB,YACxD;;cAIU,YAAY,eAAe,oBAAoB,YACzD;;;;;KC1DQ,YAAY,eAAe;;KAG3B,eAAe,eAAe;;KAG9B,cACV,UAAU,WACV,gBAAgB,mBAAmB,iBAElC,WAAW;EACV;EACA,MAAM;EACN,SAAS,iBAAiB,YAAY,EAAE;EACxC;EACA;EACA,WAAW;KAEb;;KAGU,YAAY,UAAU,gBAC/B,WAAW;EACV,MAAM;EACN,SAAS,iBAAiB,WAAW,EAAE;EACvC;WAEI;KAEI,kBAAkB,UAAU,oBAChC,mBAAmB,YAAY,SAElC,QAAQ,MACR;;;;;;KAOO,cAAc,UAAU,eAAe;EACjD;EACA,WACG,WAAW,cAAc,iBAAiB,YAAY,EAAE;EAE3D;EACA,IAAI;;;;;;;;KASM,YAAY,UAAU,eAAe;GAE5C,yBAAA,WAAW;IACV,OAAO,iBAAiB,YAAY,EAAE;IACtC;IACA,IAAI;;;;;;;;KAUE,iBAAiB,UAAU,eAAe;EACpD,UAAU,YAAY;;;;;;;KAQZ,aACV,UAAU,cACV,iBACQ,4BAA4B;KAE1B,eAAe,MAAM;;;;;EAK/B;;;;;;EAMA,cAAc;;;;;;EAMd,cAAc,iBAAiB,MAAM;;KAG3B,SACV,UAAU,YAAY,WACtB,UAAU,eAAe;;WAGhB;;WAEA,QAAQ,SAAS;;WAEjB,UAAU,SAAS;;WAEnB,OAAO,kBAAkB;;;;;;;;;;;EAWlC,QAAQ,MAAM,IAAI,MAChB,SAAS,eAAe,MAAM,KAC7B,eAAe,GAAG,GAAG;;;;;;;iBAQV,SACd,UAAU,WACV,UAAU,eAAe,sBACzB;;EAEA;;EAEA,QAAQ;;;;;;EAMR,WAAW;IACT,SAAS,GAAG;;;cClKH;;;;;;;;;;;KAcD,QACV,UAAU,YAAY,WACtB,aACA,UAAU,eAAe;;WAGhB;;WAEA,QAAQ,SAAS;;;;;;WAMjB,UAAU,SAAS;WACnB,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO;WAC7C,cAAc;;YAEb,0BAA0B,OAAO,MAAM;;;;;;;WAOxC,aAAa,0BAA0B;;;KAItC,eACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,KAAK,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO,IAAI,QAAQ,GAAG,GAAG"}
1
+ {"version":3,"file":"reducer-otzHjuJj.d.ts","names":[],"sources":["../src/standard-schema.ts","../src/contract.ts","../src/reducer.ts"],"mappings":";;;;;;;;UAQiB,iBAAiB,iBAAiB,SAAS;;WAEjD,aAAa,iBAAiB,MAAM,OAAO;;kBAG7B;;mBAEN,MAAM,iBAAiB,SAAS;;aAEtC;;aAEA;;aAEA,WACP,mBACG,OAAO,UAAU,QAAQ,OAAO;;aAE5B,QAAQ,MAAM,OAAO;;;cAIpB,OAAO,UAAU,cAAc,UAAU;;mBAGpC,cAAc;;aAEpB,OAAO;;aAEP;;;mBAIM;;aAEN,QAAQ,cAAc;;;mBAIhB;;aAEN;;aAEA,OAAO,cAAc,cAAc;;;mBAI7B;;aAEN,KAAK;;;mBAIC,MAAM,iBAAiB,SAAS;;aAEtC,OAAO;;aAEP,QAAQ;;;cAIP,WAAW,eAAe,oBAAoB,YACxD;;cAIU,YAAY,eAAe,oBAAoB,YACzD;;;;;KC1DQ,YAAY,eAAe;;KAG3B,eAAe,eAAe;;KAG9B,cACV,UAAU,WACV,gBAAgB,mBAAmB,iBAElC,WAAW;EACV;EACA,MAAM;EACN,SAAS,iBAAiB,YAAY,EAAE;EACxC;EACA;EACA,WAAW;KAEb;;KAGU,YAAY,UAAU,gBAC/B,WAAW;EACV,MAAM;EACN,SAAS,iBAAiB,WAAW,EAAE;EACvC;WAEI;KAEI,kBAAkB,UAAU,oBAChC,mBAAmB,YAAY,SAElC,QAAQ,MACR;;;;;;KAOO,cAAc,UAAU,eAAe;EACjD;EACA,WACG,WAAW,cAAc,iBAAiB,YAAY,EAAE;EAE3D;EACA,IAAI;;;;;;;;KASM,YAAY,UAAU,eAAe;GAE5C,yBAAA,WAAW;IACV,OAAO,iBAAiB,YAAY,EAAE;IACtC;IACA,IAAI;;;;;;;;KAUE,iBAAiB,UAAU,eAAe;EACpD,UAAU,YAAY;;;;;;;KAQZ,aACV,UAAU,cACV,iBACQ,4BAA4B;KAE1B,eAAe,MAAM;;;;;EAK/B;;;;;;EAMA,cAAc;;;;;;EAMd,cAAc,iBAAiB,MAAM;;KAG3B,SACV,UAAU,YAAY,WACtB,UAAU,eAAe;;WAGhB;;WAEA,QAAQ,SAAS;;WAEjB,UAAU,SAAS;;WAEnB,OAAO,kBAAkB;;;;;;;;;;;EAWlC,QAAQ,MAAM,IAAI,MAChB,SAAS,eAAe,MAAM,KAC7B,eAAe,GAAG,GAAG;;;;;;;iBAQV,SACd,UAAU,WACV,UAAU,eAAe,sBACzB;;EAEA;;EAEA,QAAQ;;;;;;EAMR,WAAW;IACT,SAAS,GAAG;;;cClKH;;;;;;;;;;;KAcD,QACV,UAAU,YAAY,WACtB,aACA,UAAU,eAAe,sBACzB,IAAI;;WAGK;;WAEA,QAAQ,SAAS;;;;;;WAMjB,UAAU,SAAS;WACnB,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO;WAC7C,cAAc;;YAEb,0BAA0B,OAAO,MAAM;;;;;;;WAOxC,aAAa,0BAA0B;;;KAItC,eACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,KAAK,OAAO,OAAO,GAAG,OAAO,cAAc,OAAO,IAAI,QAAQ,GAAG,GAAG"}
@@ -1,5 +1,5 @@
1
- import { r as Clock } from "./store-D_yhNdPz.js";
2
- import { E as SchedulerTask, t as A2Scheduler } from "./server-Bp5Nd1pF.js";
1
+ import { r as Clock } from "./store-DyZM6fS5.js";
2
+ import { E as SchedulerTask, t as A2Scheduler } from "./server-DbD7IJVK.js";
3
3
  //#region src/scheduler-qstash.d.ts
4
4
  type QStashStoreOutage = {
5
5
  version: 1;
@@ -1,7 +1,7 @@
1
1
  import { t as A2Error } from "./errors-DCk6ch5n.js";
2
2
  import { d as classifySchedulerSendFailure, g as serverInternals, t as DRAIN_TIMINGS, u as assertSchedulerTargets } from "./internal-Dq2qYxou.js";
3
3
  import { n as platformVercelOidcToken } from "./platform-B4TnJtWu.js";
4
- import { n as deliverSchedulerAppend } from "./server-CjJSGcF7.js";
4
+ import { n as deliverSchedulerAppend } from "./server-BD5ckxZb.js";
5
5
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
6
6
  import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
7
7
  import { t as normalizeSchedulerTask } from "./scheduler-task-BpzhPnRS.js";
@@ -1,5 +1,5 @@
1
- import { r as Clock } from "./store-D_yhNdPz.js";
2
- import { E as SchedulerTask, t as A2Scheduler } from "./server-Bp5Nd1pF.js";
1
+ import { r as Clock } from "./store-DyZM6fS5.js";
2
+ import { E as SchedulerTask, t as A2Scheduler } from "./server-DbD7IJVK.js";
3
3
  //#region src/scheduler-vercel.d.ts
4
4
  /**
5
5
  * The "not settled yet" signal for a stalled handler — an expected state,
@@ -1,5 +1,5 @@
1
1
  import { d as classifySchedulerSendFailure, g as serverInternals, t as DRAIN_TIMINGS, u as assertSchedulerTargets } from "./internal-Dq2qYxou.js";
2
- import { n as deliverSchedulerAppend } from "./server-CjJSGcF7.js";
2
+ import { n as deliverSchedulerAppend } from "./server-BD5ckxZb.js";
3
3
  import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
4
4
  import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
5
5
  import { t as normalizeSchedulerTask } from "./scheduler-task-BpzhPnRS.js";
@@ -1174,22 +1174,14 @@ function createServer(options) {
1174
1174
  throw asStoreUnavailable(err);
1175
1175
  }
1176
1176
  };
1177
- const readCachedState = async (store, sessionId, reducerName, throughIndex, snapshotThroughIndex) => {
1177
+ const readStoreState = async (store, sessionId, reducerName, throughIndex, snapshotThroughIndex) => {
1178
1178
  try {
1179
1179
  return await store.readState(nsId(sessionId), reducerName, throughIndex === void 0 && snapshotThroughIndex === void 0 ? void 0 : {
1180
1180
  ...throughIndex === void 0 ? {} : { throughIndex },
1181
1181
  ...snapshotThroughIndex === void 0 ? {} : { snapshotThroughIndex }
1182
1182
  });
1183
- } catch {
1184
- try {
1185
- return {
1186
- headIndex: null,
1187
- snapshot: null,
1188
- events: await store.read(nsId(sessionId), throughIndex === void 0 ? void 0 : { throughIndex })
1189
- };
1190
- } catch (err) {
1191
- throw asStoreUnavailable(err);
1192
- }
1183
+ } catch (err) {
1184
+ throw asStoreUnavailable(err);
1193
1185
  }
1194
1186
  };
1195
1187
  const planStateReads = (batch) => {
@@ -1289,7 +1281,29 @@ function createServer(options) {
1289
1281
  if (write.pinEventIndexes.size > 0) snapshot.pinEventIndexes = [...write.pinEventIndexes];
1290
1282
  snapshots.push(snapshot);
1291
1283
  }
1292
- const persistence = Promise.resolve().then(() => store.putSnapshots(nsId(plan.sessionId), reducer.name, snapshots)).catch(() => {});
1284
+ const persistence = Promise.resolve().then(() => telemetry.span("a2.snapshot", {
1285
+ "a2.contract": name,
1286
+ "a2.session_id": plan.sessionId,
1287
+ "a2.snapshot.reducer": reducer.name,
1288
+ "a2.snapshot.index": snapshots.at(-1).index,
1289
+ "a2.snapshot.count": snapshots.length
1290
+ }, async (span) => {
1291
+ try {
1292
+ await store.putSnapshots(nsId(plan.sessionId), reducer.name, snapshots);
1293
+ } catch (error) {
1294
+ try {
1295
+ span.recordError(error);
1296
+ } catch (telemetryError) {
1297
+ throw new AggregateError([error, telemetryError], "Snapshot write and error reporting failed", { cause: telemetryError });
1298
+ }
1299
+ }
1300
+ })).catch((error) => {
1301
+ console.error("[a2] error in a2.snapshot telemetry", {
1302
+ sessionId: plan.sessionId,
1303
+ reducer: reducer.name,
1304
+ index: snapshots.at(-1).index
1305
+ }, error);
1306
+ });
1293
1307
  track(persistence);
1294
1308
  platformWaitUntil(persistence);
1295
1309
  }
@@ -1303,21 +1317,20 @@ function createServer(options) {
1303
1317
  sessionId: nsId(plan.sessionId),
1304
1318
  ...plan.throughIndex === void 0 ? {} : { throughIndex: plan.throughIndex },
1305
1319
  ...plan.snapshotThroughIndex === void 0 ? {} : { snapshotThroughIndex: plan.snapshotThroughIndex }
1306
- })), reducer.name) : await Promise.all(plans.map((plan) => readCachedState(store, plan.sessionId, reducer.name, plan.throughIndex, plan.snapshotThroughIndex)));
1307
- } catch {
1308
- reads = null;
1309
- }
1310
- if (!reads || reads.length !== plans.length) try {
1311
- reads = await Promise.all(plans.map((plan) => readCachedState(store, plan.sessionId, reducer.name, plan.throughIndex, plan.snapshotThroughIndex)));
1312
- } catch (err) {
1313
- for (const entry of batch) entry.reject(err);
1320
+ })), reducer.name) : plans.map((plan) => readStoreState(store, plan.sessionId, reducer.name, plan.throughIndex, plan.snapshotThroughIndex));
1321
+ if (reads.length !== plans.length) throw new TypeError("store.readStates must return one result per request");
1322
+ } catch (error) {
1323
+ const failure = asStoreUnavailable(error);
1324
+ for (const entry of batch) entry.reject(failure);
1314
1325
  return;
1315
1326
  }
1316
- for (const [position, plan] of plans.entries()) try {
1317
- await foldStatePlan(store, reducer, plan, reads[position]);
1318
- } catch (err) {
1319
- for (const entry of plan.entries) entry.reject(err);
1320
- }
1327
+ await Promise.all(plans.map(async (plan, position) => {
1328
+ try {
1329
+ await foldStatePlan(store, reducer, plan, await reads[position]);
1330
+ } catch (error) {
1331
+ for (const entry of plan.entries) entry.reject(error);
1332
+ }
1333
+ }));
1321
1334
  };
1322
1335
  const enqueueStateRead = (store, sessionId, reducer, throughIndex, pinEventIndex, span) => new Promise((resolve, reject) => {
1323
1336
  const reducerName = reducer.name;
@@ -2325,4 +2338,4 @@ function cloneInitial(initial) {
2325
2338
  //#endregion
2326
2339
  export { sseResponse as a, setServerFetchHooks as i, deliverSchedulerAppend as n, parsePresenceSibling as o, handler as r, createServer as t };
2327
2340
 
2328
- //# sourceMappingURL=server-CjJSGcF7.js.map
2341
+ //# sourceMappingURL=server-BD5ckxZb.js.map