experimental-a2 0.6.0 → 0.8.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.
- package/CHANGELOG.md +41 -0
- package/dist/ai-server.d.ts +4 -5
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +20 -17
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +334 -2
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +1 -1
- package/dist/client.d.ts +202 -2
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1025 -1
- package/dist/client.js.map +1 -0
- package/dist/errors-BQuJpe82.js.map +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/{internal-DstsI6Re.js → internal-DRXJ56EI.js} +5 -28
- package/dist/internal-DRXJ56EI.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.js +4 -5
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +4 -4
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-Duw6MVlB.js → server-B2XNevQA.js} +830 -131
- package/dist/server-B2XNevQA.js.map +1 -0
- package/dist/{server-DpvjhdoE.d.ts → server-DjPhHnbI.d.ts} +71 -50
- package/dist/server-DjPhHnbI.d.ts.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/{store-DysUkTH3.d.ts → store-RJO35BMj.d.ts} +24 -62
- package/dist/store-RJO35BMj.d.ts.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +80 -78
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-dSeLxzfb.js → store-polling-6DW7F1DT.js} +2 -2
- package/dist/{store-polling-dSeLxzfb.js.map → store-polling-6DW7F1DT.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +231 -182
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-redis-core-BFLwz0Wj.js → store-redis-core-DT01r4GZ.js} +213 -161
- package/dist/store-redis-core-DT01r4GZ.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +3 -4
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +4 -5
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +104 -91
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{wire-BFQmSJ-9.js → wire-B6te_wns.js} +4 -3
- package/dist/wire-B6te_wns.js.map +1 -0
- package/docs/concepts/02-handlers.mdx +4 -0
- package/docs/concepts/04-state.mdx +57 -9
- package/docs/guides/03-react.mdx +20 -28
- package/docs/guides/05-production.mdx +9 -11
- package/docs/guides/06-ai-agents.mdx +10 -15
- package/docs/guides/09-presence.mdx +14 -19
- package/docs/guides/10-transports.mdx +104 -86
- package/docs/reference/01-api.mdx +182 -293
- package/docs/reference/02-errors.mdx +5 -7
- package/package.json +1 -14
- package/src/ai-server.ts +36 -15
- package/src/client.ts +2 -2
- package/src/errors.ts +1 -0
- package/src/internal.ts +3 -62
- package/src/push-envelope.ts +24 -21
- package/src/scheduler-qstash.ts +3 -3
- package/src/scheduler-vercel.ts +2 -2
- package/src/server-fetch.ts +344 -0
- package/src/server.ts +315 -312
- package/src/session-socket.ts +36 -20
- package/src/sse.ts +2 -2
- package/src/store-memory.ts +138 -101
- package/src/store-postgres.ts +355 -238
- package/src/store-redis-core.ts +247 -237
- package/src/store-redis-http.ts +1 -2
- package/src/store-redis.ts +1 -2
- package/src/store-sqlite.ts +191 -153
- package/src/store.ts +24 -66
- package/src/wire.ts +2 -1
- package/dist/ai-D_PGS-JR.d.ts +0 -334
- package/dist/ai-D_PGS-JR.d.ts.map +0 -1
- package/dist/cli-B3VuxoDe.js +0 -599
- package/dist/cli-B3VuxoDe.js.map +0 -1
- package/dist/cli-bin.d.ts +0 -1
- package/dist/cli-bin.js +0 -7
- package/dist/cli-bin.js.map +0 -1
- package/dist/cli.d.ts +0 -20
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -2
- package/dist/client-CdMqi7mC.d.ts +0 -202
- package/dist/client-CdMqi7mC.d.ts.map +0 -1
- package/dist/client-Dj5d3SP_.js +0 -1026
- package/dist/client-Dj5d3SP_.js.map +0 -1
- package/dist/devtools-J_jZ2vQf.d.ts +0 -152
- package/dist/devtools-J_jZ2vQf.d.ts.map +0 -1
- package/dist/devtools-kJJaORn-.js +0 -340
- package/dist/devtools-kJJaORn-.js.map +0 -1
- package/dist/devtools-server.browser.d.ts +0 -1
- package/dist/devtools-server.browser.js +0 -6
- package/dist/devtools-server.browser.js.map +0 -1
- package/dist/devtools-server.d.ts +0 -23
- package/dist/devtools-server.d.ts.map +0 -1
- package/dist/devtools-server.js +0 -1270
- package/dist/devtools-server.js.map +0 -1
- package/dist/devtools.d.ts +0 -2
- package/dist/devtools.js +0 -2
- package/dist/errors-W6nwJ-fm.d.ts +0 -21
- package/dist/errors-W6nwJ-fm.d.ts.map +0 -1
- package/dist/http.d.ts +0 -151
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -706
- package/dist/http.js.map +0 -1
- package/dist/inspection-DaxB5jM2.js +0 -13
- package/dist/inspection-DaxB5jM2.js.map +0 -1
- package/dist/internal-DstsI6Re.js.map +0 -1
- package/dist/platform-B4TnJtWu.js +0 -34
- package/dist/platform-B4TnJtWu.js.map +0 -1
- package/dist/server-DpvjhdoE.d.ts.map +0 -1
- package/dist/server-Duw6MVlB.js.map +0 -1
- package/dist/store-DysUkTH3.d.ts.map +0 -1
- package/dist/store-redis-core-BFLwz0Wj.js.map +0 -1
- package/dist/testing.browser.d.ts +0 -1
- package/dist/testing.browser.js +0 -6
- package/dist/testing.browser.js.map +0 -1
- package/dist/testing.d.ts +0 -32
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -103
- package/dist/testing.js.map +0 -1
- package/dist/wire-BFQmSJ-9.js.map +0 -1
- package/docs/guides/07-devtools.mdx +0 -229
- package/src/cli-bin.ts +0 -5
- package/src/cli.ts +0 -1046
- package/src/devtools-app.ts +0 -989
- package/src/devtools-server.browser.ts +0 -5
- package/src/devtools-server.ts +0 -604
- package/src/devtools.ts +0 -716
- package/src/http.ts +0 -394
- package/src/inspection.ts +0 -39
- package/src/testing.browser.ts +0 -5
- package/src/testing.ts +0 -185
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
import { a as EventDefs, c as PresencePatch, d as WithPresence, l as PresenceSnapshot, n as Contract, o as PresenceDefs, p as Reducer, r as ContractEvent, s as PresenceMap, t as AppendInput } from "./contract-jIfaR085.js";
|
|
2
|
-
import { t as A2Store } from "./store-
|
|
2
|
+
import { t as A2Store } from "./store-RJO35BMj.js";
|
|
3
3
|
import { i as A2Telemetry } from "./telemetry-CpeclqB2.js";
|
|
4
|
+
//#region src/session-socket.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The structural socket `sessionsSocket` speaks against — satisfied
|
|
7
|
+
* by `ws` and by `@vercel/functions`' upgraded socket alike, so the
|
|
8
|
+
* platform upgrade API stays out of the library. `message` data
|
|
9
|
+
* arrives as whatever the platform delivers (string, Buffer,
|
|
10
|
+
* ArrayBuffer, or fragments); the handler normalizes defensively.
|
|
11
|
+
*/
|
|
12
|
+
type A2Socket = {
|
|
13
|
+
send(data: string): void;
|
|
14
|
+
on(event: "message", listener: (data: unknown) => void): void;
|
|
15
|
+
on(event: "close", listener: () => void): void;
|
|
16
|
+
on(event: "error", listener: (error: unknown) => void): void;
|
|
17
|
+
close(code?: number, reason?: string): void;
|
|
18
|
+
/** Bytes queued but not yet transmitted — when exposed, the down
|
|
19
|
+
* pump uses it for backpressure (`SOCKET_TIMINGS`). */
|
|
20
|
+
bufferedAmount?: number;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/server-fetch.d.ts
|
|
24
|
+
type SuggestedEventType<D extends EventDefs> = (keyof D & string) | (string & Record<never, never>);
|
|
25
|
+
type SuggestedPresenceValues<P extends PresenceDefs> = Readonly<{ [F in keyof P & string]?: unknown; } & Record<string, unknown>>;
|
|
26
|
+
type A2PushEvent<D extends EventDefs = EventDefs> = {
|
|
27
|
+
readonly type: SuggestedEventType<D>;
|
|
28
|
+
readonly payload: unknown;
|
|
29
|
+
readonly id?: string;
|
|
30
|
+
};
|
|
31
|
+
type A2PushPresence<P extends PresenceDefs = PresenceDefs> = {
|
|
32
|
+
readonly participant: string;
|
|
33
|
+
readonly values: SuggestedPresenceValues<P>;
|
|
34
|
+
readonly seen?: number;
|
|
35
|
+
readonly at?: number;
|
|
36
|
+
};
|
|
37
|
+
type A2Operation<D extends EventDefs = EventDefs, P extends PresenceDefs = Record<never, never>> = {
|
|
38
|
+
readonly type: "stream";
|
|
39
|
+
readonly sessionId: string;
|
|
40
|
+
readonly startAfter: number;
|
|
41
|
+
readonly transport: "http" | "websocket";
|
|
42
|
+
} | {
|
|
43
|
+
readonly type: "history";
|
|
44
|
+
readonly sessionId: string;
|
|
45
|
+
readonly gte: number;
|
|
46
|
+
readonly lte: number;
|
|
47
|
+
readonly transport: "http";
|
|
48
|
+
} | {
|
|
49
|
+
readonly type: "push";
|
|
50
|
+
readonly sessionId: string;
|
|
51
|
+
readonly events: readonly A2PushEvent<D>[];
|
|
52
|
+
readonly presence?: A2PushPresence<P>;
|
|
53
|
+
readonly transport: "http" | "websocket";
|
|
54
|
+
};
|
|
55
|
+
type UpgradeWebSocket = (attach: (socket: A2Socket) => void) => Response | Promise<Response>;
|
|
56
|
+
type ServerFetchOptions<D extends EventDefs = EventDefs, P extends PresenceDefs = Record<never, never>> = {
|
|
57
|
+
authorize?: (operation: A2Operation<D, P>) => boolean | Promise<boolean>;
|
|
58
|
+
upgradeWebSocket?: UpgradeWebSocket;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
4
61
|
//#region src/scheduler-task.d.ts
|
|
5
62
|
/** One event carried by a delayed append task. */
|
|
6
63
|
type ScheduledEvent = {
|
|
@@ -33,42 +90,6 @@ type SchedulerAppendTask = {
|
|
|
33
90
|
type SchedulerTask = SchedulerDrainTask | SchedulerAppendTask;
|
|
34
91
|
//#endregion
|
|
35
92
|
//#region src/server.d.ts
|
|
36
|
-
/**
|
|
37
|
-
* Events that arrived over the wire through `parsePushBody` — already
|
|
38
|
-
* envelope-validated, headed for schema validation inside `append`.
|
|
39
|
-
* The brand lets the documented push route hand them straight to
|
|
40
|
-
* `session.append` without weakening typed appends for app code: a
|
|
41
|
-
* hand-written `{ type: string }` literal still fails to compile.
|
|
42
|
-
*/
|
|
43
|
-
type PushedEvent = {
|
|
44
|
-
type: string;
|
|
45
|
-
payload: unknown;
|
|
46
|
-
id?: string;
|
|
47
|
-
readonly "~a2.pushed": true;
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* A presence patch that arrived over the wire through `parsePushBody`
|
|
51
|
-
* — same provenance brand as `PushedEvent`, so the documented route
|
|
52
|
-
* hands it whole to `session.setPresence` while a hand-written
|
|
53
|
-
* untyped patch still fails to compile. Field validation happens
|
|
54
|
-
* inside `setPresence`.
|
|
55
|
-
*/
|
|
56
|
-
type PushedPresence = {
|
|
57
|
-
participant: string;
|
|
58
|
-
values: Record<string, unknown>;
|
|
59
|
-
seen?: number;
|
|
60
|
-
/** The sender's LWW stamp in epoch ms; receipt time when absent. */
|
|
61
|
-
at?: number;
|
|
62
|
-
readonly "~a2.pushed": true;
|
|
63
|
-
};
|
|
64
|
-
type PushValidationContext = {
|
|
65
|
-
sessionId: string;
|
|
66
|
-
events: readonly PushedEvent[];
|
|
67
|
-
/** The whole pushed patch — present exactly on the presence-plane
|
|
68
|
-
* invocation, so the callback authorizes the participant id and can
|
|
69
|
-
* apply size or cardinality policy. */
|
|
70
|
-
presence?: PushedPresence;
|
|
71
|
-
};
|
|
72
93
|
/** What every handler receives. */
|
|
73
94
|
type HandlerContext<D extends EventDefs, K extends keyof D & string = keyof D & string, P extends PresenceDefs = Record<never, never>> = {
|
|
74
95
|
/** The triggering event. */
|
|
@@ -88,11 +109,7 @@ type LaneContext<D extends EventDefs, K extends keyof D & string = keyof D & str
|
|
|
88
109
|
};
|
|
89
110
|
};
|
|
90
111
|
type Lane<D extends EventDefs, K extends keyof D & string = keyof D & string> = string | ((context: LaneContext<D, K>) => string);
|
|
91
|
-
type SessionDispatch<D extends EventDefs> =
|
|
92
|
-
(...events: AppendInput<D>[]): Promise<ContractEvent<D>[]>;
|
|
93
|
-
/** The push-route path: events from `parsePushBody`. */
|
|
94
|
-
(...events: PushedEvent[]): Promise<ContractEvent<D>[]>;
|
|
95
|
-
};
|
|
112
|
+
type SessionDispatch<D extends EventDefs> = (...events: AppendInput<D>[]) => Promise<ContractEvent<D>[]>;
|
|
96
113
|
type SessionAppend<D extends EventDefs> = SessionDispatch<D> & {
|
|
97
114
|
/** Commit, then hand pending work directly to configured scheduler. */
|
|
98
115
|
dispatch: SessionDispatch<D>;
|
|
@@ -107,6 +124,9 @@ type ScheduleTiming = {
|
|
|
107
124
|
delay?: never;
|
|
108
125
|
};
|
|
109
126
|
type SessionSchedule<D extends EventDefs> = (name: string, timing: ScheduleTiming, ...events: AppendInput<D>[]) => Promise<void>;
|
|
127
|
+
type StateOptions = {
|
|
128
|
+
through?: number | "latest";
|
|
129
|
+
};
|
|
110
130
|
/**
|
|
111
131
|
* The presence members of a session — intersected in via
|
|
112
132
|
* `WithPresence`, so they exist exactly when the contract declares
|
|
@@ -135,7 +155,7 @@ type SessionPresence<D extends EventDefs, P extends PresenceDefs> = {
|
|
|
135
155
|
values: PresencePatch<P>["values"];
|
|
136
156
|
seen?: number;
|
|
137
157
|
at?: number;
|
|
138
|
-
}
|
|
158
|
+
}): Promise<void>;
|
|
139
159
|
/** The current map, expired values pruned — a point-in-time read. */
|
|
140
160
|
presence(): Promise<PresenceMap<P>>;
|
|
141
161
|
};
|
|
@@ -148,14 +168,13 @@ type Session<D extends EventDefs, Append = SessionAppend<D>, P extends PresenceD
|
|
|
148
168
|
gte?: number;
|
|
149
169
|
lte?: number;
|
|
150
170
|
}): Promise<ContractEvent<D>[]>;
|
|
151
|
-
state<S>(reducer: Reducer<D, S
|
|
171
|
+
state<S>(reducer: Reducer<D, S>, options?: StateOptions): Promise<{
|
|
152
172
|
state: S;
|
|
153
173
|
index: number;
|
|
154
174
|
}>;
|
|
155
175
|
/**
|
|
156
176
|
* A live feed of this session's events, starting after `startAfter`
|
|
157
|
-
* (exclusive). Server-side only
|
|
158
|
-
* exposes it over SSE as the route's stream lane.
|
|
177
|
+
* (exclusive). Server-side only. `server.fetch` exposes it over SSE.
|
|
159
178
|
*/
|
|
160
179
|
stream(opts?: {
|
|
161
180
|
startAfter?: number;
|
|
@@ -190,6 +209,10 @@ type A2Scheduler = {
|
|
|
190
209
|
type A2Server<D extends EventDefs, P extends PresenceDefs = Record<never, never>> = {
|
|
191
210
|
/** The contract this server implements. */
|
|
192
211
|
readonly contract: Contract<D, P>;
|
|
212
|
+
readonly fetch: {
|
|
213
|
+
(request: Request): Promise<Response>;
|
|
214
|
+
(request: Request, options: ServerFetchOptions<D, P>): Promise<Response>;
|
|
215
|
+
};
|
|
193
216
|
session(id: string): Session<D, SessionAppend<D>, P>;
|
|
194
217
|
/**
|
|
195
218
|
* Process every currently eligible event. `settled` means nothing
|
|
@@ -236,8 +259,6 @@ type ServerOptions<D extends EventDefs, P extends PresenceDefs = Record<never, n
|
|
|
236
259
|
scheduler?: A2Scheduler;
|
|
237
260
|
/** Optional instrumentation — e.g. `otel()` from `experimental-a2/otel`. */
|
|
238
261
|
telemetry?: A2Telemetry;
|
|
239
|
-
/** Validate events that came through `parsePushBody` before writing them. */
|
|
240
|
-
validatePush?: (context: PushValidationContext) => void | PromiseLike<void>;
|
|
241
262
|
/**
|
|
242
263
|
* Presence-plane policy — valid only when the contract declares
|
|
243
264
|
* presence fields (TypeError at construction otherwise). `ttlMs` is
|
|
@@ -257,5 +278,5 @@ type ServerOptions<D extends EventDefs, P extends PresenceDefs = Record<never, n
|
|
|
257
278
|
/** Implement a contract: bind its vocabulary to storage and reactions. */
|
|
258
279
|
declare function createServer<D extends EventDefs, P extends PresenceDefs = Record<never, never>>(options: ServerOptions<D, P>): A2Server<D, P>;
|
|
259
280
|
//#endregion
|
|
260
|
-
export {
|
|
261
|
-
//# sourceMappingURL=server-
|
|
281
|
+
export { UpgradeWebSocket as A, SchedulerAppendTask as C, A2PushEvent as D, A2Operation as E, A2PushPresence as O, ScheduledEvent as S, SchedulerTask as T, SessionPresence as _, Handler as a, createServer as b, HandlerEntry as c, ScheduleDelay as d, ScheduleTiming as f, SessionDispatch as g, SessionAppend as h, DrainableServer as i, A2Socket as j, ServerFetchOptions as k, Lane as l, Session as m, A2Server as n, HandlerAppend as o, ServerOptions as p, AbortSpec as r, HandlerContext as s, A2Scheduler as t, LaneContext as u, SessionSchedule as v, SchedulerDrainTask as w, deliverSchedulerAppend as x, StateOptions as y };
|
|
282
|
+
//# sourceMappingURL=server-DjPhHnbI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-DjPhHnbI.d.ts","names":[],"sources":["../src/session-socket.ts","../src/server-fetch.ts","../src/scheduler-task.ts","../src/server.ts"],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as EventDefs, c as PresencePatch, l as PresenceSnapshot, n as Contract, o as PresenceDefs, r as ContractEvent, s as PresenceMap, t as AppendInput } from "./contract-jIfaR085.js";
|
|
2
|
-
import { a as
|
|
3
|
-
import { C as
|
|
4
|
-
export { A2Scheduler, A2Server, type
|
|
2
|
+
import { a as EventCause, c as PresenceRow, d as StoreStateRead, f as StoredEvent, i as Event, l as StoreAppendResult, n as AppendEvent, o as FailAttemptResult, r as Clock, s as IdSource, t as A2Store, u as StoreClaimAvailableResult } from "./store-RJO35BMj.js";
|
|
3
|
+
import { A as UpgradeWebSocket, C as SchedulerAppendTask, D as A2PushEvent, E as A2Operation, O as A2PushPresence, S as ScheduledEvent, T as SchedulerTask, _ as SessionPresence, a as Handler, b as createServer, c as HandlerEntry, d as ScheduleDelay, f as ScheduleTiming, g as SessionDispatch, h as SessionAppend, i as DrainableServer, j as A2Socket, k as ServerFetchOptions, l as Lane, m as Session, n as A2Server, o as HandlerAppend, p as ServerOptions, r as AbortSpec, s as HandlerContext, t as A2Scheduler, u as LaneContext, v as SessionSchedule, w as SchedulerDrainTask, x as deliverSchedulerAppend, y as StateOptions } from "./server-DjPhHnbI.js";
|
|
4
|
+
export { type A2Operation, type A2PushEvent, type A2PushPresence, A2Scheduler, A2Server, type A2Socket, type A2Store, AbortSpec, type AppendEvent, type AppendInput, type Clock, type Contract, type ContractEvent, DrainableServer, type Event, type EventCause, type EventDefs, type FailAttemptResult, Handler, HandlerAppend, HandlerContext, HandlerEntry, type IdSource, Lane, LaneContext, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceRow, type PresenceSnapshot, ScheduleDelay, ScheduleTiming, type ScheduledEvent, type SchedulerAppendTask, type SchedulerDrainTask, type SchedulerTask, type ServerFetchOptions, ServerOptions, Session, SessionAppend, SessionDispatch, SessionPresence, SessionSchedule, StateOptions, type StoreAppendResult, type StoreClaimAvailableResult, type StoreStateRead, type StoredEvent, type UpgradeWebSocket, createServer, deliverSchedulerAppend };
|
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as deliverSchedulerAppend, t as createServer } from "./server-
|
|
1
|
+
import { n as deliverSchedulerAppend, t as createServer } from "./server-B2XNevQA.js";
|
|
2
2
|
export { createServer, deliverSchedulerAppend };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-N8PXxDAS.js","names":[],"sources":["../src/store.ts"],"sourcesContent":["/**\n * The A2Store interface — the storage contract every store backend\n * implements. See specs/a2-implementation.md §2–3.\n *\n * This is the whole storage contract: append, read, dispatch claims,\n * failure markers, snapshots, and the live stream. The scheduler\n * needs nothing extra — the armed queue message is its own state, and\n * the store is the only thing it consults.\n */\n\nimport type { PresencePatch } from './contract.ts'\n\n/** A stored event, as the public API exposes it. */\nexport type Event = {\n id: string\n type: string\n payload: unknown\n /** Position in the session's event log, from 1. */\n index: number\n sessionId: string\n createdAt: Date\n}\n\n/** The handler dispatch whose append first persisted a child event. */\nexport type EventCause = {\n index: number\n attempt: number\n /** Size of one named handler append, used to reject truncated retries. */\n batchSize?: number\n}\n\n/**\n * What the store persists: immutable event history, including its causal edge,\n * plus derived dispatch and failure bookkeeping. The bookkeeping is disposable;\n * the event and its cause are not.\n */\nexport type StoredEvent = Event & {\n /** Same-session handler dispatch that appended this event; null means root. */\n cause: EventCause | null\n /** Session-scoped serial execution key resolved when the event is appended. */\n lane: string | null\n /** Adapter clock time recorded by append settlement, completion, or manual skip; null while pending. */\n processedAt: Date | null\n /** Dispatch attempt that completed this event; null without dispatch or while pending. */\n processedByAttempt: number | null\n /** Exact ordered child ids atomically returned by the completing attempt. */\n returnedEventIds: string[] | null\n /** Adapter clock time recorded by the first durable dispatch claim. */\n firstClaimedAt: Date | null\n /** Adapter clock time recorded by the most recent durable dispatch claim. */\n lastClaimedAt: Date | null\n /** Durable dispatch claims, including claims abandoned by hard kills. */\n attemptCount: number\n /** Current dispatch holder; null when the event is not claimed. */\n claimHolder: string | null\n /** Adapter clock expiry for the current dispatch claim. */\n claimExpiresAt: Date | null\n /** Caught handler failures. This alone drives dead-lettering. */\n failureCount: number\n /** Adapter clock time recorded by the most recent caught handler failure. */\n lastFailedAt: Date | null\n /** Dispatch attempt that produced the most recent caught handler failure. */\n lastFailedAttempt: number | null\n /** The last handler failure, stringified. */\n lastError: string | null\n /** Adapter clock time recorded when dead-lettered; null otherwise. */\n failedAt: Date | null\n}\n\n/** Durable, read-only session metadata for administrative inspection. */\nexport type StoredSessionSummary = {\n sessionId: string\n eventCount: number\n pendingCount: number\n failedCount: number\n /** Durable dispatch claims across every event in the session. */\n attemptCount: number\n /** Caught handler failures across every event in the session. */\n failureCount: number\n firstEventAt: Date\n updatedAt: Date\n}\n\n/**\n * One live presence value, as `A2Store.presence.read` returns it. The\n * row is the LWW unit — one participant's one field. `at` is the\n * sender's stamp, the LWW comparator. `expiresAt` is the storage's\n * own clock at the applied set plus its `ttlMs` — never derived from\n * the sender stamp; a row is live strictly before it.\n */\nexport type PresenceRow = {\n participant: string\n field: string\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\n/** Metadata for the latest cached fold of one reducer. */\nexport type StoredSnapshot = {\n reducerName: string\n index: number\n updatedAt: Date\n}\n\n/** One consistent cache-plus-tail read for a reducer fold. */\nexport type StoreStateRead = {\n /** The latest cached fold for this reducer, if one exists. */\n snapshot: { index: number; state: unknown } | null\n /** Immutable events strictly after `snapshot.index`, or the full log on a miss. */\n events: Event[]\n}\n\nexport type StoredSessionPage = {\n sessions: StoredSessionSummary[]\n /** Opaque backend cursor; null means there is no next page. */\n cursor: string | null\n}\n\nexport type StoredEventPage = {\n events: StoredEvent[]\n /** Fixed inclusive log frontier shared by every page in one inspection. */\n throughIndex: number\n}\n\n/**\n * Optional read-only administration implemented by A2's shipped stores.\n * It is separate from the correctness contract so custom stores do not\n * need to expose storage-wide discovery merely to process events.\n */\nexport type A2StoreInspection = {\n listSessions(options: {\n /** Storage namespace prefix, including the contract separator. */\n prefix: string\n cursor?: string\n limit: number\n }): Promise<StoredSessionPage>\n /**\n * A bounded event page for read-only administration. The first page captures\n * `throughIndex`; callers pass it back so concurrent appends cannot extend a\n * walk indefinitely. Optional for custom stores; A2 falls back to `read()`.\n */\n readEvents?(\n sessionId: string,\n options: { afterIndex: number; throughIndex?: number; limit: number },\n ): Promise<StoredEventPage>\n listSnapshots(sessionId: string): Promise<StoredSnapshot[]>\n}\n\n/** The result of atomically claiming every currently eligible event. */\nexport type StoreClaimAvailableResult =\n | { outcome: 'claimed'; events: StoredEvent[] }\n | { outcome: 'busy'; retryAt: Date }\n | { outcome: 'settled' }\n\n/** A completion may lose to a newer claim or an earlier completion. */\nexport type CompleteAttemptResult =\n { outcome: 'completed'; events: StoredEvent[] } | { outcome: 'superseded' }\n\n/** The result of one claim-renewal heartbeat, both lists in append order. */\nexport type RenewClaimsResult = {\n /** Listed claims that remain owned by the holder. */\n renewed: number[]\n /** Listed claims whose event a newer attempt has durably taken. */\n superseded: number[]\n}\n\n/** The result of atomically recording a caught handler failure. */\nexport type FailAttemptResult = {\n outcome: 'failed' | 'dead_lettered' | 'superseded'\n failureCount: number\n}\n\n/** Input to `A2Store.append` — already validated by the machine. */\nexport type AppendEvent = {\n type: string\n payload: unknown\n /** Caller-supplied idempotency key; generated when absent. */\n id?: string\n /** Internal causal edge supplied atomically by handler `session.append`. */\n cause?: EventCause\n /** Session-scoped serial execution key resolved before persistence. */\n lane?: string\n /** Internal hint: settle this event in the append transaction; no handler is registered. */\n settled?: true\n}\n\n/** A handler-returned event with the deterministic id retries require. */\nexport type ReturnedEvent = AppendEvent & { id: string }\n\n/** The rows written by an append and the session's pending state. */\nexport type StoreAppendResult = {\n events: StoredEvent[]\n /** Whether the session contains an event without a completion marker. */\n hasPending: boolean\n}\n\n/**\n * An injectable clock. Adapters take one so tests can drive claim\n * expiry, failure timestamps, and (later) stuck-session detection\n * deterministically — against real storage, no mocking.\n */\nexport type Clock = {\n now(): Date\n}\n\n/** An injectable id source for generated event ids. */\nexport type IdSource = () => string\n\nexport const SYSTEM_CLOCK: Clock = {\n now: () => new Date(),\n}\n\nexport const RANDOM_IDS: IdSource = () => crypto.randomUUID()\n\nexport interface A2Store {\n /**\n * Accepts a batch; the batch is atomic — one transaction, consecutive\n * `index`es, all-or-nothing. The idempotency key covers the whole\n * operation, not each item: if *every* event's `id` already exists in\n * this session, this is a retry of a committed batch whose ack was\n * lost — return the existing rows as success. If only *some* ids\n * exist, the caller mixed an already-sent batch with fresh events —\n * always a caller bug — so throw `A2Error('PARTIAL_DUPLICATE_BATCH')`.\n * Events carrying `settled: true` get `processedAt` in this same atomic\n * operation, with no dispatch claim or `processedByAttempt`. The result's\n * `hasPending` reflects the whole session in the same atomic operation,\n * including older events and idempotent retries.\n *\n * An event carrying `cause` is a handler append, fenced by attempt\n * currency: accept it only while `cause.attempt` is still the parent\n * event's latest attempt and the parent is not dead-lettered; otherwise\n * throw `A2Error('SUPERSEDED_ATTEMPT')` and write nothing. The check is\n * part of this atomic operation and must serialize against a concurrent\n * `claimAvailable` — an unlocked read of the parent admits write skew.\n * The idempotent-replay path runs first, so a batch whose ids all exist\n * replays regardless of the current attempt.\n */\n append(sessionId: string, events: AppendEvent[]): Promise<StoreAppendResult>\n\n /** Events for one session, oldest first. Bounds form `(afterIndex, throughIndex]`. */\n read(\n sessionId: string,\n opts?: { afterIndex?: number; throughIndex?: number },\n ): Promise<StoredEvent[]>\n\n /**\n * Atomically claims every eligible pending event. Unlaned events are all\n * independently eligible. Within a lane, only the lowest-index unfinished\n * event is eligible. A live claim produces `busy` only when it is the sole\n * remaining obstacle to actionable work. Claimed events are returned in event-log\n * order. Excluded rows remain lane barriers.\n */\n claimAvailable(options: {\n sessionId: string\n holder: string\n ttlMs: number\n expiresAtMs?: number\n excludeIndexes?: readonly number[]\n }): Promise<StoreClaimAvailableResult>\n\n /**\n * Renews the listed live claims still owned by `holder`. Each listed claim\n * carries the attempt ordinal the holder owns. `renewed` lists the claims\n * that remain owned after the operation; `superseded` lists the claims\n * whose event's `attemptCount` has durably passed the listed attempt. An\n * expired claim no successor has taken appears in neither list — its\n * attempt may still complete (`completeAttempt` is the fence), so it is\n * not reported as lost. Renewal never revives an expired claim.\n */\n renewClaims(options: {\n sessionId: string\n holder: string\n claims: readonly { index: number; attempt: number }[]\n ttlMs: number\n expiresAtMs?: number\n }): Promise<RenewClaimsResult>\n\n /**\n * Atomically completes one current attempt and appends its returned events.\n * Retrying a committed completion with the same attempt and deterministic\n * child ids returns the existing children. A stale attempt never appends.\n */\n completeAttempt(options: {\n sessionId: string\n index: number\n attempt: number\n events: ReturnedEvent[]\n }): Promise<CompleteAttemptResult>\n\n /**\n * Atomically records a caught failure for one claimed attempt. A stale\n * attempt cannot poison a processed event or a newer dispatch. Accepted\n * failures record the operation's clock time.\n */\n failAttempt(options: {\n sessionId: string\n index: number\n attempt: number\n error: string\n maxFailures: number\n }): Promise<FailAttemptResult>\n\n /**\n * Reads a reducer snapshot and its event tail as one consistent adapter\n * operation. On a cache miss, `snapshot` is null and `events` is the full\n * log. The snapshot is untrusted; core may reject it and issue a full\n * `read()` when its state schema no longer accepts the cached value.\n */\n readState(sessionId: string, reducerName: string): Promise<StoreStateRead>\n\n /**\n * Optional batched `readState` — one consistent snapshot-plus-tail read\n * per session id, aligned positionally with the input (duplicates\n * allowed). Each element has its own frontier; the batch makes no\n * cross-session consistency claim. Core falls back to parallel\n * `readState` calls when absent.\n */\n readStates?(\n sessionIds: string[],\n reducerName: string,\n ): Promise<StoreStateRead[]>\n\n /**\n * Writes a disposable reducer cache. Guard this operation so a slower\n * concurrent writer can never clobber a further-along snapshot\n * (`where up_to_index < excluded.up_to_index`).\n */\n putSnapshot(\n sessionId: string,\n reducerName: string,\n index: number,\n state: unknown,\n ): Promise<void>\n\n /**\n * Optional ephemeral-plane capability (specs/a2-implementation.md\n * §15.1) — optional like snapshots are. Values arrive already\n * validated by core; adapters store them opaquely. Presence never\n * touches the event log: no index, no history row, no recovery arm.\n */\n presence?: {\n /**\n * Field-wise last-writer-wins merge of one participant's values:\n * a field whose existing row has a strictly newer `at` (the\n * sender's stamp) is left untouched; `null` deletes the row. Every\n * applied write refreshes that field's expiry to the storage's own\n * clock plus `ttlMs` — the sender stamp orders writes but never\n * anchors their lifetime.\n */\n set(\n ns: string,\n participant: string,\n values: Record<string, unknown | null>,\n meta: { seen: number; at: Date; ttlMs: number },\n ): Promise<void>\n\n /** The current map, pruned of rows at or past their `expiresAt`. */\n read(ns: string): Promise<PresenceRow[]>\n\n /**\n * Push-tier patch delivery; present only on backends with a real\n * broadcast primitive. Without it the backend is the degraded\n * tier: live feeds surface presence by re-reading on their\n * existing poll cadence.\n */\n subscribe?(ns: string, onPatch: (patch: PresencePatch) => void): () => void\n }\n\n /** Read-only operational data used by `experimental-a2/devtools/server`. */\n inspect?: A2StoreInspection\n\n /**\n * A live feed of one session's events, starting after `startAfter`\n * (exclusive). Transport is the backend's choice — in-process pub/sub,\n * polling, LISTEN/NOTIFY — callers never branch on which. The iterable\n * ends when the consumer calls `return()` (e.g. a disconnecting SSE\n * client) and must deliver events appended after subscription.\n */\n stream(\n sessionId: string,\n opts?: { startAfter?: number },\n ): AsyncIterable<Event>\n}\n"],"mappings":";AAkNA,MAAa,eAAsB,EACjC,2BAAW,IAAI,KAAK,EACtB;AAEA,MAAa,mBAA6B,OAAO,WAAW"}
|
|
1
|
+
{"version":3,"file":"store-N8PXxDAS.js","names":[],"sources":["../src/store.ts"],"sourcesContent":["/**\n * The A2Store interface — the storage contract every store backend\n * implements. See specs/a2-implementation.md §2–3.\n *\n * This is the whole storage contract: append, read, dispatch claims,\n * failure markers, snapshots, and the live stream. The scheduler\n * needs nothing extra — the armed queue message is its own state, and\n * the store is the only thing it consults.\n */\n\nimport type { PresencePatch } from './contract.ts'\n\n/** A stored event, as the public API exposes it. */\nexport type Event = {\n id: string\n type: string\n payload: unknown\n /** Position in the session's event log, from 1. */\n index: number\n sessionId: string\n createdAt: Date\n}\n\n/** The handler dispatch whose append first persisted a child event. */\nexport type EventCause = {\n index: number\n attempt: number\n /** Size of one named handler append, used to reject truncated retries. */\n batchSize?: number\n}\n\n/**\n * What the store persists: immutable event history, including its causal edge,\n * plus derived dispatch and failure bookkeeping. The bookkeeping is disposable;\n * the event and its cause are not.\n */\nexport type StoredEvent = Event & {\n /** Same-session handler dispatch that appended this event; null means root. */\n cause: EventCause | null\n /** Session-scoped serial execution key resolved when the event is appended. */\n lane: string | null\n /** Adapter clock time recorded by append settlement, completion, or manual skip; null while pending. */\n processedAt: Date | null\n /** Dispatch attempt that completed this event; null without dispatch or while pending. */\n processedByAttempt: number | null\n /** Exact ordered child ids atomically returned by the completing attempt. */\n returnedEventIds: string[] | null\n /** Adapter clock time recorded by the first durable dispatch claim. */\n firstClaimedAt: Date | null\n /** Adapter clock time recorded by the most recent durable dispatch claim. */\n lastClaimedAt: Date | null\n /** Durable dispatch claims, including claims abandoned by hard kills. */\n attemptCount: number\n /** Current dispatch holder; null when the event is not claimed. */\n claimHolder: string | null\n /** Adapter clock expiry for the current dispatch claim. */\n claimExpiresAt: Date | null\n /** Caught handler failures. This alone drives dead-lettering. */\n failureCount: number\n /** Adapter clock time recorded by the most recent caught handler failure. */\n lastFailedAt: Date | null\n /** Dispatch attempt that produced the most recent caught handler failure. */\n lastFailedAttempt: number | null\n /** The last handler failure, stringified. */\n lastError: string | null\n /** Adapter clock time recorded when dead-lettered; null otherwise. */\n failedAt: Date | null\n}\n\n/**\n * One live presence value, as `A2Store.presence.read` returns it. The\n * row is the LWW unit — one participant's one field. `at` is the\n * sender's stamp, the LWW comparator. `expiresAt` is the storage's\n * own clock at the applied set plus its `ttlMs` — never derived from\n * the sender stamp; a row is live strictly before it.\n */\nexport type PresenceRow = {\n participant: string\n field: string\n value: unknown\n seen: number\n at: Date\n expiresAt: Date\n}\n\n/** One consistent cache-plus-tail read for a reducer fold. */\nexport type StoreStateRead = {\n /** The current head checkpoint, even when an older historical snapshot is selected. */\n headIndex: number | null\n /** The greatest cached fold at or before the requested snapshot frontier. */\n snapshot: { index: number; state: unknown } | null\n /** Immutable events strictly after `snapshot.index`, or the full log on a miss. */\n events: Event[]\n}\n\nexport type StoreSnapshotWrite = {\n index: number\n state: unknown\n /** Unfinished trigger events that durably retain this exact checkpoint. */\n pinEventIndexes?: readonly number[]\n}\n\nexport type StoreStateReadRequest = {\n sessionId: string\n throughIndex?: number\n snapshotThroughIndex?: number\n}\n/** The result of atomically claiming every currently eligible event. */\nexport type StoreClaimAvailableResult =\n | { outcome: 'claimed'; events: StoredEvent[] }\n | { outcome: 'busy'; retryAt: Date }\n | { outcome: 'settled' }\n\n/** A completion may lose to a newer claim or an earlier completion. */\nexport type CompleteAttemptResult =\n { outcome: 'completed'; events: StoredEvent[] } | { outcome: 'superseded' }\n\n/** The result of one claim-renewal heartbeat, both lists in append order. */\nexport type RenewClaimsResult = {\n /** Listed claims that remain owned by the holder. */\n renewed: number[]\n /** Listed claims whose event a newer attempt has durably taken. */\n superseded: number[]\n}\n\n/** The result of atomically recording a caught handler failure. */\nexport type FailAttemptResult = {\n outcome: 'failed' | 'dead_lettered' | 'superseded'\n failureCount: number\n}\n\n/** Input to `A2Store.append` — already validated by the machine. */\nexport type AppendEvent = {\n type: string\n payload: unknown\n /** Caller-supplied idempotency key; generated when absent. */\n id?: string\n /** Internal causal edge supplied atomically by handler `session.append`. */\n cause?: EventCause\n /** Session-scoped serial execution key resolved before persistence. */\n lane?: string\n /** Internal hint: settle this event in the append transaction; no handler is registered. */\n settled?: true\n}\n\n/** A handler-returned event with the deterministic id retries require. */\nexport type ReturnedEvent = AppendEvent & { id: string }\n\n/** The rows written by an append and the session's pending state. */\nexport type StoreAppendResult = {\n events: StoredEvent[]\n /** Whether the session contains an event without a completion marker. */\n hasPending: boolean\n}\n\n/**\n * An injectable clock. Adapters take one so tests can drive claim\n * expiry, failure timestamps, and (later) stuck-session detection\n * deterministically — against real storage, no mocking.\n */\nexport type Clock = {\n now(): Date\n}\n\n/** An injectable id source for generated event ids. */\nexport type IdSource = () => string\n\nexport const SYSTEM_CLOCK: Clock = {\n now: () => new Date(),\n}\n\nexport const RANDOM_IDS: IdSource = () => crypto.randomUUID()\n\nexport interface A2Store {\n /**\n * Accepts a batch; the batch is atomic — one transaction, consecutive\n * `index`es, all-or-nothing. The idempotency key covers the whole\n * operation, not each item: if *every* event's `id` already exists in\n * this session, this is a retry of a committed batch whose ack was\n * lost — return the existing rows as success. If only *some* ids\n * exist, the caller mixed an already-sent batch with fresh events —\n * always a caller bug — so throw `A2Error('PARTIAL_DUPLICATE_BATCH')`.\n * Events carrying `settled: true` get `processedAt` in this same atomic\n * operation, with no dispatch claim or `processedByAttempt`. The result's\n * `hasPending` reflects the whole session in the same atomic operation,\n * including older events and idempotent retries.\n *\n * An event carrying `cause` is a handler append, fenced by attempt\n * currency: accept it only while `cause.attempt` is still the parent\n * event's latest attempt and the parent is not dead-lettered; otherwise\n * throw `A2Error('SUPERSEDED_ATTEMPT')` and write nothing. The check is\n * part of this atomic operation and must serialize against a concurrent\n * `claimAvailable` — an unlocked read of the parent admits write skew.\n * The idempotent-replay path runs first, so a batch whose ids all exist\n * replays regardless of the current attempt.\n */\n append(sessionId: string, events: AppendEvent[]): Promise<StoreAppendResult>\n\n /** Events for one session, oldest first. Bounds form `(afterIndex, throughIndex]`. */\n read(\n sessionId: string,\n opts?: { afterIndex?: number; throughIndex?: number },\n ): Promise<StoredEvent[]>\n\n /**\n * Atomically claims every eligible pending event. Unlaned events are all\n * independently eligible. Within a lane, only the lowest-index unfinished\n * event is eligible. A live claim produces `busy` only when it is the sole\n * remaining obstacle to actionable work. Claimed events are returned in event-log\n * order. Excluded rows remain lane barriers.\n */\n claimAvailable(options: {\n sessionId: string\n holder: string\n ttlMs: number\n expiresAtMs?: number\n excludeIndexes?: readonly number[]\n }): Promise<StoreClaimAvailableResult>\n\n /**\n * Renews the listed live claims still owned by `holder`. Each listed claim\n * carries the attempt ordinal the holder owns. `renewed` lists the claims\n * that remain owned after the operation; `superseded` lists the claims\n * whose event's `attemptCount` has durably passed the listed attempt. An\n * expired claim no successor has taken appears in neither list — its\n * attempt may still complete (`completeAttempt` is the fence), so it is\n * not reported as lost. Renewal never revives an expired claim.\n */\n renewClaims(options: {\n sessionId: string\n holder: string\n claims: readonly { index: number; attempt: number }[]\n ttlMs: number\n expiresAtMs?: number\n }): Promise<RenewClaimsResult>\n\n /**\n * Atomically completes one current attempt and appends its returned events.\n * Retrying a committed completion with the same attempt and deterministic\n * child ids returns the existing children. A stale attempt never appends.\n */\n completeAttempt(options: {\n sessionId: string\n index: number\n attempt: number\n events: ReturnedEvent[]\n }): Promise<CompleteAttemptResult>\n\n /**\n * Atomically records a caught failure for one claimed attempt. A stale\n * attempt cannot poison a processed event or a newer dispatch. Accepted\n * failures record the operation's clock time.\n */\n failAttempt(options: {\n sessionId: string\n index: number\n attempt: number\n error: string\n maxFailures: number\n }): Promise<FailAttemptResult>\n\n /**\n * Reads a reducer snapshot and its event tail as one consistent adapter\n * operation. On a cache miss, `snapshot` is null and `events` is the full\n * log. The snapshot is untrusted; core may reject it and issue a full\n * `read()` when its state schema no longer accepts the cached value.\n */\n readState(\n sessionId: string,\n reducerName: string,\n options?: { throughIndex?: number; snapshotThroughIndex?: number },\n ): Promise<StoreStateRead>\n\n /**\n * Optional batched `readState` — one consistent snapshot-plus-tail read\n * per session id, aligned positionally with the input (duplicates\n * allowed). Each element has its own frontier; the batch makes no\n * cross-session consistency claim. Core falls back to parallel\n * `readState` calls when absent.\n */\n readStates?(\n requests: readonly StoreStateReadRequest[],\n reducerName: string,\n ): Promise<StoreStateRead[]>\n\n /**\n * Atomically writes disposable reducer checkpoints. The greatest index\n * advances the head cache. Older checkpoints survive only when at least one\n * listed trigger event is still unfinished; completion and dead-lettering\n * release that event's pins and collect unreferenced historical checkpoints.\n */\n putSnapshots(\n sessionId: string,\n reducerName: string,\n snapshots: readonly StoreSnapshotWrite[],\n ): Promise<void>\n\n /**\n * Optional ephemeral-plane capability (specs/a2-implementation.md\n * §15.1) — optional like snapshots are. Values arrive already\n * validated by core; adapters store them opaquely. Presence never\n * touches the event log: no index, no history row, no recovery arm.\n */\n presence?: {\n /**\n * Field-wise last-writer-wins merge of one participant's values:\n * a field whose existing row has a strictly newer `at` (the\n * sender's stamp) is left untouched; `null` deletes the row. Every\n * applied write refreshes that field's expiry to the storage's own\n * clock plus `ttlMs` — the sender stamp orders writes but never\n * anchors their lifetime.\n */\n set(\n ns: string,\n participant: string,\n values: Record<string, unknown | null>,\n meta: { seen: number; at: Date; ttlMs: number },\n ): Promise<void>\n\n /** The current map, pruned of rows at or past their `expiresAt`. */\n read(ns: string): Promise<PresenceRow[]>\n\n /**\n * Push-tier patch delivery; present only on backends with a real\n * broadcast primitive. Without it the backend is the degraded\n * tier: live feeds surface presence by re-reading on their\n * existing poll cadence.\n */\n subscribe?(ns: string, onPatch: (patch: PresencePatch) => void): () => void\n }\n\n /**\n * A live feed of one session's events, starting after `startAfter`\n * (exclusive). Transport is the backend's choice — in-process pub/sub,\n * polling, LISTEN/NOTIFY — callers never branch on which. The iterable\n * ends when the consumer calls `return()` (e.g. a disconnecting SSE\n * client) and must deliver events appended after subscription.\n */\n stream(\n sessionId: string,\n opts?: { startAfter?: number },\n ): AsyncIterable<Event>\n}\n"],"mappings":";AAuKA,MAAa,eAAsB,EACjC,2BAAW,IAAI,KAAK,EACtB;AAEA,MAAa,mBAA6B,OAAO,WAAW"}
|
|
@@ -54,19 +54,6 @@ type StoredEvent = Event & {
|
|
|
54
54
|
/** Adapter clock time recorded when dead-lettered; null otherwise. */
|
|
55
55
|
failedAt: Date | null;
|
|
56
56
|
};
|
|
57
|
-
/** Durable, read-only session metadata for administrative inspection. */
|
|
58
|
-
type StoredSessionSummary = {
|
|
59
|
-
sessionId: string;
|
|
60
|
-
eventCount: number;
|
|
61
|
-
pendingCount: number;
|
|
62
|
-
failedCount: number;
|
|
63
|
-
/** Durable dispatch claims across every event in the session. */
|
|
64
|
-
attemptCount: number;
|
|
65
|
-
/** Caught handler failures across every event in the session. */
|
|
66
|
-
failureCount: number;
|
|
67
|
-
firstEventAt: Date;
|
|
68
|
-
updatedAt: Date;
|
|
69
|
-
};
|
|
70
57
|
/**
|
|
71
58
|
* One live presence value, as `A2Store.presence.read` returns it. The
|
|
72
59
|
* row is the LWW unit — one participant's one field. `at` is the
|
|
@@ -82,15 +69,11 @@ type PresenceRow = {
|
|
|
82
69
|
at: Date;
|
|
83
70
|
expiresAt: Date;
|
|
84
71
|
};
|
|
85
|
-
/** Metadata for the latest cached fold of one reducer. */
|
|
86
|
-
type StoredSnapshot = {
|
|
87
|
-
reducerName: string;
|
|
88
|
-
index: number;
|
|
89
|
-
updatedAt: Date;
|
|
90
|
-
};
|
|
91
72
|
/** One consistent cache-plus-tail read for a reducer fold. */
|
|
92
73
|
type StoreStateRead = {
|
|
93
|
-
/** The
|
|
74
|
+
/** The current head checkpoint, even when an older historical snapshot is selected. */
|
|
75
|
+
headIndex: number | null;
|
|
76
|
+
/** The greatest cached fold at or before the requested snapshot frontier. */
|
|
94
77
|
snapshot: {
|
|
95
78
|
index: number;
|
|
96
79
|
state: unknown;
|
|
@@ -98,39 +81,16 @@ type StoreStateRead = {
|
|
|
98
81
|
/** Immutable events strictly after `snapshot.index`, or the full log on a miss. */
|
|
99
82
|
events: Event[];
|
|
100
83
|
};
|
|
101
|
-
type
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
type StoredEventPage = {
|
|
107
|
-
events: StoredEvent[];
|
|
108
|
-
/** Fixed inclusive log frontier shared by every page in one inspection. */
|
|
109
|
-
throughIndex: number;
|
|
84
|
+
type StoreSnapshotWrite = {
|
|
85
|
+
index: number;
|
|
86
|
+
state: unknown;
|
|
87
|
+
/** Unfinished trigger events that durably retain this exact checkpoint. */
|
|
88
|
+
pinEventIndexes?: readonly number[];
|
|
110
89
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*/
|
|
116
|
-
type A2StoreInspection = {
|
|
117
|
-
listSessions(options: {
|
|
118
|
-
/** Storage namespace prefix, including the contract separator. */
|
|
119
|
-
prefix: string;
|
|
120
|
-
cursor?: string;
|
|
121
|
-
limit: number;
|
|
122
|
-
}): Promise<StoredSessionPage>;
|
|
123
|
-
/**
|
|
124
|
-
* A bounded event page for read-only administration. The first page captures
|
|
125
|
-
* `throughIndex`; callers pass it back so concurrent appends cannot extend a
|
|
126
|
-
* walk indefinitely. Optional for custom stores; A2 falls back to `read()`.
|
|
127
|
-
*/
|
|
128
|
-
readEvents?(sessionId: string, options: {
|
|
129
|
-
afterIndex: number;
|
|
130
|
-
throughIndex?: number;
|
|
131
|
-
limit: number;
|
|
132
|
-
}): Promise<StoredEventPage>;
|
|
133
|
-
listSnapshots(sessionId: string): Promise<StoredSnapshot[]>;
|
|
90
|
+
type StoreStateReadRequest = {
|
|
91
|
+
sessionId: string;
|
|
92
|
+
throughIndex?: number;
|
|
93
|
+
snapshotThroughIndex?: number;
|
|
134
94
|
};
|
|
135
95
|
/** The result of atomically claiming every currently eligible event. */
|
|
136
96
|
type StoreClaimAvailableResult = {
|
|
@@ -285,7 +245,10 @@ interface A2Store {
|
|
|
285
245
|
* log. The snapshot is untrusted; core may reject it and issue a full
|
|
286
246
|
* `read()` when its state schema no longer accepts the cached value.
|
|
287
247
|
*/
|
|
288
|
-
readState(sessionId: string, reducerName: string
|
|
248
|
+
readState(sessionId: string, reducerName: string, options?: {
|
|
249
|
+
throughIndex?: number;
|
|
250
|
+
snapshotThroughIndex?: number;
|
|
251
|
+
}): Promise<StoreStateRead>;
|
|
289
252
|
/**
|
|
290
253
|
* Optional batched `readState` — one consistent snapshot-plus-tail read
|
|
291
254
|
* per session id, aligned positionally with the input (duplicates
|
|
@@ -293,13 +256,14 @@ interface A2Store {
|
|
|
293
256
|
* cross-session consistency claim. Core falls back to parallel
|
|
294
257
|
* `readState` calls when absent.
|
|
295
258
|
*/
|
|
296
|
-
readStates?(
|
|
259
|
+
readStates?(requests: readonly StoreStateReadRequest[], reducerName: string): Promise<StoreStateRead[]>;
|
|
297
260
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
261
|
+
* Atomically writes disposable reducer checkpoints. The greatest index
|
|
262
|
+
* advances the head cache. Older checkpoints survive only when at least one
|
|
263
|
+
* listed trigger event is still unfinished; completion and dead-lettering
|
|
264
|
+
* release that event's pins and collect unreferenced historical checkpoints.
|
|
301
265
|
*/
|
|
302
|
-
|
|
266
|
+
putSnapshots(sessionId: string, reducerName: string, snapshots: readonly StoreSnapshotWrite[]): Promise<void>;
|
|
303
267
|
/**
|
|
304
268
|
* Optional ephemeral-plane capability (specs/a2-implementation.md
|
|
305
269
|
* §15.1) — optional like snapshots are. Values arrive already
|
|
@@ -330,8 +294,6 @@ interface A2Store {
|
|
|
330
294
|
*/
|
|
331
295
|
subscribe?(ns: string, onPatch: (patch: PresencePatch) => void): () => void;
|
|
332
296
|
};
|
|
333
|
-
/** Read-only operational data used by `experimental-a2/devtools/server`. */
|
|
334
|
-
inspect?: A2StoreInspection;
|
|
335
297
|
/**
|
|
336
298
|
* A live feed of one session's events, starting after `startAfter`
|
|
337
299
|
* (exclusive). Transport is the backend's choice — in-process pub/sub,
|
|
@@ -344,5 +306,5 @@ interface A2Store {
|
|
|
344
306
|
}): AsyncIterable<Event>;
|
|
345
307
|
}
|
|
346
308
|
//#endregion
|
|
347
|
-
export {
|
|
348
|
-
//# sourceMappingURL=store-
|
|
309
|
+
export { EventCause as a, PresenceRow as c, StoreStateRead as d, StoredEvent as f, Event as i, StoreAppendResult as l, AppendEvent as n, FailAttemptResult as o, Clock as r, IdSource as s, A2Store as t, StoreClaimAvailableResult as u };
|
|
310
|
+
//# sourceMappingURL=store-RJO35BMj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-RJO35BMj.d.ts","names":[],"sources":["../src/store.ts"],"mappings":";;;KAaY;EACV;EACA;EACA;;EAEA;EACA;EACA,WAAW;;;KAID;EACV;EACA;;EAEA;;;;;;;KAQU,cAAc;;EAExB,OAAO;;EAEP;;EAEA,aAAa;;EAEb;;EAEA;;EAEA,gBAAgB;;EAEhB,eAAe;;EAEf;;EAEA;;EAEA,gBAAgB;;EAEhB;;EAEA,cAAc;;EAEd;;EAEA;;EAEA,UAAU;;;;;;;;;KAUA;EACV;EACA;EACA;EACA;EACA,IAAI;EACJ,WAAW;;;KAID;;EAEV;;EAEA;IAAY;IAAe;;;EAE3B,QAAQ;;KAGE;EACV;EACA;;EAEA;;KAGU;EACV;EACA;EACA;;;KAGU;EACN;EAAoB,QAAQ;;EAC5B;EAAiB,SAAS;;EAC1B;;;KAGM;EACR;EAAsB,QAAQ;;EAAoB;;;KAG1C;;EAEV;;EAEA;;;KAIU;EACV;EACA;;;KAIU;EACV;EACA;;EAEA;;EAEA,QAAQ;;EAER;;EAEA;;;KAIU,gBAAgB;EAAgB;;;KAGhC;EACV,QAAQ;;EAER;;;;;;;KAQU;EACV,OAAO;;;KAIG;UAQK;;;;;;;;;;;;;;;;;;;;;;;EAuBf,OAAO,mBAAmB,QAAQ,gBAAgB,QAAQ;;EAG1D,KACE,mBACA;IAAS;IAAqB;MAC7B,QAAQ;;;;;;;;EASX,eAAe;IACb;IACA;IACA;IACA;IACA;MACE,QAAQ;;;;;;;;;;EAWZ,YAAY;IACV;IACA;IACA;MAAmB;MAAe;;IAClC;IACA;MACE,QAAQ;;;;;;EAOZ,gBAAgB;IACd;IACA;IACA;IACA,QAAQ;MACN,QAAQ;;;;;;EAOZ,YAAY;IACV;IACA;IACA;IACA;IACA;MACE,QAAQ;;;;;;;EAQZ,UACE,mBACA,qBACA;IAAY;IAAuB;MAClC,QAAQ;;;;;;;;EASX,YACE,mBAAmB,yBACnB,sBACC,QAAQ;;;;;;;EAQX,aACE,mBACA,qBACA,oBAAoB,uBACnB;;;;;;;EAQH;;;;;;;;;IASE,IACE,YACA,qBACA,QAAQ,gCACR;MAAQ;MAAc,IAAI;MAAM;QAC/B;;IAGH,KAAK,aAAa,QAAQ;;;;;;;IAQ1B,WAAW,YAAY,UAAU,OAAO;;;;;;;;;EAU1C,OACE,mBACA;IAAS;MACR,cAAc"}
|
package/dist/store-memory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as Clock, s as IdSource, t as A2Store } from "./store-RJO35BMj.js";
|
|
2
2
|
//#region src/store-memory.d.ts
|
|
3
3
|
type MemoryStoreOptions = {
|
|
4
4
|
/** Injectable clock — every stored timestamp comes from here. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-memory.d.ts","names":[],"sources":["../src/store-memory.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"store-memory.d.ts","names":[],"sources":["../src/store-memory.ts"],"mappings":";;KA0BY;;EAEV,QAAQ;;EAER,MAAM;;iBA+EQ,OAAO,UAAS,qBAA0B"}
|