experimental-a2 0.3.0 → 0.5.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 +207 -0
- package/dist/ai-CFNeCrRl.d.ts +334 -0
- package/dist/ai-CFNeCrRl.d.ts.map +1 -0
- package/dist/ai-server.browser.js +2 -0
- package/dist/ai-server.browser.js.map +1 -0
- package/dist/ai-server.d.ts +49 -9
- package/dist/ai-server.d.ts.map +1 -0
- package/dist/ai-server.js +123 -49
- package/dist/ai-server.js.map +1 -0
- package/dist/ai.d.ts +2 -303
- package/dist/ai.js +233 -86
- package/dist/ai.js.map +1 -0
- package/dist/cli-B3VuxoDe.js +599 -0
- package/dist/cli-B3VuxoDe.js.map +1 -0
- package/dist/cli-bin.d.ts +1 -0
- package/dist/cli-bin.js +7 -0
- package/dist/cli-bin.js.map +1 -0
- package/dist/cli.d.ts +20 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2 -0
- package/dist/client-BKlyLiOU.js +1008 -0
- package/dist/client-BKlyLiOU.js.map +1 -0
- package/dist/client-D7mvIXrF.d.ts +191 -0
- package/dist/client-D7mvIXrF.d.ts.map +1 -0
- package/dist/client.d.ts +2 -90
- package/dist/client.js +1 -409
- package/dist/{contract-CG_adnu_.js → contract-48bUMgcL.js} +12 -2
- package/dist/contract-48bUMgcL.js.map +1 -0
- package/dist/{contract-C_3dIIEU.d.ts → contract-jIfaR085.d.ts} +63 -8
- package/dist/contract-jIfaR085.d.ts.map +1 -0
- package/dist/devtools-J_jZ2vQf.d.ts +152 -0
- package/dist/devtools-J_jZ2vQf.d.ts.map +1 -0
- package/dist/devtools-kJJaORn-.js +340 -0
- package/dist/devtools-kJJaORn-.js.map +1 -0
- package/dist/devtools-server.browser.js +3 -1
- package/dist/devtools-server.browser.js.map +1 -0
- package/dist/devtools-server.d.ts +4 -3
- package/dist/devtools-server.d.ts.map +1 -0
- package/dist/devtools-server.js +226 -43
- package/dist/devtools-server.js.map +1 -0
- package/dist/devtools.d.ts +2 -0
- package/dist/devtools.js +2 -0
- package/dist/{errors-BJRMd-h6.js → errors-BQuJpe82.js} +6 -4
- package/dist/errors-BQuJpe82.js.map +1 -0
- package/dist/{errors-xL_JTXsY.d.ts → errors-W6nwJ-fm.d.ts} +3 -2
- package/dist/errors-W6nwJ-fm.d.ts.map +1 -0
- package/dist/http.d.ts +135 -28
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +647 -61
- package/dist/http.js.map +1 -0
- package/dist/{idempotent-replay-BMyHrP0L.js → idempotent-replay-DuqEkYA7.js} +4 -2
- package/dist/idempotent-replay-DuqEkYA7.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/{inspection-E7qbD0Xj.js → inspection-DaxB5jM2.js} +4 -1
- package/dist/inspection-DaxB5jM2.js.map +1 -0
- package/dist/internal-DstsI6Re.js +211 -0
- package/dist/internal-DstsI6Re.js.map +1 -0
- package/dist/otel.d.ts +3 -2
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +2 -0
- package/dist/otel.js.map +1 -0
- package/dist/platform-B4TnJtWu.js +34 -0
- package/dist/platform-B4TnJtWu.js.map +1 -0
- package/dist/react.d.ts +47 -15
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +30 -15
- package/dist/react.js.map +1 -0
- package/dist/retryable-lazy-DZWmHpii.js +2 -0
- package/dist/retryable-lazy-DZWmHpii.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +79 -0
- package/dist/scheduler-qstash.d.ts.map +1 -0
- package/dist/scheduler-qstash.js +501 -0
- package/dist/scheduler-qstash.js.map +1 -0
- package/dist/scheduler-task-BpzhPnRS.js +56 -0
- package/dist/scheduler-task-BpzhPnRS.js.map +1 -0
- package/dist/{recovery-vercel.d.ts → scheduler-vercel.d.ts} +18 -24
- package/dist/scheduler-vercel.d.ts.map +1 -0
- package/dist/scheduler-vercel.js +228 -0
- package/dist/scheduler-vercel.js.map +1 -0
- package/dist/server-C72KOw51.js +1457 -0
- package/dist/server-C72KOw51.js.map +1 -0
- package/dist/server-DUF9pjsx.d.ts +261 -0
- package/dist/server-DUF9pjsx.d.ts.map +1 -0
- package/dist/server.browser.js +2 -0
- package/dist/server.browser.js.map +1 -0
- package/dist/server.d.ts +4 -155
- package/dist/server.js +2 -2
- package/dist/{log-ldf5g8Cx.d.ts → store-DysUkTH3.d.ts} +120 -35
- package/dist/store-DysUkTH3.d.ts.map +1 -0
- package/dist/{log-yJbXUf72.js → store-N8PXxDAS.js} +3 -1
- package/dist/store-N8PXxDAS.js.map +1 -0
- package/dist/store-codec-DTG0Ftek.js +10 -0
- package/dist/store-codec-DTG0Ftek.js.map +1 -0
- package/dist/store-memory.d.ts +12 -0
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/{log-memory.js → store-memory.js} +145 -34
- package/dist/store-memory.js.map +1 -0
- package/dist/{log-polling-DZ1MiKLg.js → store-polling-dSeLxzfb.js} +7 -5
- package/dist/store-polling-dSeLxzfb.js.map +1 -0
- package/dist/{log-postgres.d.ts → store-postgres.d.ts} +7 -6
- package/dist/store-postgres.d.ts.map +1 -0
- package/dist/{log-postgres.js → store-postgres.js} +214 -24
- package/dist/store-postgres.js.map +1 -0
- package/dist/{log-redis-core-CyJ5L8yR.js → store-redis-core-BFLwz0Wj.js} +271 -66
- package/dist/store-redis-core-BFLwz0Wj.js.map +1 -0
- package/dist/{log-redis-http.d.ts → store-redis-http.d.ts} +8 -7
- package/dist/store-redis-http.d.ts.map +1 -0
- package/dist/{log-redis-http.js → store-redis-http.js} +22 -12
- package/dist/store-redis-http.js.map +1 -0
- package/dist/{log-redis.d.ts → store-redis.d.ts} +7 -6
- package/dist/store-redis.d.ts.map +1 -0
- package/dist/{log-redis.js → store-redis.js} +101 -39
- package/dist/store-redis.js.map +1 -0
- package/dist/{log-sqlite.d.ts → store-sqlite.d.ts} +7 -6
- package/dist/store-sqlite.d.ts.map +1 -0
- package/dist/{log-sqlite.js → store-sqlite.js} +118 -22
- package/dist/store-sqlite.js.map +1 -0
- package/dist/{telemetry-Cso0qyHQ.d.ts → telemetry-BjYHTfh2.d.ts} +3 -2
- package/dist/telemetry-BjYHTfh2.d.ts.map +1 -0
- package/dist/testing.browser.d.ts +1 -0
- package/dist/testing.browser.js +6 -0
- package/dist/testing.browser.js.map +1 -0
- package/dist/testing.d.ts +32 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +103 -0
- package/dist/testing.js.map +1 -0
- package/dist/validate-XKT4FSNn.js +2 -0
- package/dist/validate-XKT4FSNn.js.map +1 -0
- package/dist/wire-BFQmSJ-9.js +284 -0
- package/dist/wire-BFQmSJ-9.js.map +1 -0
- package/docs/01-quickstart.mdx +3 -3
- package/docs/concepts/01-contracts.mdx +18 -13
- package/docs/concepts/02-handlers.mdx +7 -7
- package/docs/concepts/03-durability.mdx +21 -21
- package/docs/concepts/04-state.mdx +18 -21
- package/docs/guides/01-timers.mdx +154 -49
- package/docs/guides/02-cancellation.mdx +30 -4
- package/docs/guides/03-react.mdx +76 -57
- package/docs/guides/04-local-first.mdx +1 -1
- package/docs/guides/05-production.mdx +310 -61
- package/docs/guides/06-ai-agents.mdx +252 -74
- package/docs/guides/07-devtools.mdx +137 -12
- package/docs/guides/08-application-data.mdx +1 -1
- package/docs/guides/09-presence.mdx +263 -0
- package/docs/guides/10-transports.mdx +140 -0
- package/docs/index.mdx +18 -14
- package/docs/reference/01-api.mdx +839 -110
- package/docs/reference/02-errors.mdx +13 -6
- package/package.json +24 -7
- package/src/ai-coordinator.ts +358 -0
- package/src/ai-projector.ts +524 -0
- package/src/ai-sdk-step.ts +261 -0
- package/src/ai-server.browser.ts +5 -0
- package/src/ai-server.ts +1719 -0
- package/src/ai.ts +2155 -0
- package/src/cache-indexeddb.ts +10 -0
- package/src/cli-bin.ts +5 -0
- package/src/cli.ts +1046 -0
- package/src/client.ts +1826 -0
- package/src/contract.ts +206 -0
- package/src/deterministic-id.ts +72 -0
- package/src/devtools-app.ts +989 -0
- package/src/devtools-server.browser.ts +5 -0
- package/src/devtools-server.ts +604 -0
- package/src/devtools.ts +716 -0
- package/src/errors.ts +50 -0
- package/src/http.ts +394 -0
- package/src/idempotent-replay.ts +53 -0
- package/src/index.ts +37 -0
- package/src/inspection.ts +39 -0
- package/src/internal.ts +426 -0
- package/src/otel.ts +59 -0
- package/src/platform.ts +60 -0
- package/src/push-envelope.ts +137 -0
- package/src/react.ts +284 -0
- package/src/reducer.ts +108 -0
- package/src/retryable-lazy.ts +27 -0
- package/src/scheduler-qstash.ts +915 -0
- package/src/scheduler-task.ts +106 -0
- package/src/scheduler-vercel.ts +437 -0
- package/src/server.browser.ts +12 -0
- package/src/server.ts +2700 -0
- package/src/session-socket.ts +548 -0
- package/src/sse.ts +141 -0
- package/src/standard-schema.ts +77 -0
- package/src/store-codec.ts +10 -0
- package/src/store-memory.ts +788 -0
- package/src/store-polling.ts +102 -0
- package/src/store-postgres.ts +1212 -0
- package/src/store-redis-core.ts +1494 -0
- package/src/store-redis-http.ts +116 -0
- package/src/store-redis.ts +458 -0
- package/src/store-sqlite.ts +1108 -0
- package/src/store.ts +385 -0
- package/src/telemetry.ts +47 -0
- package/src/testing.browser.ts +5 -0
- package/src/testing.ts +185 -0
- package/src/validate.ts +39 -0
- package/src/wire.ts +454 -0
- package/dist/internal-gCd5qMry.js +0 -44
- package/dist/log-memory.d.ts +0 -11
- package/dist/recovery-vercel.js +0 -119
- package/dist/server-BcLa4RFL.js +0 -877
- package/dist/wire-BVsgR8o9.js +0 -62
package/dist/server.d.ts
CHANGED
|
@@ -1,155 +1,4 @@
|
|
|
1
|
-
import { a as EventDefs, c as
|
|
2
|
-
import { a as Event, c as IdSource, d as
|
|
3
|
-
import { i as
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Events that arrived over the wire through `parsePushBody` — already
|
|
7
|
-
* envelope-validated, headed for schema validation inside `append`.
|
|
8
|
-
* The brand lets the documented push route hand them straight to
|
|
9
|
-
* `session.append` without weakening typed appends for app code: a
|
|
10
|
-
* hand-written `{ type: string }` literal still fails to compile.
|
|
11
|
-
*/
|
|
12
|
-
type PushedEvent = {
|
|
13
|
-
type: string;
|
|
14
|
-
payload: unknown;
|
|
15
|
-
id?: string;
|
|
16
|
-
readonly "~a2.pushed": true;
|
|
17
|
-
};
|
|
18
|
-
type PushValidationContext = {
|
|
19
|
-
sessionId: string;
|
|
20
|
-
events: readonly PushedEvent[];
|
|
21
|
-
};
|
|
22
|
-
/** What every handler receives. */
|
|
23
|
-
type HandlerContext<D extends EventDefs, K extends keyof D & string = keyof D & string> = {
|
|
24
|
-
/** The triggering event. */
|
|
25
|
-
event: ContractEvent<D, K>;
|
|
26
|
-
/** Durable, 1-based dispatch ordinal for this event. */
|
|
27
|
-
attempt: number;
|
|
28
|
-
/** This session, with handler-scoped idempotent append. */
|
|
29
|
-
session: Session<D, HandlerAppend<D>>;
|
|
30
|
-
/** Fires on `abortOn` events (cancellation slice); dormant otherwise. */
|
|
31
|
-
signal: AbortSignal;
|
|
32
|
-
};
|
|
33
|
-
type Handler<D extends EventDefs, K extends keyof D & string = keyof D & string> = (ctx: HandlerContext<D, K>) => Promise<void | AppendInput<D> | readonly AppendInput<D>[]>;
|
|
34
|
-
type LaneContext<D extends EventDefs, K extends keyof D & string = keyof D & string> = {
|
|
35
|
-
sessionId: string;
|
|
36
|
-
event: Pick<ContractEvent<D, K>, "type" | "payload"> & {
|
|
37
|
-
id?: string;
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
type Lane<D extends EventDefs, K extends keyof D & string = keyof D & string> = string | ((context: LaneContext<D, K>) => string);
|
|
41
|
-
type SessionDispatch<D extends EventDefs> = {
|
|
42
|
-
(...events: AppendInput<D>[]): Promise<ContractEvent<D>[]>;
|
|
43
|
-
/** The push-route path: events from `parsePushBody`. */
|
|
44
|
-
(...events: PushedEvent[]): Promise<ContractEvent<D>[]>;
|
|
45
|
-
};
|
|
46
|
-
type SessionAppend<D extends EventDefs> = SessionDispatch<D> & {
|
|
47
|
-
/** Commit, then hand pending work directly to configured recovery. */
|
|
48
|
-
dispatch: SessionDispatch<D>;
|
|
49
|
-
};
|
|
50
|
-
type HandlerAppend<D extends EventDefs> = (name: string, ...events: AppendInput<D>[]) => Promise<ContractEvent<D>[]>;
|
|
51
|
-
/** A handle on one instance of the machine. Creating it does no I/O. */
|
|
52
|
-
type Session<D extends EventDefs, Append = SessionAppend<D>> = {
|
|
53
|
-
readonly id: string;
|
|
54
|
-
append: Append;
|
|
55
|
-
history(): Promise<ContractEvent<D>[]>;
|
|
56
|
-
state<S>(reducer: Reducer<D, S>): Promise<{
|
|
57
|
-
state: S;
|
|
58
|
-
index: number;
|
|
59
|
-
}>;
|
|
60
|
-
/**
|
|
61
|
-
* A live feed of this session's events, starting after `startAt`
|
|
62
|
-
* (exclusive). Server-side only — expose it over SSE with
|
|
63
|
-
* `sseResponse` from experimental-a2/http.
|
|
64
|
-
*/
|
|
65
|
-
stream(opts?: {
|
|
66
|
-
startAt?: number;
|
|
67
|
-
}): AsyncIterable<ContractEvent<D>>;
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* What recovery needs from a server — structural, so servers of any
|
|
71
|
-
* contract mix in one `handler(...)` call. Deliberately tiny: recovery
|
|
72
|
-
* consults the log through `drain` and nothing else.
|
|
73
|
-
*/
|
|
74
|
-
type RecoverableServer = {
|
|
75
|
-
readonly contract: {
|
|
76
|
-
readonly name: string;
|
|
77
|
-
};
|
|
78
|
-
drain(sessionId: string): Promise<{
|
|
79
|
-
settled: boolean;
|
|
80
|
-
}>;
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* The recovery seam (a2-implementation.md §7, §9). `arm` puts a
|
|
84
|
-
* delayed, coalesced "drain this session" message on durable
|
|
85
|
-
* infrastructure while inline work starts; `handler` returns the route
|
|
86
|
-
* the transport delivers to. Claim holders move the watchdog alongside
|
|
87
|
-
* their renewable execution window. Implementations ship as
|
|
88
|
-
* entry points (`experimental-a2/recovery-vercel`); core never imports a transport.
|
|
89
|
-
*/
|
|
90
|
-
type A2Recovery = {
|
|
91
|
-
arm(opts: {
|
|
92
|
-
contract: string;
|
|
93
|
-
sessionId: string;
|
|
94
|
-
/** Earliest useful delivery as epoch milliseconds. */
|
|
95
|
-
dueAt?: number;
|
|
96
|
-
}): Promise<void>;
|
|
97
|
-
handler(...servers: RecoverableServer[]): (req: Request) => Promise<Response>;
|
|
98
|
-
};
|
|
99
|
-
type A2Server<D extends EventDefs> = {
|
|
100
|
-
/** The contract this server implements. */
|
|
101
|
-
readonly contract: Contract<D>;
|
|
102
|
-
session(id: string): Session<D>;
|
|
103
|
-
/**
|
|
104
|
-
* Process every currently eligible event. `settled` means nothing
|
|
105
|
-
* actionable remains, including work blocked behind a dead letter.
|
|
106
|
-
*/
|
|
107
|
-
drain(sessionId: string): Promise<{
|
|
108
|
-
settled: boolean;
|
|
109
|
-
}>;
|
|
110
|
-
};
|
|
111
|
-
/**
|
|
112
|
-
* Which events fire `ctx.signal` while a handler runs — the preemption
|
|
113
|
-
* channel for user cancellation. The array form matches by type; the
|
|
114
|
-
* object form takes per-type predicates for targeted cancellation
|
|
115
|
-
* (`(event, trigger) => event.payload.of === trigger.id`). Handlers
|
|
116
|
-
* without `abortOn` pay nothing. An aborted handler should catch and
|
|
117
|
-
* return normally; throwing means "retry me".
|
|
118
|
-
*/
|
|
119
|
-
type AbortSpec<D extends EventDefs, K extends keyof D & string> = Array<keyof D & string> | { [T in keyof D & string]?: true | ((event: ContractEvent<D, T>, trigger: ContractEvent<D, K>, context: {
|
|
120
|
-
attempt: number;
|
|
121
|
-
}) => boolean); };
|
|
122
|
-
type HandlerEntry<D extends EventDefs, K extends keyof D & string = keyof D & string> = Handler<D, K> | {
|
|
123
|
-
abortOn?: AbortSpec<D, K>;
|
|
124
|
-
/** Session-scoped FIFO key, resolved and persisted when the event lands. */
|
|
125
|
-
lane?: Lane<D, K>;
|
|
126
|
-
handler: Handler<D, K>;
|
|
127
|
-
};
|
|
128
|
-
type ServerOptions<D extends EventDefs> = {
|
|
129
|
-
/** The contract this server implements (see `a2.contract`). */
|
|
130
|
-
contract: Contract<D>;
|
|
131
|
-
/** Where events live. Defaults: sqlite in dev, memory in tests, required in prod. */
|
|
132
|
-
log?: A2Log;
|
|
133
|
-
/**
|
|
134
|
-
* Queue-backed recovery — e.g. `vercelQueues()` from
|
|
135
|
-
* `experimental-a2/recovery-vercel`. Absent means append-driven healing only: a
|
|
136
|
-
* working configuration, but a clockless one. Recommended in
|
|
137
|
-
* production.
|
|
138
|
-
*/
|
|
139
|
-
recovery?: A2Recovery;
|
|
140
|
-
/** Optional instrumentation — e.g. `otel()` from `experimental-a2/otel`. */
|
|
141
|
-
telemetry?: A2Telemetry;
|
|
142
|
-
/** Validate events that came through `parsePushBody` before writing them. */
|
|
143
|
-
validatePush?: (context: PushValidationContext) => void | PromiseLike<void>;
|
|
144
|
-
/**
|
|
145
|
-
* The reactions, keyed by event type — all present at construction,
|
|
146
|
-
* so a handler can never be silently missing because its module
|
|
147
|
-
* wasn't imported. Compose across files by spreading objects (note:
|
|
148
|
-
* a duplicate key under spread silently last-wins).
|
|
149
|
-
*/
|
|
150
|
-
handlers?: { [K in keyof D & string]?: HandlerEntry<D, K>; };
|
|
151
|
-
};
|
|
152
|
-
/** Implement a contract: bind its vocabulary to storage and reactions. */
|
|
153
|
-
declare function createServer<D extends EventDefs>(options: ServerOptions<D>): A2Server<D>;
|
|
154
|
-
//#endregion
|
|
155
|
-
export { type A2Log, type A2LogInspection, A2Recovery, A2Server, AbortSpec, type AppendEvent, type AppendInput, type Clock, type Contract, type ContractEvent, type Event, type EventCause, type EventDefs, type FailAttemptResult, Handler, HandlerAppend, HandlerContext, HandlerEntry, type IdSource, Lane, LaneContext, type LogAppendResult, type LogClaimAvailableResult, type LogStateRead, PushValidationContext, PushedEvent, RecoverableServer, ServerOptions, Session, SessionAppend, SessionDispatch, type StoredEvent, type StoredSessionPage, type StoredSessionSummary, type StoredSnapshot, createServer };
|
|
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 Event, c as IdSource, d as StoreClaimAvailableResult, f as StoreStateRead, g as StoredSnapshot, h as StoredSessionSummary, i as Clock, l as PresenceRow, m as StoredSessionPage, n as A2StoreInspection, o as EventCause, p as StoredEvent, r as AppendEvent, s as FailAttemptResult, t as A2Store, u as StoreAppendResult } from "./store-DysUkTH3.js";
|
|
3
|
+
import { C as deliverSchedulerAppend, D as SchedulerTask, E as SchedulerDrainTask, S as createServer, T as SchedulerAppendTask, _ as Session, a as Handler, b as SessionPresence, c as HandlerEntry, d as PushValidationContext, f as PushedEvent, g as ServerOptions, h as ScheduleTiming, i as DrainableServer, l as Lane, m as ScheduleDelay, n as A2Server, o as HandlerAppend, p as PushedPresence, r as AbortSpec, s as HandlerContext, t as A2Scheduler, u as LaneContext, v as SessionAppend, w as ScheduledEvent, x as SessionSchedule, y as SessionDispatch } from "./server-DUF9pjsx.js";
|
|
4
|
+
export { A2Scheduler, A2Server, type A2Store, type A2StoreInspection, 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, PushValidationContext, PushedEvent, PushedPresence, ScheduleDelay, ScheduleTiming, type ScheduledEvent, type SchedulerAppendTask, type SchedulerDrainTask, type SchedulerTask, ServerOptions, Session, SessionAppend, SessionDispatch, SessionPresence, SessionSchedule, type StoreAppendResult, type StoreClaimAvailableResult, type StoreStateRead, type StoredEvent, type StoredSessionPage, type StoredSessionSummary, type StoredSnapshot, createServer, deliverSchedulerAppend };
|
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createServer } from "./server-
|
|
2
|
-
export { createServer };
|
|
1
|
+
import { n as deliverSchedulerAppend, t as createServer } from "./server-C72KOw51.js";
|
|
2
|
+
export { createServer, deliverSchedulerAppend };
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* The A2Log interface — the storage contract every log backend
|
|
4
|
-
* implements. See specs/a2-implementation.md §2–3.
|
|
5
|
-
*
|
|
6
|
-
* This is the whole storage contract: append, read, dispatch claims,
|
|
7
|
-
* failure markers, snapshots, and the live stream. Recovery
|
|
8
|
-
* needs nothing extra — the armed queue message is its own state, and
|
|
9
|
-
* the log is the only thing it consults.
|
|
10
|
-
*/
|
|
1
|
+
import { c as PresencePatch } from "./contract-jIfaR085.js";
|
|
2
|
+
//#region src/store.d.ts
|
|
11
3
|
/** A stored event, as the public API exposes it. */
|
|
12
4
|
type Event = {
|
|
13
5
|
id: string;
|
|
14
6
|
type: string;
|
|
15
7
|
payload: unknown;
|
|
16
|
-
/** Position in the session's log, from 1. */
|
|
8
|
+
/** Position in the session's event log, from 1. */
|
|
17
9
|
index: number;
|
|
18
10
|
sessionId: string;
|
|
19
11
|
createdAt: Date;
|
|
@@ -26,7 +18,7 @@ type EventCause = {
|
|
|
26
18
|
batchSize?: number;
|
|
27
19
|
};
|
|
28
20
|
/**
|
|
29
|
-
* What the
|
|
21
|
+
* What the store persists: immutable event history, including its causal edge,
|
|
30
22
|
* plus derived dispatch and failure bookkeeping. The bookkeeping is disposable;
|
|
31
23
|
* the event and its cause are not.
|
|
32
24
|
*/
|
|
@@ -75,6 +67,21 @@ type StoredSessionSummary = {
|
|
|
75
67
|
firstEventAt: Date;
|
|
76
68
|
updatedAt: Date;
|
|
77
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* One live presence value, as `A2Store.presence.read` returns it. The
|
|
72
|
+
* row is the LWW unit — one participant's one field. `at` is the
|
|
73
|
+
* sender's stamp, the LWW comparator. `expiresAt` is the storage's
|
|
74
|
+
* own clock at the applied set plus its `ttlMs` — never derived from
|
|
75
|
+
* the sender stamp; a row is live strictly before it.
|
|
76
|
+
*/
|
|
77
|
+
type PresenceRow = {
|
|
78
|
+
participant: string;
|
|
79
|
+
field: string;
|
|
80
|
+
value: unknown;
|
|
81
|
+
seen: number;
|
|
82
|
+
at: Date;
|
|
83
|
+
expiresAt: Date;
|
|
84
|
+
};
|
|
78
85
|
/** Metadata for the latest cached fold of one reducer. */
|
|
79
86
|
type StoredSnapshot = {
|
|
80
87
|
reducerName: string;
|
|
@@ -82,7 +89,7 @@ type StoredSnapshot = {
|
|
|
82
89
|
updatedAt: Date;
|
|
83
90
|
};
|
|
84
91
|
/** One consistent cache-plus-tail read for a reducer fold. */
|
|
85
|
-
type
|
|
92
|
+
type StoreStateRead = {
|
|
86
93
|
/** The latest cached fold for this reducer, if one exists. */
|
|
87
94
|
snapshot: {
|
|
88
95
|
index: number;
|
|
@@ -96,22 +103,37 @@ type StoredSessionPage = {
|
|
|
96
103
|
/** Opaque backend cursor; null means there is no next page. */
|
|
97
104
|
cursor: string | null;
|
|
98
105
|
};
|
|
106
|
+
type StoredEventPage = {
|
|
107
|
+
events: StoredEvent[];
|
|
108
|
+
/** Fixed inclusive log frontier shared by every page in one inspection. */
|
|
109
|
+
throughIndex: number;
|
|
110
|
+
};
|
|
99
111
|
/**
|
|
100
|
-
* Optional read-only administration implemented by A2's shipped
|
|
101
|
-
* It is separate from the correctness contract so custom
|
|
112
|
+
* Optional read-only administration implemented by A2's shipped stores.
|
|
113
|
+
* It is separate from the correctness contract so custom stores do not
|
|
102
114
|
* need to expose storage-wide discovery merely to process events.
|
|
103
115
|
*/
|
|
104
|
-
type
|
|
116
|
+
type A2StoreInspection = {
|
|
105
117
|
listSessions(options: {
|
|
106
118
|
/** Storage namespace prefix, including the contract separator. */
|
|
107
119
|
prefix: string;
|
|
108
120
|
cursor?: string;
|
|
109
121
|
limit: number;
|
|
110
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>;
|
|
111
133
|
listSnapshots(sessionId: string): Promise<StoredSnapshot[]>;
|
|
112
134
|
};
|
|
113
135
|
/** The result of atomically claiming every currently eligible event. */
|
|
114
|
-
type
|
|
136
|
+
type StoreClaimAvailableResult = {
|
|
115
137
|
outcome: "claimed";
|
|
116
138
|
events: StoredEvent[];
|
|
117
139
|
} | {
|
|
@@ -127,12 +149,19 @@ type CompleteAttemptResult = {
|
|
|
127
149
|
} | {
|
|
128
150
|
outcome: "superseded";
|
|
129
151
|
};
|
|
152
|
+
/** The result of one claim-renewal heartbeat, both lists in append order. */
|
|
153
|
+
type RenewClaimsResult = {
|
|
154
|
+
/** Listed claims that remain owned by the holder. */
|
|
155
|
+
renewed: number[];
|
|
156
|
+
/** Listed claims whose event a newer attempt has durably taken. */
|
|
157
|
+
superseded: number[];
|
|
158
|
+
};
|
|
130
159
|
/** The result of atomically recording a caught handler failure. */
|
|
131
160
|
type FailAttemptResult = {
|
|
132
161
|
outcome: "failed" | "dead_lettered" | "superseded";
|
|
133
162
|
failureCount: number;
|
|
134
163
|
};
|
|
135
|
-
/** Input to `
|
|
164
|
+
/** Input to `A2Store.append` — already validated by the machine. */
|
|
136
165
|
type AppendEvent = {
|
|
137
166
|
type: string;
|
|
138
167
|
payload: unknown;
|
|
@@ -145,12 +174,12 @@ type AppendEvent = {
|
|
|
145
174
|
/** Internal hint: settle this event in the append transaction; no handler is registered. */
|
|
146
175
|
settled?: true;
|
|
147
176
|
};
|
|
148
|
-
/** A handler-returned event with the deterministic id
|
|
177
|
+
/** A handler-returned event with the deterministic id retries require. */
|
|
149
178
|
type ReturnedEvent = AppendEvent & {
|
|
150
179
|
id: string;
|
|
151
180
|
};
|
|
152
181
|
/** The rows written by an append and the session's pending state. */
|
|
153
|
-
type
|
|
182
|
+
type StoreAppendResult = {
|
|
154
183
|
events: StoredEvent[];
|
|
155
184
|
/** Whether the session contains an event without a completion marker. */
|
|
156
185
|
hasPending: boolean;
|
|
@@ -165,7 +194,7 @@ type Clock = {
|
|
|
165
194
|
};
|
|
166
195
|
/** An injectable id source for generated event ids. */
|
|
167
196
|
type IdSource = () => string;
|
|
168
|
-
interface
|
|
197
|
+
interface A2Store {
|
|
169
198
|
/**
|
|
170
199
|
* Accepts a batch; the batch is atomic — one transaction, consecutive
|
|
171
200
|
* `index`es, all-or-nothing. The idempotency key covers the whole
|
|
@@ -178,17 +207,27 @@ interface A2Log {
|
|
|
178
207
|
* operation, with no dispatch claim or `processedByAttempt`. The result's
|
|
179
208
|
* `hasPending` reflects the whole session in the same atomic operation,
|
|
180
209
|
* including older events and idempotent retries.
|
|
210
|
+
*
|
|
211
|
+
* An event carrying `cause` is a handler append, fenced by attempt
|
|
212
|
+
* currency: accept it only while `cause.attempt` is still the parent
|
|
213
|
+
* event's latest attempt and the parent is not dead-lettered; otherwise
|
|
214
|
+
* throw `A2Error('SUPERSEDED_ATTEMPT')` and write nothing. The check is
|
|
215
|
+
* part of this atomic operation and must serialize against a concurrent
|
|
216
|
+
* `claimAvailable` — an unlocked read of the parent admits write skew.
|
|
217
|
+
* The idempotent-replay path runs first, so a batch whose ids all exist
|
|
218
|
+
* replays regardless of the current attempt.
|
|
181
219
|
*/
|
|
182
|
-
append(sessionId: string, events: AppendEvent[]): Promise<
|
|
183
|
-
/** Events for one session, oldest first. */
|
|
220
|
+
append(sessionId: string, events: AppendEvent[]): Promise<StoreAppendResult>;
|
|
221
|
+
/** Events for one session, oldest first. Bounds form `(afterIndex, throughIndex]`. */
|
|
184
222
|
read(sessionId: string, opts?: {
|
|
185
223
|
afterIndex?: number;
|
|
224
|
+
throughIndex?: number;
|
|
186
225
|
}): Promise<StoredEvent[]>;
|
|
187
226
|
/**
|
|
188
227
|
* Atomically claims every eligible pending event. Unlaned events are all
|
|
189
228
|
* independently eligible. Within a lane, only the lowest-index unfinished
|
|
190
229
|
* event is eligible. A live claim produces `busy` only when it is the sole
|
|
191
|
-
* remaining obstacle to actionable work. Claimed events are returned in log
|
|
230
|
+
* remaining obstacle to actionable work. Claimed events are returned in event-log
|
|
192
231
|
* order. Excluded rows remain lane barriers.
|
|
193
232
|
*/
|
|
194
233
|
claimAvailable(options: {
|
|
@@ -197,19 +236,26 @@ interface A2Log {
|
|
|
197
236
|
ttlMs: number;
|
|
198
237
|
expiresAtMs?: number;
|
|
199
238
|
excludeIndexes?: readonly number[];
|
|
200
|
-
}): Promise<
|
|
239
|
+
}): Promise<StoreClaimAvailableResult>;
|
|
201
240
|
/**
|
|
202
|
-
* Renews the listed live claims still owned by `holder`.
|
|
203
|
-
*
|
|
204
|
-
*
|
|
241
|
+
* Renews the listed live claims still owned by `holder`. Each listed claim
|
|
242
|
+
* carries the attempt ordinal the holder owns. `renewed` lists the claims
|
|
243
|
+
* that remain owned after the operation; `superseded` lists the claims
|
|
244
|
+
* whose event's `attemptCount` has durably passed the listed attempt. An
|
|
245
|
+
* expired claim no successor has taken appears in neither list — its
|
|
246
|
+
* attempt may still complete (`completeAttempt` is the fence), so it is
|
|
247
|
+
* not reported as lost. Renewal never revives an expired claim.
|
|
205
248
|
*/
|
|
206
249
|
renewClaims(options: {
|
|
207
250
|
sessionId: string;
|
|
208
251
|
holder: string;
|
|
209
|
-
|
|
252
|
+
claims: readonly {
|
|
253
|
+
index: number;
|
|
254
|
+
attempt: number;
|
|
255
|
+
}[];
|
|
210
256
|
ttlMs: number;
|
|
211
257
|
expiresAtMs?: number;
|
|
212
|
-
}): Promise<
|
|
258
|
+
}): Promise<RenewClaimsResult>;
|
|
213
259
|
/**
|
|
214
260
|
* Atomically completes one current attempt and appends its returned events.
|
|
215
261
|
* Retrying a committed completion with the same attempt and deterministic
|
|
@@ -239,25 +285,64 @@ interface A2Log {
|
|
|
239
285
|
* log. The snapshot is untrusted; core may reject it and issue a full
|
|
240
286
|
* `read()` when its state schema no longer accepts the cached value.
|
|
241
287
|
*/
|
|
242
|
-
readState(sessionId: string, reducerName: string): Promise<
|
|
288
|
+
readState(sessionId: string, reducerName: string): Promise<StoreStateRead>;
|
|
289
|
+
/**
|
|
290
|
+
* Optional batched `readState` — one consistent snapshot-plus-tail read
|
|
291
|
+
* per session id, aligned positionally with the input (duplicates
|
|
292
|
+
* allowed). Each element has its own frontier; the batch makes no
|
|
293
|
+
* cross-session consistency claim. Core falls back to parallel
|
|
294
|
+
* `readState` calls when absent.
|
|
295
|
+
*/
|
|
296
|
+
readStates?(sessionIds: string[], reducerName: string): Promise<StoreStateRead[]>;
|
|
243
297
|
/**
|
|
244
298
|
* Writes a disposable reducer cache. Guard this operation so a slower
|
|
245
299
|
* concurrent writer can never clobber a further-along snapshot
|
|
246
300
|
* (`where up_to_index < excluded.up_to_index`).
|
|
247
301
|
*/
|
|
248
302
|
putSnapshot(sessionId: string, reducerName: string, index: number, state: unknown): Promise<void>;
|
|
303
|
+
/**
|
|
304
|
+
* Optional ephemeral-plane capability (specs/a2-implementation.md
|
|
305
|
+
* §15.1) — optional like snapshots are. Values arrive already
|
|
306
|
+
* validated by core; adapters store them opaquely. Presence never
|
|
307
|
+
* touches the event log: no index, no history row, no recovery arm.
|
|
308
|
+
*/
|
|
309
|
+
presence?: {
|
|
310
|
+
/**
|
|
311
|
+
* Field-wise last-writer-wins merge of one participant's values:
|
|
312
|
+
* a field whose existing row has a strictly newer `at` (the
|
|
313
|
+
* sender's stamp) is left untouched; `null` deletes the row. Every
|
|
314
|
+
* applied write refreshes that field's expiry to the storage's own
|
|
315
|
+
* clock plus `ttlMs` — the sender stamp orders writes but never
|
|
316
|
+
* anchors their lifetime.
|
|
317
|
+
*/
|
|
318
|
+
set(ns: string, participant: string, values: Record<string, unknown | null>, meta: {
|
|
319
|
+
seen: number;
|
|
320
|
+
at: Date;
|
|
321
|
+
ttlMs: number;
|
|
322
|
+
}): Promise<void>;
|
|
323
|
+
/** The current map, pruned of rows at or past their `expiresAt`. */
|
|
324
|
+
read(ns: string): Promise<PresenceRow[]>;
|
|
325
|
+
/**
|
|
326
|
+
* Push-tier patch delivery; present only on backends with a real
|
|
327
|
+
* broadcast primitive. Without it the backend is the degraded
|
|
328
|
+
* tier: live feeds surface presence by re-reading on their
|
|
329
|
+
* existing poll cadence.
|
|
330
|
+
*/
|
|
331
|
+
subscribe?(ns: string, onPatch: (patch: PresencePatch) => void): () => void;
|
|
332
|
+
};
|
|
249
333
|
/** Read-only operational data used by `experimental-a2/devtools/server`. */
|
|
250
|
-
inspect?:
|
|
334
|
+
inspect?: A2StoreInspection;
|
|
251
335
|
/**
|
|
252
|
-
* A live feed of one session's events, starting after `
|
|
336
|
+
* A live feed of one session's events, starting after `startAfter`
|
|
253
337
|
* (exclusive). Transport is the backend's choice — in-process pub/sub,
|
|
254
338
|
* polling, LISTEN/NOTIFY — callers never branch on which. The iterable
|
|
255
339
|
* ends when the consumer calls `return()` (e.g. a disconnecting SSE
|
|
256
340
|
* client) and must deliver events appended after subscription.
|
|
257
341
|
*/
|
|
258
342
|
stream(sessionId: string, opts?: {
|
|
259
|
-
|
|
343
|
+
startAfter?: number;
|
|
260
344
|
}): AsyncIterable<Event>;
|
|
261
345
|
}
|
|
262
346
|
//#endregion
|
|
263
|
-
export { Event as a, IdSource as c,
|
|
347
|
+
export { Event as a, IdSource as c, StoreClaimAvailableResult as d, StoreStateRead as f, StoredSnapshot as g, StoredSessionSummary as h, Clock as i, PresenceRow as l, StoredSessionPage as m, A2StoreInspection as n, EventCause as o, StoredEvent as p, AppendEvent as r, FailAttemptResult as s, A2Store as t, StoreAppendResult as u };
|
|
348
|
+
//# sourceMappingURL=store-DysUkTH3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-DysUkTH3.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;;;KAIA;EACV;EACA;EACA;EACA;;EAEA;;EAEA;EACA,cAAc;EACd,WAAW;;;;;;;;;KAUD;EACV;EACA;EACA;EACA;EACA,IAAI;EACJ,WAAW;;;KAID;EACV;EACA;EACA,WAAW;;;KAID;;EAEV;IAAY;IAAe;;;EAE3B,QAAQ;;KAGE;EACV,UAAU;;EAEV;;KAGU;EACV,QAAQ;;EAER;;;;;;;KAQU;EACV,aAAa;;IAEX;IACA;IACA;MACE,QAAQ;;;;;;EAMZ,YACE,mBACA;IAAW;IAAoB;IAAuB;MACrD,QAAQ;EACX,cAAc,oBAAoB,QAAQ;;;KAIhC;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,UAAU,mBAAmB,sBAAsB,QAAQ;;;;;;;;EAS3D,YACE,sBACA,sBACC,QAAQ;;;;;;EAOX,YACE,mBACA,qBACA,eACA,iBACC;;;;;;;EAQH;;;;;;;;;IASE,IACE,YACA,qBACA,QAAQ,gCACR;MAAQ;MAAc,IAAI;MAAM;QAC/B;;IAGH,KAAK,aAAa,QAAQ;;;;;;;IAQ1B,WAAW,YAAY,UAAU,OAAO;;;EAI1C,UAAU;;;;;;;;EASV,OACE,mBACA;IAAS;MACR,cAAc"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/store.ts
|
|
2
2
|
const SYSTEM_CLOCK = { now: () => /* @__PURE__ */ new Date() };
|
|
3
3
|
const RANDOM_IDS = () => crypto.randomUUID();
|
|
4
4
|
//#endregion
|
|
5
5
|
export { SYSTEM_CLOCK as n, RANDOM_IDS as t };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=store-N8PXxDAS.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/store-codec.ts
|
|
2
|
+
const decodeReturnedEventIds = (value) => {
|
|
3
|
+
const decoded = typeof value === "string" ? JSON.parse(value) : value;
|
|
4
|
+
if (!Array.isArray(decoded) || decoded.some((eventId) => typeof eventId !== "string")) throw new TypeError("stored event has invalid returned event ids");
|
|
5
|
+
return decoded;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { decodeReturnedEventIds as t };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=store-codec-DTG0Ftek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-codec-DTG0Ftek.js","names":[],"sources":["../src/store-codec.ts"],"sourcesContent":["export const decodeReturnedEventIds = (value: unknown): string[] => {\n const decoded: unknown = typeof value === 'string' ? JSON.parse(value) : value\n if (\n !Array.isArray(decoded) ||\n decoded.some((eventId) => typeof eventId !== 'string')\n ) {\n throw new TypeError('stored event has invalid returned event ids')\n }\n return decoded\n}\n"],"mappings":";AAAA,MAAa,0BAA0B,UAA6B;CAClE,MAAM,UAAmB,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;CACzE,IACE,CAAC,MAAM,QAAQ,OAAO,KACtB,QAAQ,MAAM,YAAY,OAAO,YAAY,QAAQ,GAErD,MAAM,IAAI,UAAU,6CAA6C;CAEnE,OAAO;AACT"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { c as IdSource, i as Clock, t as A2Store } from "./store-DysUkTH3.js";
|
|
2
|
+
//#region src/store-memory.d.ts
|
|
3
|
+
type MemoryStoreOptions = {
|
|
4
|
+
/** Injectable clock — every stored timestamp comes from here. */
|
|
5
|
+
clock?: Clock;
|
|
6
|
+
/** Injectable id source for generated event ids. */
|
|
7
|
+
ids?: IdSource;
|
|
8
|
+
};
|
|
9
|
+
declare function memory(options?: MemoryStoreOptions): A2Store;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { MemoryStoreOptions, memory };
|
|
12
|
+
//# sourceMappingURL=store-memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-memory.d.ts","names":[],"sources":["../src/store-memory.ts"],"mappings":";;KA2BY;;EAEV,QAAQ;;EAER,MAAM;;iBAyEQ,OAAO,UAAS,qBAA0B"}
|