experimental-a2 0.4.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 +51 -0
- package/dist/{ai-B4YhEnfw.d.ts → ai-CFNeCrRl.d.ts} +3 -2
- 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 +4 -3
- package/dist/ai-server.d.ts.map +1 -0
- package/dist/ai-server.js +4 -2
- package/dist/ai-server.js.map +1 -0
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +3 -1
- package/dist/ai.js.map +1 -0
- package/dist/cli-B3VuxoDe.js +2 -0
- package/dist/cli-B3VuxoDe.js.map +1 -0
- package/dist/cli-bin.js +2 -0
- package/dist/cli-bin.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -0
- package/dist/{client-Bt4tAKi9.js → client-BKlyLiOU.js} +295 -85
- package/dist/client-BKlyLiOU.js.map +1 -0
- package/dist/{client-BrfDXQ8A.d.ts → client-D7mvIXrF.d.ts} +40 -4
- package/dist/client-D7mvIXrF.d.ts.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/contract-48bUMgcL.js +2 -0
- package/dist/contract-48bUMgcL.js.map +1 -0
- package/dist/contract-jIfaR085.d.ts +2 -1
- package/dist/contract-jIfaR085.d.ts.map +1 -0
- package/dist/devtools-J_jZ2vQf.d.ts +2 -1
- package/dist/devtools-J_jZ2vQf.d.ts.map +1 -0
- package/dist/devtools-kJJaORn-.js +2 -0
- package/dist/devtools-kJJaORn-.js.map +1 -0
- package/dist/devtools-server.browser.js +2 -0
- package/dist/devtools-server.browser.js.map +1 -0
- package/dist/devtools-server.d.ts +2 -1
- package/dist/devtools-server.d.ts.map +1 -0
- package/dist/devtools-server.js +2 -0
- package/dist/devtools-server.js.map +1 -0
- package/dist/errors-BQuJpe82.js +2 -0
- package/dist/errors-BQuJpe82.js.map +1 -0
- package/dist/errors-W6nwJ-fm.d.ts +2 -1
- package/dist/errors-W6nwJ-fm.d.ts.map +1 -0
- package/dist/http.d.ts +121 -72
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +503 -178
- package/dist/http.js.map +1 -0
- package/dist/idempotent-replay-DuqEkYA7.js +2 -0
- package/dist/idempotent-replay-DuqEkYA7.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/inspection-DaxB5jM2.js +2 -0
- package/dist/inspection-DaxB5jM2.js.map +1 -0
- package/dist/{internal-aEotMzu_.js → internal-DstsI6Re.js} +3 -1
- package/dist/internal-DstsI6Re.js.map +1 -0
- package/dist/otel.d.ts +2 -1
- 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 +2 -0
- package/dist/platform-B4TnJtWu.js.map +1 -0
- package/dist/react.d.ts +12 -3
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +5 -1
- 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 +4 -3
- package/dist/scheduler-qstash.d.ts.map +1 -0
- package/dist/scheduler-qstash.js +4 -2
- package/dist/scheduler-qstash.js.map +1 -0
- package/dist/scheduler-task-BpzhPnRS.js +2 -0
- package/dist/scheduler-task-BpzhPnRS.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -3
- package/dist/scheduler-vercel.d.ts.map +1 -0
- package/dist/scheduler-vercel.js +4 -2
- package/dist/scheduler-vercel.js.map +1 -0
- package/dist/{server-CcNnFnoW.js → server-C72KOw51.js} +100 -48
- package/dist/server-C72KOw51.js.map +1 -0
- package/dist/{server-YtPq7hjw.d.ts → server-DUF9pjsx.d.ts} +5 -4
- 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 +2 -2
- package/dist/server.js +1 -1
- package/dist/{store-C3sNAaBT.d.ts → store-DysUkTH3.d.ts} +10 -1
- package/dist/store-DysUkTH3.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js +2 -0
- package/dist/store-N8PXxDAS.js.map +1 -0
- package/dist/store-codec-DTG0Ftek.js +2 -0
- package/dist/store-codec-DTG0Ftek.js.map +1 -0
- package/dist/store-memory.d.ts +3 -2
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/store-memory.js +19 -11
- package/dist/store-memory.js.map +1 -0
- package/dist/{store-polling-DgrrAE3d.js → store-polling-dSeLxzfb.js} +3 -1
- package/dist/store-polling-dSeLxzfb.js.map +1 -0
- package/dist/store-postgres.d.ts +3 -2
- package/dist/store-postgres.d.ts.map +1 -0
- package/dist/store-postgres.js +57 -1
- package/dist/store-postgres.js.map +1 -0
- package/dist/{store-redis-core-DWqx3F47.js → store-redis-core-BFLwz0Wj.js} +3 -1
- package/dist/store-redis-core-BFLwz0Wj.js.map +1 -0
- package/dist/store-redis-http.d.ts +3 -2
- package/dist/store-redis-http.d.ts.map +1 -0
- package/dist/store-redis-http.js +4 -2
- package/dist/store-redis-http.js.map +1 -0
- package/dist/store-redis.d.ts +3 -2
- package/dist/store-redis.d.ts.map +1 -0
- package/dist/store-redis.js +5 -3
- package/dist/store-redis.js.map +1 -0
- package/dist/store-sqlite.d.ts +3 -2
- package/dist/store-sqlite.d.ts.map +1 -0
- package/dist/store-sqlite.js +3 -1
- package/dist/store-sqlite.js.map +1 -0
- package/dist/telemetry-BjYHTfh2.d.ts +2 -1
- package/dist/telemetry-BjYHTfh2.d.ts.map +1 -0
- package/dist/testing.browser.js +2 -0
- package/dist/testing.browser.js.map +1 -0
- package/dist/testing.d.ts +2 -1
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +2 -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-DCUZBUlT.js → wire-BFQmSJ-9.js} +77 -15
- package/dist/wire-BFQmSJ-9.js.map +1 -0
- package/docs/guides/03-react.mdx +59 -39
- package/docs/guides/06-ai-agents.mdx +5 -27
- package/docs/guides/09-presence.mdx +19 -40
- package/docs/guides/10-transports.mdx +49 -40
- package/docs/reference/01-api.mdx +107 -26
- package/docs/reference/02-errors.mdx +4 -2
- package/package.json +2 -1
- 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/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","names":[],"sources":["../src/sse.ts","../src/push-envelope.ts","../src/session-socket.ts","../src/http.ts"],"sourcesContent":["/**\n * The SSE response protocol — framing, heartbeat, deadline, and\n * teardown for the live stream lane. Internal module: `handle`'s\n * stream branch is the one sanctioned caller; the framing rules stay\n * documented on `sseResponse` because the client's parser (and the\n * stall watchdog) depend on them.\n */\n\nimport { STREAM_TIMINGS } from './internal.ts'\nimport type { Event } from './store.ts'\nimport type { PresencePatch, PresenceSnapshot } from './contract.ts'\nimport { invocationDeadlineMs } from './platform.ts'\nimport {\n eventToWire,\n presencePatchToWire,\n presenceSnapshotToWire,\n} from './wire.ts'\n\n/**\n * Pipe a live event iterable into an SSE `Response`. Each event is one\n * frame — `id:` carries the event-log index, `data:` the JSON event. A\n * `stream({ presence: true })` iterable also yields presence items,\n * which ride as named frames old clients skip: `event:\n * presence-snapshot` first (the map, ISO dates), then `event: presence`\n * per patch — no `id:`, presence never advances the resume frontier. A\n * disconnecting client cancels the stream, which closes the underlying\n * subscription. Two kinds of comment frames ride along: a `: connected`\n * prelude that flushes headers immediately, and a `: ping` heartbeat\n * every 15 seconds so clients (and proxies) can tell a quiet stream\n * from a dead connection — the session client's stall watchdog counts\n * on it. When the platform exposes an invocation deadline, the response\n * closes cleanly one second before it so the client reconnects without\n * a platform-timeout failure.\n */\nexport function sseResponse(\n iterable: AsyncIterable<Event | PresencePatch | PresenceSnapshot>,\n): Response {\n const iterator = iterable[Symbol.asyncIterator]()\n const encoder = new TextEncoder()\n let heartbeat: ReturnType<typeof setInterval> | undefined\n let deadlineTimer: ReturnType<typeof setTimeout> | undefined\n let closed = false\n let iteratorReturned = false\n const stopTimers = (): void => {\n if (heartbeat !== undefined) clearInterval(heartbeat)\n if (deadlineTimer !== undefined) clearTimeout(deadlineTimer)\n heartbeat = undefined\n deadlineTimer = undefined\n }\n const returnIterator = async (): Promise<void> => {\n if (iteratorReturned) return\n iteratorReturned = true\n await iterator.return?.()\n }\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n // Prelude comment frame: flushes headers immediately (a fresh\n // session may otherwise send nothing for a long time — some\n // servers buffer headers until the first byte, and clients can't\n // report \"live\" until they see the response), and gives proxies\n // early proof this is a stream. Comment frames are invisible to\n // SSE consumers.\n controller.enqueue(encoder.encode(': connected\\n\\n'))\n // Heartbeat: comment frames are ignored by SSE parsers but are\n // bytes on the wire — liveness proof for the client's watchdog\n // and for idle-timeout-happy intermediaries.\n heartbeat = setInterval(() => {\n try {\n controller.enqueue(encoder.encode(': ping\\n\\n'))\n } catch {\n // The stream closed underneath the timer.\n stopTimers()\n }\n }, STREAM_TIMINGS.sseHeartbeatMs)\n ;(heartbeat as { unref?: () => void }).unref?.()\n\n const invocationDeadline = invocationDeadlineMs()\n if (invocationDeadline !== null) {\n const delay = Math.max(\n 0,\n invocationDeadline - Date.now() - STREAM_TIMINGS.sseDeadlineGraceMs,\n )\n deadlineTimer = setTimeout(() => {\n if (closed) return\n closed = true\n stopTimers()\n void returnIterator().catch(() => {})\n try {\n controller.close()\n } catch {\n // The response already closed underneath the timer.\n }\n }, delay)\n ;(deadlineTimer as { unref?: () => void }).unref?.()\n }\n },\n async pull(controller) {\n try {\n const { value, done } = await iterator.next()\n if (closed) return\n if (done) {\n closed = true\n stopTimers()\n controller.close()\n return\n }\n controller.enqueue(encoder.encode(frameFor(value)))\n } catch (error) {\n if (closed) return\n closed = true\n stopTimers()\n void returnIterator().catch(() => {})\n throw error\n }\n },\n async cancel() {\n if (closed) return\n closed = true\n stopTimers()\n await returnIterator()\n },\n })\n return new Response(stream, {\n status: 200,\n headers: {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n },\n })\n}\n\nfunction frameFor(item: Event | PresencePatch | PresenceSnapshot): string {\n if ('snapshot' in item) {\n return `event: presence-snapshot\\ndata: ${JSON.stringify(presenceSnapshotToWire(item))}\\n\\n`\n }\n if ('participant' in item) {\n return `event: presence\\ndata: ${JSON.stringify(presencePatchToWire(item))}\\n\\n`\n }\n return `id: ${item.index}\\ndata: ${JSON.stringify(eventToWire(item))}\\n\\n`\n}\n","/**\n * The push envelope's trust boundary — parsing and provenance-branding\n * for `{ sessionId, events, presence? }`, shared verbatim by the HTTP\n * POST lane and the socket's push/presence frames so INVALID_PAYLOAD\n * shapes are one implementation. Internal module — `handle` (and the\n * socket handler) compose it; it is not part of the public surface.\n */\n\nimport { A2Error } from './errors.ts'\nimport { MAX_DATE_MS, RESERVED_PARTICIPANT_IDS } from './internal.ts'\nimport type { PushedEvent, PushedPresence } from './server.ts'\n\nexport const invalidPushBody = (message: string): A2Error =>\n new A2Error('INVALID_PAYLOAD', `malformed push body: ${message}`)\n\nconst invalid = invalidPushBody\n\nexport type PushBody = {\n sessionId: string\n /** Branded: `session.append` accepts these directly (the push route\n * path); schema validation still happens inside `append`. Empty only\n * for a presence-only push. */\n events: PushedEvent[]\n /** Branded like `events`: `session.setPresence` accepts it whole;\n * field validation still happens inside `setPresence`. */\n presence?: PushedPresence\n}\n\nexport function parsePresenceSibling(\n value: unknown,\n): PushedPresence | undefined {\n if (value === undefined) return undefined\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw invalid('presence must be an object when present')\n }\n const { participant, values, seen, at } = value as Record<string, unknown>\n if (typeof participant !== 'string' || participant.length === 0) {\n throw invalid('presence.participant must be a non-empty string')\n }\n if (RESERVED_PARTICIPANT_IDS.has(participant)) {\n throw invalid(`presence.participant must not be '${participant}'`)\n }\n if (values === null || typeof values !== 'object' || Array.isArray(values)) {\n throw invalid('presence.values must be an object')\n }\n if (\n seen !== undefined &&\n (typeof seen !== 'number' || !Number.isFinite(seen) || seen < 0)\n ) {\n throw invalid('presence.seen must be a non-negative number when present')\n }\n if (\n at !== undefined &&\n (typeof at !== 'number' ||\n !Number.isFinite(at) ||\n at < 0 ||\n at > MAX_DATE_MS)\n ) {\n throw invalid(\n 'presence.at must be a non-negative epoch-milliseconds timestamp within the Date range when present',\n )\n }\n const out: {\n participant: string\n values: Record<string, unknown>\n seen?: number\n at?: number\n } = { participant, values: values as Record<string, unknown> }\n if (seen !== undefined) out.seen = seen\n if (at !== undefined) out.at = at\n Object.defineProperty(out, '~a2.pushed', { value: true })\n return out as PushedPresence\n}\n\n/** The events half of the envelope — shared verbatim by the POST body\n * and the socket's `push` frames, so the provenance brand and the\n * INVALID_PAYLOAD shapes are one implementation. */\nexport function parsePushEvents(events: unknown): PushedEvent[] {\n if (!Array.isArray(events) || events.length === 0) {\n throw invalid('events must be a non-empty array')\n }\n return events.map((event, i) => {\n if (event === null || typeof event !== 'object') {\n throw invalid(`events[${i}] must be an object`)\n }\n const { type, payload, id } = event as Record<string, unknown>\n if (typeof type !== 'string' || type.length === 0) {\n throw invalid(`events[${i}].type must be a non-empty string`)\n }\n if (id !== undefined && typeof id !== 'string') {\n throw invalid(`events[${i}].id must be a string when present`)\n }\n const out: { type: string; payload: unknown; id?: string } = {\n type,\n payload,\n }\n if (id !== undefined) out.id = id\n Object.defineProperty(out, '~a2.pushed', { value: true })\n return out as PushedEvent\n })\n}\n\n/**\n * Validate the push envelope — `{ sessionId, events, presence? }` —\n * throwing `INVALID_PAYLOAD` on a malformed body. Payload validation\n * against the machine's schemas happens in `append` and `setPresence`,\n * not here. Either plane may appear alone: a presence-only push has no\n * (or empty) `events`.\n */\nexport async function parsePushBody(req: Request): Promise<PushBody> {\n let body: unknown\n try {\n body = await req.json()\n } catch (cause) {\n throw new A2Error('INVALID_PAYLOAD', 'push body is not valid JSON', {\n cause,\n })\n }\n if (body === null || typeof body !== 'object') {\n throw invalid('expected an object')\n }\n const { sessionId, events, presence } = body as Record<string, unknown>\n if (typeof sessionId !== 'string' || sessionId.length === 0) {\n throw invalid('sessionId must be a non-empty string')\n }\n const parsedPresence = parsePresenceSibling(presence)\n if (\n parsedPresence !== undefined &&\n (events === undefined || (Array.isArray(events) && events.length === 0))\n ) {\n return { sessionId, events: [], presence: parsedPresence }\n }\n const parsed = parsePushEvents(events)\n return parsedPresence !== undefined\n ? { sessionId, events: parsed, presence: parsedPresence }\n : { sessionId, events: parsed }\n}\n","/**\n * The A2 socket protocol's server half (specs/a2-api.md §13): the\n * per-socket shell — teardown, backpressure, heartbeat, the malformed\n * budget, the in-flight bound — and `sessionsSocket`, the multiplexed\n * frame handler riding it. Internal module: `handle`'s upgrade lane is\n * the sanctioned mount; only `A2Socket` (the structural socket the\n * platform hands over) is re-exported as public surface.\n */\n\nimport { A2Error } from './errors.ts'\nimport { SOCKET_TIMINGS, STREAM_TIMINGS } from './internal.ts'\nimport type { Event } from './store.ts'\nimport type { EventDefs, PresencePatch, PresenceSnapshot } from './contract.ts'\nimport type { PushedEvent, PushedPresence, Session } from './server.ts'\nimport { parsePresenceSibling, parsePushEvents } from './push-envelope.ts'\nimport {\n SOCKET_PING_FRAME,\n asA2Error,\n socketAckFor,\n socketErrorAckFor,\n socketFrameFor,\n socketSubscribedFor,\n socketUnsubscribedFor,\n} from './wire.ts'\n\n// ── the ws wire ───────────────────────────────────────────────────────\n\n/**\n * The structural socket `sessionsSocket` speaks against — satisfied\n * by `ws` and by `@vercel/functions`' upgraded socket alike, so the\n * platform upgrade API stays out of the library. `message` data\n * arrives as whatever the platform delivers (string, Buffer,\n * ArrayBuffer, or fragments); the handler normalizes defensively.\n */\nexport type A2Socket = {\n send(data: string): void\n on(event: 'message', listener: (data: unknown) => void): void\n on(event: 'close', listener: () => void): void\n on(event: 'error', listener: (error: unknown) => void): void\n close(code?: number, reason?: string): void\n /** Bytes queued but not yet transmitted — when exposed, the down\n * pump uses it for backpressure (`SOCKET_TIMINGS`). */\n bufferedAmount?: number\n}\n\nconst messageText = (data: unknown): string | null => {\n if (typeof data === 'string') return data\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data)\n if (ArrayBuffer.isView(data)) return new TextDecoder().decode(data)\n if (Array.isArray(data) && data.every(ArrayBuffer.isView)) {\n // ws delivers fragmented messages as a Buffer array; stream-decode\n // so multi-byte characters can span fragment boundaries.\n const decoder = new TextDecoder()\n let text = ''\n for (const part of data) text += decoder.decode(part, { stream: true })\n return text + decoder.decode()\n }\n return null\n}\n\n/**\n * How many unparseable or invalid frames a socket may send before it\n * is closed (1008): the budget absorbs isolated client bugs without\n * letting a broken peer spin the validation path forever. Push frames\n * with a usable `req` never count — their failures are answered with\n * error acks, exactly like the POST path's 4xx bodies.\n */\nconst MAX_MALFORMED_FRAMES = 5\n\n/**\n * The pump's runtime view of a session. The presence members are\n * conditional on the public surface (`WithPresence`) and therefore\n * invisible on a generic `Session<D>`, so capability is checked at\n * runtime (`setPresence` present) — the same resolution as the docs'\n * route sample. The casts live in `sessionsSocket` and `handle`.\n */\nexport type SocketSession = {\n stream(opts: {\n startAfter: number\n presence?: boolean\n }): AsyncIterable<Event | PresencePatch | PresenceSnapshot>\n append(...events: PushedEvent[]): Promise<Event[]>\n setPresence?(patch: PushedPresence): Promise<void>\n}\n\n/**\n * The per-socket machinery `sessionsSocket` builds on: the torn flag\n * with a release-once teardown, the backpressure-aware send with its\n * non-reader ceiling, the malformed budget, the in-flight bound, the\n * message normalization, the heartbeat, and the optional deadline.\n */\ntype SocketShell = {\n torn(): boolean\n send(data: string): void\n shutdown(code: number, reason?: string): void\n malformedFrame(reason: string): void\n buffered(): number\n overCap(): boolean\n track(work: Promise<void>): void\n}\n\nfunction socketShell(\n socket: A2Socket,\n release: () => void,\n onFrame: (frame: Record<string, unknown>) => void,\n deadlineAt: number | undefined,\n): SocketShell {\n let torn = false\n let malformed = 0\n let heartbeat: ReturnType<typeof setInterval> | undefined\n let deadline: ReturnType<typeof setTimeout> | undefined\n\n const teardown = (): void => {\n if (torn) return\n torn = true\n if (heartbeat !== undefined) clearInterval(heartbeat)\n if (deadline !== undefined) clearTimeout(deadline)\n release()\n }\n\n const shutdown = (code: number, reason?: string): void => {\n if (torn) return\n teardown()\n try {\n socket.close(code, reason)\n } catch {\n // Already closed underneath us — teardown has run either way.\n }\n }\n\n const send = (data: string): void => {\n if (torn) return\n // The pump parks at the high-water mark, but acks and pings are\n // exempt (small, correctness-relevant) — so a peer that never\n // reads while it keeps uploading grows the buffer through its own\n // acks. The ceiling closes the class: past it, the peer is not a\n // slow reader, it is a non-reader (adversarial F4).\n if (\n typeof socket.bufferedAmount === 'number' &&\n socket.bufferedAmount > SOCKET_TIMINGS.disconnectWaterMarkBytes\n ) {\n shutdown(1008, 'backpressure: peer not reading')\n return\n }\n try {\n socket.send(data)\n } catch {\n // The platform closed the socket without an event; closing it\n // again would throw the same way, so just tear down.\n teardown()\n }\n }\n\n const malformedFrame = (reason: string): void => {\n malformed += 1\n if (malformed >= MAX_MALFORMED_FRAMES) {\n shutdown(1008, `too many malformed frames (last: ${reason})`)\n }\n }\n\n // The up-lane bound (adversarial F3): one socket must not hold\n // unbounded concurrent appends. Shedding is per-plane semantics —\n // pushes are TOLD (retryable error ack, ids make the retry safe),\n // presence is silently dropped (a lost update is repainted by the\n // next one). Sheds are our load decision, never the peer's\n // malformedness, so they spend no budget.\n let inFlight = 0\n const track = (work: Promise<void>): void => {\n inFlight += 1\n void work.finally(() => {\n inFlight -= 1\n })\n }\n\n const handleMessage = (data: unknown): void => {\n if (torn) return\n const text = messageText(data)\n if (text === null) {\n malformedFrame('non-text frame')\n return\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n malformedFrame('frame is not valid JSON')\n return\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n malformedFrame('frame is not an object')\n return\n }\n onFrame(parsed as Record<string, unknown>)\n }\n\n socket.on('message', handleMessage)\n socket.on('close', teardown)\n socket.on('error', teardown)\n\n heartbeat = setInterval(\n () => send(SOCKET_PING_FRAME),\n STREAM_TIMINGS.sseHeartbeatMs,\n )\n ;(heartbeat as { unref?: () => void }).unref?.()\n\n if (deadlineAt !== undefined) {\n deadline = setTimeout(\n () => shutdown(1000, 'deadline'),\n Math.max(0, deadlineAt - Date.now()),\n )\n ;(deadline as { unref?: () => void }).unref?.()\n }\n\n return {\n torn: () => torn,\n send,\n shutdown,\n malformedFrame,\n buffered: () =>\n typeof socket.bufferedAmount === 'number' ? socket.bufferedAmount : 0,\n overCap: () => inFlight >= SOCKET_TIMINGS.maxInFlightMessages,\n track,\n }\n}\n\n/**\n * The per-session authorization seam of `sessionsSocket`: called once\n * per `subscribe` entry (and never for the pushes and presence that\n * ride an accepted subscription). `null` rejects that session — the\n * socket answers `unsubscribed` with a rejection reason and every\n * other session on it is untouched.\n */\nexport type SessionsSocketResolve<D extends EventDefs> = (\n sessionId: string,\n /** The entry's resume frontier — `stream({ startAfter })`. */\n startAfter: number,\n) => Session<D> | null | Promise<Session<D> | null>\n\nexport type SessionsSocketOptions = {\n /** The `stream()` presence opt-in for every session on the socket —\n * the server decides, same rule as the SSE route. */\n presence?: boolean\n /** Epoch ms: close cleanly (code 1000) at this time, ahead of a known\n * platform deadline, so clients reconnect on our schedule. */\n deadline?: number\n /**\n * Per-frame push gate — policy's seam on the up lane, the socket\n * counterpart of a POST route's checks (`handle`'s `before` rides\n * it). Runs after the frame parses and before `append`: return an\n * `A2Error` to answer the push with that error ack (nothing\n * appended), `null` to allow. Absent, every push on an accepted\n * subscription is allowed — the prior behavior, unchanged.\n */\n gatePush?(\n sessionId: string,\n events: PushedEvent[],\n ): A2Error | null | Promise<A2Error | null>\n}\n\ntype SocketSubscription = {\n target: SocketSession\n iterator: AsyncIterator<Event | PresencePatch | PresenceSnapshot>\n stopped: boolean\n}\n\nconst stopSubscription = (subscription: SocketSubscription): void => {\n subscription.stopped = true\n void subscription.iterator.return?.()?.catch(() => {})\n}\n\nconst parseSubscribeEntries = (\n value: unknown,\n): Array<{ id: string; index: number }> | null => {\n if (!Array.isArray(value) || value.length === 0) return null\n const entries: Array<{ id: string; index: number }> = []\n for (const entry of value) {\n if (entry === null || typeof entry !== 'object') return null\n const { id, index } = entry as Record<string, unknown>\n if (typeof id !== 'string' || id.length === 0) return null\n if (typeof index !== 'number' || !Number.isInteger(index) || index < 0) {\n return null\n }\n entries.push({ id, index })\n }\n return entries\n}\n\n/**\n * Speak the multiplexed A2 socket protocol (specs/a2-api.md §13)\n * against many sessions on one socket. `subscribe` frames open a\n * per-session pump at that session's own resume frontier; push and\n * presence frames route by `sessionId` through the same validation\n * seams as the POST lane; every down frame carries the `sessionId` it\n * belongs to. One heartbeat, one\n * malformed budget, one in-flight bound, one backpressure gauge — per\n * socket, shared by all sessions. A session's stream ending or failing\n * answers `unsubscribed` for that session; only the peer, the\n * deadline, the malformed budget, or backpressure closes the socket.\n */\nexport function sessionsSocket<D extends EventDefs>(\n resolve: SessionsSocketResolve<D>,\n socket: A2Socket,\n options?: SessionsSocketOptions,\n): void {\n const presence = options?.presence === true\n const subscriptions = new Map<string, SocketSubscription>()\n\n const retire = (id: string, subscription: SocketSubscription): void => {\n if (subscriptions.get(id) === subscription) subscriptions.delete(id)\n }\n\n const pump = async (\n id: string,\n subscription: SocketSubscription,\n ): Promise<void> => {\n for (;;) {\n for (;;) {\n if (\n shell.torn() ||\n subscription.stopped ||\n shell.buffered() <= SOCKET_TIMINGS.highWaterMarkBytes\n )\n break\n // oxlint-disable-next-line no-await-in-loop -- backpressure park\n await new Promise((wake) =>\n setTimeout(wake, SOCKET_TIMINGS.resumePollMs),\n )\n }\n if (shell.torn() || subscription.stopped) return\n // oxlint-disable-next-line no-await-in-loop -- stream pump\n const { value, done } = await subscription.iterator.next()\n if (shell.torn() || subscription.stopped) return\n if (done) return\n shell.send(socketFrameFor(value, id))\n }\n }\n\n const runSubscription = async (\n id: string,\n subscription: SocketSubscription,\n ): Promise<void> => {\n try {\n await pump(id, subscription)\n if (shell.torn() || subscription.stopped) return\n retire(id, subscription)\n shell.send(socketUnsubscribedFor(id))\n } catch {\n if (shell.torn() || subscription.stopped) return\n retire(id, subscription)\n shell.send(socketUnsubscribedFor(id, 'stream failed'))\n }\n }\n\n const startSubscription = async (\n id: string,\n index: number,\n ): Promise<void> => {\n let session: Session<D> | null\n try {\n session = await resolve(id, index)\n } catch {\n session = null\n }\n if (shell.torn()) return\n if (session === null) {\n shell.send(socketUnsubscribedFor(id, 'subscribe rejected'))\n return\n }\n const target = session as unknown as SocketSession\n const existing = subscriptions.get(id)\n if (existing) stopSubscription(existing)\n const iterator = (\n presence\n ? target.stream({ startAfter: index, presence: true })\n : target.stream({ startAfter: index })\n )[Symbol.asyncIterator]()\n const subscription: SocketSubscription = {\n target,\n iterator,\n stopped: false,\n }\n subscriptions.set(id, subscription)\n shell.send(socketSubscribedFor(id))\n void runSubscription(id, subscription)\n }\n\n // Control frames (subscribe/unsubscribe) run in arrival order — an\n // unsubscribe can never overtake the subscribe it targets while the\n // resolve hook is still pending.\n let controlTail: Promise<void> = Promise.resolve()\n const control = (work: () => Promise<void> | void): void => {\n controlTail = controlTail.then(work).catch(() => {})\n }\n\n const handlePush = async (\n frame: Record<string, unknown>,\n req: number,\n sessionId: string,\n ): Promise<void> => {\n const subscription = subscriptions.get(sessionId)\n if (subscription === undefined) {\n // Retryable: the client re-subscribes on reconnect and the retry\n // rides the same client-generated event ids.\n shell.send(\n socketErrorAckFor(\n req,\n new A2Error(\n 'STORE_UNAVAILABLE',\n 'session is not subscribed on this socket',\n ),\n sessionId,\n ),\n )\n return\n }\n try {\n const events = parsePushEvents(frame['events'])\n const denial = (await options?.gatePush?.(sessionId, events)) ?? null\n if (denial !== null) {\n shell.send(socketErrorAckFor(req, denial, sessionId))\n return\n }\n const appended = await subscription.target.append(...events)\n shell.send(socketAckFor(req, appended, sessionId))\n } catch (err) {\n shell.send(socketErrorAckFor(req, asA2Error(err), sessionId))\n }\n }\n\n const handlePresence = async (\n frame: Record<string, unknown>,\n sessionId: string,\n ): Promise<void> => {\n const subscription = subscriptions.get(sessionId)\n // An unsubscribe race, not malformedness — the plane repaints.\n if (subscription === undefined) return\n if (typeof subscription.target.setPresence !== 'function') {\n shell.malformedFrame('presence frame on a presence-less session')\n return\n }\n try {\n const patch = parsePresenceSibling(frame)\n if (patch) await subscription.target.setPresence(patch)\n } catch {\n shell.malformedFrame('invalid presence frame')\n }\n }\n\n const handleFrame = (frame: Record<string, unknown>): void => {\n switch (frame['kind']) {\n case 'subscribe': {\n const entries = parseSubscribeEntries(frame['sessions'])\n if (entries === null) {\n shell.malformedFrame('malformed subscribe frame')\n return\n }\n for (const entry of entries) {\n control(() => startSubscription(entry.id, entry.index))\n }\n return\n }\n case 'unsubscribe': {\n const ids = frame['sessions']\n if (\n !Array.isArray(ids) ||\n ids.length === 0 ||\n !ids.every((id) => typeof id === 'string')\n ) {\n shell.malformedFrame('malformed unsubscribe frame')\n return\n }\n for (const id of ids as string[]) {\n control(() => {\n const subscription = subscriptions.get(id)\n if (subscription === undefined) return\n stopSubscription(subscription)\n subscriptions.delete(id)\n })\n }\n return\n }\n case 'push': {\n const req = frame['req']\n const sessionId = frame['sessionId']\n if (shell.overCap()) {\n if (typeof req === 'number') {\n shell.send(\n socketErrorAckFor(\n req,\n new A2Error(\n 'STORE_UNAVAILABLE',\n 'push shed: too many in flight on this socket',\n ),\n typeof sessionId === 'string' ? sessionId : undefined,\n ),\n )\n } else {\n shell.malformedFrame('push frame without a numeric req')\n }\n return\n }\n if (typeof req !== 'number') {\n shell.malformedFrame('push frame without a numeric req')\n return\n }\n if (typeof sessionId !== 'string' || sessionId.length === 0) {\n shell.send(\n socketErrorAckFor(\n req,\n new A2Error('INVALID_PAYLOAD', 'push frame without a sessionId'),\n ),\n )\n return\n }\n shell.track(handlePush(frame, req, sessionId))\n return\n }\n case 'presence': {\n if (shell.overCap()) return\n const sessionId = frame['sessionId']\n if (typeof sessionId !== 'string' || sessionId.length === 0) {\n shell.malformedFrame('presence frame without a sessionId')\n return\n }\n shell.track(handlePresence(frame, sessionId))\n return\n }\n default:\n return // unknown kinds are skipped — the forward-compatibility rule\n }\n }\n\n const shell = socketShell(\n socket,\n () => {\n for (const subscription of subscriptions.values()) {\n stopSubscription(subscription)\n }\n subscriptions.clear()\n },\n (frame) => handleFrame(frame),\n options?.deadline,\n )\n}\n","/**\n * experimental-a2/http — the wire's server half (specs/a2-api.md §9–10, §13).\n *\n * A thin public assembly. The surface: `handle` — GET, POST, and the\n * socket's frames parsed into intents (stream, history, push,\n * ws-upgrade) with `before`/`after` hooks around them for policy —\n * plus `A2Intent`/`A2Outcome`/`UpgradeFn`/`A2Socket` (its argument\n * types), `errorResponse`/`deserializeError` (the A2Error wire pair\n * both sides branch on), and `schedulerHandler` (the scheduler\n * callback mount). The machinery lives in internal modules — the SSE\n * framing in sse.ts, the push-envelope trust boundary in\n * push-envelope.ts, the socket shell and multiplexed handler in\n * session-socket.ts — because nothing outside `handle` is sanctioned\n * to compose them. Policy — auth, caching, quotas — never enters this\n * module: the hooks own it.\n */\n\nimport { A2Error } from './errors.ts'\nimport {\n assertServerSchedulerBindings,\n assertSchedulerTargets,\n schedulerForServer,\n} from './internal.ts'\nimport type { ContractEvent, EventDefs, PresenceDefs } from './contract.ts'\nimport type {\n A2Server,\n DrainableServer,\n PushedEvent,\n PushedPresence,\n} from './server.ts'\nimport { sseResponse } from './sse.ts'\nimport { invalidPushBody, parsePushBody } from './push-envelope.ts'\nimport {\n sessionsSocket,\n type A2Socket,\n type SocketSession,\n} from './session-socket.ts'\nimport {\n asA2Error,\n errorFromWire,\n errorStatus,\n errorToWire,\n eventToWire,\n} from './wire.ts'\n\nexport type { A2Socket } from './session-socket.ts'\n\n/**\n * Create one scheduler callback route for servers sharing the same configured\n * scheduler. Configuration mistakes throw immediately while the route module\n * is evaluated, before a delivery can be acknowledged by the wrong adapter.\n */\nexport function schedulerHandler(\n firstServer: DrainableServer,\n ...servers: DrainableServer[]\n): (req: Request) => Promise<Response> {\n if (firstServer === undefined) {\n throw new TypeError('schedulerHandler() requires at least one A2 server')\n }\n const scheduler = schedulerForServer(firstServer)\n const targets = [firstServer, ...servers]\n assertSchedulerTargets(targets, { allowStructural: false })\n assertServerSchedulerBindings(scheduler, targets)\n return scheduler.handler(...targets)\n}\n\n/**\n * Serialize an error into the documented wire shape,\n * `{ error: { code, message, details } }`, with the mapped status\n * (400 for caller bugs, 503 for STORE_UNAVAILABLE). Non-A2Errors become a\n * 503 STORE_UNAVAILABLE — from the client's perspective an unknown server\n * failure is retryable-once, not a protocol contract. The natural\n * return value of a `before` hook that wants the wire's own error\n * shapes.\n */\nexport function errorResponse(error: unknown): Response {\n const a2error = asA2Error(error)\n return Response.json(errorToWire(a2error), {\n status: errorStatus(a2error.code),\n })\n}\n\n/**\n * The other half of the wire pair: rebuild an `A2Error` from a response\n * body, or null if the body isn't one. Client `push` uses it so both\n * sides branch on identical codes.\n */\nexport function deserializeError(body: unknown): A2Error | null {\n return errorFromWire(body)\n}\n\n// ── handle: the request protocol, parsed ────────────────────────\n\n/**\n * What a request (or socket frame) asks of a session, parsed — the\n * argument policy hooks branch on. `stream` covers the SSE GET and the\n * socket's subscribe frames; `push` covers the HTTP POST and the\n * socket's push frames; `history` is the bounded cold read the\n * client's `loadHistory` rides; `ws-upgrade` is the upgrade GET before\n * any frame flows.\n */\nexport type A2Intent<D extends EventDefs = EventDefs> =\n | { type: 'ws-upgrade' }\n | {\n type: 'stream'\n sessionId: string\n startAfter: number\n transport: 'sse' | 'ws'\n }\n | { type: 'history'; sessionId: string; gte: number; lte: number }\n | {\n type: 'push'\n sessionId: string\n /** Parsed and branded, payloads not yet validated — `type`\n * autocompletes the contract's names but is any string off the\n * wire; `append` rejects unknown types after the hooks run. */\n events: Array<PushedEvent & { type: SuggestedEventType<D> }>\n presence?: PushedPresence\n transport: 'http' | 'ws'\n }\n\n/** The contract's event names for editor completion, without excluding\n * the wire's reality (any string can arrive; validation is later). */\ntype SuggestedEventType<D extends EventDefs> =\n (keyof D & string) | (string & Record<never, never>)\n\n/** What the library did with an allowed intent — `after`'s view.\n * `covered` = the closed range is fully present (an immutable slice of\n * the append-only log), the one caching-relevant fact the library\n * owns; policy over it belongs to the app's `after`. */\nexport type A2Outcome<D extends EventDefs = EventDefs> =\n | { type: 'stream' }\n | { type: 'history'; covered: boolean; events: ContractEvent<D>[] }\n | { type: 'push'; appended: ContractEvent<D>[] }\n\n/**\n * The platform's socket-upgrade seam: given the attach callback, return\n * the 101 response. On Vercel:\n * `upgrade: (attach) => experimental_upgradeWebSocket(attach)`.\n */\nexport type UpgradeFn = (\n attach: (socket: A2Socket) => void,\n) => Response | Promise<Response>\n\nconst parseHistoryBounds = (\n gte: string | null,\n lte: string | null,\n): { gte: number; lte: number } => {\n if (gte === null || lte === null) {\n throw new A2Error(\n 'INVALID_PAYLOAD',\n 'a history read takes both gte and lte',\n )\n }\n const bounds = { gte: Number(gte), lte: Number(lte) }\n for (const [name, value] of Object.entries(bounds)) {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new A2Error(\n 'INVALID_PAYLOAD',\n `history ${name} must be a non-negative safe integer`,\n )\n }\n }\n if (bounds.gte > bounds.lte) {\n throw new A2Error(\n 'INVALID_PAYLOAD',\n 'history gte must be less than or equal to lte',\n )\n }\n return bounds\n}\n\nconst parseResumeIndex = (raw: string | null): number => {\n if (raw === null) return 0\n const index = Number(raw)\n if (!Number.isSafeInteger(index) || index < 0) {\n throw new A2Error(\n 'INVALID_PAYLOAD',\n 'stream index must be a non-negative safe integer',\n )\n }\n return index\n}\n\n/** One bag: the policy hooks and the capability options — the names\n * distinguish them, so `handle(server, { presence: true })` and\n * `handle(server, { before, upgrade })` both read naturally. */\nexport type HandleOptions<D extends EventDefs = EventDefs> = {\n /** Return a Response to deny; thrown errors are treated as\n * infrastructure failures and answer with the retryable wire shape. */\n before?(args: {\n request: Request\n intent: A2Intent<D>\n }): Response | undefined | void | Promise<Response | undefined | void>\n after?(args: {\n request: Request\n intent: A2Intent<D>\n outcome: A2Outcome<D>\n response: Response\n }): Response | undefined | void | Promise<Response | undefined | void>\n upgrade?: UpgradeFn\n presence?: boolean\n deadline?: number\n}\n\n/**\n * The session route pair as one call: `export const { GET, POST } =\n * handle(server, options?)` serves the live stream (SSE GET, resumed\n * at `index`), the history slice (GET with `gte`/`lte`), the push\n * envelope (POST), and — with `options.upgrade` — the multiplexed\n * WebSocket, all through one pair of policy hooks.\n *\n * Parsing is protocol, hooks are policy: a request that fails to parse\n * (missing sessionId, malformed bounds, bad push envelope) answers\n * INVALID_PAYLOAD on the wire before any hook runs. `before` sees\n * every parsed intent — HTTP requests and socket frames alike, with\n * `request` always the original (for socket frames: upgrade) Request —\n * and short-circuits by returning a Response: over HTTP it is the\n * response verbatim; over the socket it is translated to the wire's\n * own vocabulary (subscribe → `unsubscribed { reason: 'subscribe\n * rejected' }`, push → an INVALID_PAYLOAD error ack), because a\n * Response body cannot cross a socket. `after` runs only where the\n * library produced an HTTP response — never after a short-circuit,\n * never for socket frames — and may mutate `response.headers` in place\n * or return a replacement (history caching policy lives here, on\n * `outcome.covered`). `options.deadline` (epoch ms) closes sockets\n * cleanly ahead of a known platform deadline, so clients reconnect on\n * our schedule.\n */\nexport function handle<\n D extends EventDefs,\n P extends PresenceDefs = Record<never, never>,\n>(\n server: A2Server<D, P>,\n options?: HandleOptions<D>,\n): {\n GET(req: Request): Promise<Response>\n POST(req: Request): Promise<Response>\n} {\n const presence = options?.presence === true\n\n const before = async (\n request: Request,\n intent: A2Intent<D>,\n ): Promise<Response | null> => {\n const result = await options?.before?.({ request, intent })\n return result instanceof Response ? result : null\n }\n\n const decorate = async (\n request: Request,\n intent: A2Intent<D>,\n outcome: A2Outcome<D>,\n response: Response,\n ): Promise<Response> => {\n const replaced = await options?.after?.({\n request,\n intent,\n outcome,\n response,\n })\n return replaced instanceof Response ? replaced : response\n }\n\n // Socket frames re-enter `before` with the upgrade Request — one\n // policy point for both wires, and the fix for pushes that were\n // gated only at subscribe time.\n const attach =\n (request: Request) =>\n (socket: A2Socket): void => {\n sessionsSocket<D>(\n async (sessionId, startAfter) => {\n const short = await before(request, {\n type: 'stream',\n sessionId,\n startAfter,\n transport: 'ws',\n })\n return short === null ? server.session(sessionId) : null\n },\n socket,\n {\n presence,\n ...(options?.deadline === undefined\n ? {}\n : { deadline: options.deadline }),\n gatePush: async (sessionId, events) => {\n const short = await before(request, {\n type: 'push',\n sessionId,\n events,\n transport: 'ws',\n })\n // The denial Response cannot cross the socket; INVALID_PAYLOAD\n // is the one caller-fault code the client never retries — a\n // retryable code would spin retries against a standing denial.\n return short === null\n ? null\n : new A2Error('INVALID_PAYLOAD', 'push rejected')\n },\n },\n )\n }\n\n return {\n async GET(req: Request): Promise<Response> {\n try {\n if (req.headers.get('upgrade')?.toLowerCase() === 'websocket') {\n const short = await before(req, { type: 'ws-upgrade' })\n if (short !== null) return short\n if (options?.upgrade === undefined) {\n return new Response(\n \"WebSocket upgrade requested, but handle() has no upgrade implementation — pass options.upgrade, e.g. { upgrade: (attach) => experimental_upgradeWebSocket(attach) } from '@vercel/functions'\",\n { status: 426 },\n )\n }\n return await options.upgrade(attach(req))\n }\n const { searchParams } = new URL(req.url)\n const sessionId = searchParams.get('sessionId')\n if (sessionId === null || sessionId.length === 0) {\n throw new A2Error('INVALID_PAYLOAD', 'missing sessionId')\n }\n const gteRaw = searchParams.get('gte')\n const lteRaw = searchParams.get('lte')\n if (gteRaw !== null || lteRaw !== null) {\n const bounds = parseHistoryBounds(gteRaw, lteRaw)\n const intent: A2Intent<D> = { type: 'history', sessionId, ...bounds }\n const short = await before(req, intent)\n if (short !== null) return short\n const events = await server.session(sessionId).history(bounds)\n const covered = events.length === bounds.lte - bounds.gte + 1\n return await decorate(\n req,\n intent,\n { type: 'history', covered, events },\n Response.json(events.map(eventToWire)),\n )\n }\n const startAfter = parseResumeIndex(searchParams.get('index'))\n const intent: A2Intent<D> = {\n type: 'stream',\n sessionId,\n startAfter,\n transport: 'sse',\n }\n const short = await before(req, intent)\n if (short !== null) return short\n const target = server.session(sessionId) as unknown as SocketSession\n const response = sseResponse(\n presence\n ? target.stream({ startAfter, presence: true })\n : target.stream({ startAfter }),\n )\n return await decorate(req, intent, { type: 'stream' }, response)\n } catch (err) {\n return errorResponse(err)\n }\n },\n\n async POST(req: Request): Promise<Response> {\n try {\n const body = await parsePushBody(req)\n const intent: A2Intent<D> = {\n type: 'push',\n sessionId: body.sessionId,\n events: body.events,\n ...(body.presence === undefined ? {} : { presence: body.presence }),\n transport: 'http',\n }\n const short = await before(req, intent)\n if (short !== null) return short\n const session = server.session(body.sessionId)\n if (body.presence !== undefined) {\n const target = session as unknown as SocketSession\n if (typeof target.setPresence !== 'function') {\n throw invalidPushBody('presence is not declared by this contract')\n }\n await target.setPresence(body.presence)\n }\n const appended =\n body.events.length === 0 ? [] : await session.append(...body.events)\n return await decorate(\n req,\n intent,\n { type: 'push', appended },\n Response.json(appended),\n )\n } catch (err) {\n return errorResponse(err)\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotent-replay-DuqEkYA7.js","names":[],"sources":["../src/idempotent-replay.ts"],"sourcesContent":["import { A2Error } from './errors.ts'\nimport type { AppendEvent, StoredEvent } from './store.ts'\n\nconst canonicalJson = (value: unknown): string =>\n JSON.stringify(value, (_key, candidate: unknown) => {\n if (\n candidate === null ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate)\n ) {\n return candidate\n }\n return Object.fromEntries(\n Object.entries(candidate).toSorted(([left], [right]) =>\n left < right ? -1 : left > right ? 1 : 0,\n ),\n )\n }) ?? 'null'\n\nexport const idempotentReplay = (\n events: readonly AppendEvent[],\n existing: readonly StoredEvent[],\n): StoredEvent[] => {\n const byId = new Map(existing.map((event) => [event.id, event]))\n const ordered = events.map((event) =>\n event.id === undefined ? undefined : byId.get(event.id),\n )\n const firstIndex = ordered[0]?.index\n const batchSize = events[0]?.cause?.batchSize\n\n if (\n firstIndex === undefined ||\n (batchSize !== undefined && batchSize !== events.length) ||\n ordered.some((stored, index) => {\n const event = events[index]!\n return (\n stored === undefined ||\n stored.index !== firstIndex + index ||\n stored.type !== event.type ||\n stored.lane !== (event.lane ?? null) ||\n stored.cause?.batchSize !== event.cause?.batchSize ||\n canonicalJson(stored.payload) !== canonicalJson(event.payload)\n )\n })\n ) {\n throw new A2Error(\n 'PARTIAL_DUPLICATE_BATCH',\n 'already-appended events do not match the retried batch',\n )\n }\n\n return ordered as StoredEvent[]\n}\n"],"mappings":";;AAGA,MAAM,iBAAiB,UACrB,KAAK,UAAU,QAAQ,MAAM,cAAuB;CAClD,IACE,cAAc,QACd,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,GAEvB,OAAO;CAET,OAAO,OAAO,YACZ,OAAO,QAAQ,SAAS,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,WAC3C,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI,CACzC,CACF;AACF,CAAC,KAAK;AAER,MAAa,oBACX,QACA,aACkB;CAClB,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;CAC/D,MAAM,UAAU,OAAO,KAAK,UAC1B,MAAM,OAAO,KAAA,IAAY,KAAA,IAAY,KAAK,IAAI,MAAM,EAAE,CACxD;CACA,MAAM,aAAa,QAAQ,EAAE,EAAE;CAC/B,MAAM,YAAY,OAAO,EAAE,EAAE,OAAO;CAEpC,IACE,eAAe,KAAA,KACd,cAAc,KAAA,KAAa,cAAc,OAAO,UACjD,QAAQ,MAAM,QAAQ,UAAU;EAC9B,MAAM,QAAQ,OAAO;EACrB,OACE,WAAW,KAAA,KACX,OAAO,UAAU,aAAa,SAC9B,OAAO,SAAS,MAAM,QACtB,OAAO,UAAU,MAAM,QAAQ,SAC/B,OAAO,OAAO,cAAc,MAAM,OAAO,aACzC,cAAc,OAAO,OAAO,MAAM,cAAc,MAAM,OAAO;CAEjE,CAAC,GAED,MAAM,IAAI,QACR,2BACA,wDACF;CAGF,OAAO;AACT"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as EventDefs, c as PresencePatch, f as contract, h as StandardSchemaV1, i as EventBatchFactory, l as PresenceSnapshot, m as ReducerBuilder, n as Contract, o as PresenceDefs, p as Reducer, r as ContractEvent, s as PresenceMap, t as AppendInput, u as ReducerOptions } from "./contract-jIfaR085.js";
|
|
2
|
-
import { a as Event } from "./store-
|
|
2
|
+
import { a as Event } from "./store-DysUkTH3.js";
|
|
3
3
|
import { i as A2Telemetry, n as A2SpanHandle, r as A2SpanName, t as A2AttributeValue } from "./telemetry-BjYHTfh2.js";
|
|
4
4
|
import { n as A2ErrorCode, t as A2Error } from "./errors-W6nwJ-fm.js";
|
|
5
5
|
export { type A2AttributeValue, A2Error, type A2ErrorCode, type A2SpanHandle, type A2SpanName, type A2Telemetry, type AppendInput, type Contract, type ContractEvent, type Event, type EventBatchFactory, type EventDefs, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceSnapshot, type Reducer, type ReducerBuilder, type ReducerOptions, type StandardSchemaV1, contract };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspection-DaxB5jM2.js","names":[],"sources":["../src/inspection.ts"],"sourcesContent":["/**\n * Private bridge between `createServer` and `experimental-a2/devtools/server`.\n * Servers keep their store closure private; the WeakMap grants A2's own\n * read-only devtools entry point access without adding public methods to\n * application server handles.\n */\n\nimport type { StoredEvent, StoredSessionPage, StoredSnapshot } from './store.ts'\n\nexport class InspectionUnsupportedError extends Error {}\n\nexport type ServerInspection = {\n listSessions(options: {\n cursor?: string\n limit: number\n }): Promise<StoredSessionPage>\n readSessionPage(\n sessionId: string,\n options: { afterIndex: number; throughIndex?: number; limit: number },\n ): Promise<{\n events: StoredEvent[]\n snapshots: StoredSnapshot[]\n throughIndex: number\n nextIndex: number | null\n }>\n}\n\nexport const serverInspection: WeakMap<object, ServerInspection> = new WeakMap()\n\n/** Owned live-inspection cadence; mutable only for white-box tests. */\nexport const DEVTOOLS_TIMINGS: {\n activeMs: number\n idleMs: number\n heartbeatMs: number\n} = {\n activeMs: 250,\n idleMs: 1_000,\n heartbeatMs: 15_000,\n}\n"],"mappings":";AASA,IAAa,6BAAb,cAAgD,MAAM,CAAC;AAkBvD,MAAa,mCAAsD,IAAI,QAAQ;;AAG/E,MAAa,mBAIT;CACF,UAAU;CACV,QAAQ;CACR,aAAa;AACf"}
|
|
@@ -182,7 +182,7 @@ const STREAM_TIMINGS = {
|
|
|
182
182
|
reconnectMaxMs: 5e3
|
|
183
183
|
};
|
|
184
184
|
/**
|
|
185
|
-
* WebSocket down-pump backpressure — `
|
|
185
|
+
* WebSocket down-pump backpressure — `sessionsSocket` parks its pull
|
|
186
186
|
* loop while the socket reports more buffered bytes than the
|
|
187
187
|
* high-water mark and re-checks on the poll cadence, so a slow reader
|
|
188
188
|
* applies backpressure to the store subscription instead of buffering
|
|
@@ -207,3 +207,5 @@ const SOCKET_TIMINGS = {
|
|
|
207
207
|
const PRESENCE_TIMINGS = { sendIntervalMs: 66 };
|
|
208
208
|
//#endregion
|
|
209
209
|
export { schedulerForServer as _, PRESENCE_TIMINGS as a, STREAM_TIMINGS as c, assertServerSchedulerBindings as d, classifySchedulerSendFailure as f, nullProtoRecord as g, markSchedulerSendFailure as h, POLL_TIMINGS as i, ambientToolScopeStorage as l, installAmbientToolScopeStorage as m, MAX_DATE_MS as n, RESERVED_PARTICIPANT_IDS as o, consumeSchedulerSendFailure as p, NOTIFY_TIMINGS as r, SOCKET_TIMINGS as s, DRAIN_TIMINGS as t, assertSchedulerTargets as u, serverInternals as v, serverSchedulerBindings as y };
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=internal-DstsI6Re.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-DstsI6Re.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["/**\n * Private seams shared by a2 entry points and a2's own tests. Not exported\n * from the package. Nothing here is public API.\n */\n\nimport type { ScheduledEvent } from './scheduler-task.ts'\nimport type { A2Scheduler, DrainableServer } from './server.ts'\n\nexport type DrainOutcome = 'busy' | 'stalled' | 'settled'\n\n/**\n * A null-prototype record for maps keyed by caller-controlled strings\n * (participant ids, presence field names). On a normal object,\n * assigning to '__proto__' rewrites the prototype instead of creating\n * an own property — the prototype-pollution class. Null-prototype\n * objects still satisfy Record types and JSON.stringify cleanly.\n */\nexport function nullProtoRecord<T extends object>(): T {\n return Object.create(null) as T\n}\n\n/**\n * ECMA-262's maximum Date value (±8.64e15 ms). A presence stamp past\n * it becomes an Invalid Date whose toISOString throws — one hostile\n * stamp would poison every subscriber's snapshot frame — so both\n * seams bound `at` to the representable range.\n */\nexport const MAX_DATE_MS = 8_640_000_000_000_000\n\n/**\n * Participant ids that collide with Object.prototype plumbing.\n * Null-prototype construction already defuses them, but they are\n * rejected at both seams (`parsePushBody`, `setPresence`) so a hostile\n * id never propagates into userland maps either.\n */\nexport const RESERVED_PARTICIPANT_IDS: ReadonlySet<string> = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n])\n\nexport type DrainResult = {\n settled: boolean\n outcome: DrainOutcome\n /** Existing successor arm a scheduler callback may join before acking. */\n schedulerArm?: Promise<void>\n}\n\nexport type ServerInternals = {\n /**\n * Await every in-flight background drain this machine has scheduled,\n * including ones scheduled while waiting. Lets tests flush the\n * fire-and-forget inline path deterministically — no sleeps.\n */\n settle(): Promise<void>\n /** The public `server.drain()` result deliberately remains `{ settled }`. */\n schedulerDrain(\n sessionId: string,\n opts?: { watchdogDueAt?: number },\n ): Promise<DrainResult>\n /** Deliver a scheduler-owned delayed append through the normal root path. */\n schedulerAppend(\n sessionId: string,\n events: readonly ScheduledEvent[],\n ): Promise<void>\n}\n\nexport const serverInternals: WeakMap<object, ServerInternals> = new WeakMap()\n\ntype ServerSchedulerBinding = {\n scheduler: A2Scheduler | undefined\n}\n\n/** Scheduler configuration captured only for servers made by `createServer`. */\nexport const serverSchedulerBindings: WeakMap<object, ServerSchedulerBinding> =\n new WeakMap()\n\nconst objectLike = (value: unknown): value is object =>\n (typeof value === 'object' && value !== null) || typeof value === 'function'\n\n/** The ambient value delivered to `handlerContext` during a tool execution. */\nexport type AmbientToolScope = {\n /** Executing server identity; a test scope from `runWithHandlerContext` has none. */\n contract?: { readonly name: string }\n context: unknown\n}\n\n/**\n * Structural AsyncLocalStorage surface for the ambient tool scope. The\n * instance is created by the server-only AI entry point (the only place\n * `node:async_hooks` may be imported) and shared through a global\n * symbol slot, so `handlerContext` in the isomorphic AI entry point and\n * independently bundled A2 copies all read the same scope.\n */\nexport type AmbientToolScopeStorage = {\n getStore(): AmbientToolScope | undefined\n run<R>(store: AmbientToolScope, callback: () => R): R\n}\n\nconst TOOL_SCOPE_STORAGE = Symbol.for('experimental-a2.ai.tool-scope')\n\nexport function ambientToolScopeStorage(): AmbientToolScopeStorage | undefined {\n const value = Reflect.get(globalThis, TOOL_SCOPE_STORAGE)\n if (\n !objectLike(value) ||\n typeof Reflect.get(value, 'getStore') !== 'function' ||\n typeof Reflect.get(value, 'run') !== 'function'\n ) {\n return undefined\n }\n return value as AmbientToolScopeStorage\n}\n\nexport function installAmbientToolScopeStorage(\n create: () => AmbientToolScopeStorage,\n): AmbientToolScopeStorage {\n const existing = ambientToolScopeStorage()\n if (existing !== undefined) return existing\n const storage = create()\n Reflect.set(globalThis, TOOL_SCOPE_STORAGE, storage)\n return storage\n}\n\nexport type SchedulerSendFailureDisposition = 'retryable' | 'terminal'\n\ntype SchedulerSendFailureMarker = {\n readonly version: 1\n readonly pending: SchedulerSendFailureDisposition[]\n readonly active: SchedulerSendFailureDisposition[]\n}\n\nconst SCHEDULER_SEND_FAILURE = Symbol.for(\n 'experimental-a2.scheduler.send-failure',\n)\n\nconst schedulerSendFailureMarker = (\n value: unknown,\n): SchedulerSendFailureMarker | undefined => {\n if (!objectLike(value)) return undefined\n const marker = Reflect.get(value, SCHEDULER_SEND_FAILURE)\n if (\n !objectLike(marker) ||\n Reflect.get(marker, 'version') !== 1 ||\n !Array.isArray(Reflect.get(marker, 'pending')) ||\n !Array.isArray(Reflect.get(marker, 'active'))\n ) {\n return undefined\n }\n return marker as SchedulerSendFailureMarker\n}\n\nconst schedulerFailureObject = (error: unknown): object => {\n if (\n objectLike(error) &&\n (schedulerSendFailureMarker(error) !== undefined ||\n Object.isExtensible(error))\n ) {\n return error\n }\n return new Error(String(error), { cause: error })\n}\n\nconst schedulerSendFailureState = (\n error: object,\n): SchedulerSendFailureMarker => {\n const existing = schedulerSendFailureMarker(error)\n if (existing !== undefined) return existing\n const marker: SchedulerSendFailureMarker = {\n version: 1,\n pending: [],\n active: [],\n }\n Object.defineProperty(error, SCHEDULER_SEND_FAILURE, {\n value: marker,\n enumerable: false,\n configurable: true,\n writable: false,\n })\n return marker\n}\n\nconst clearSchedulerSendFailureState = (\n error: object,\n marker: SchedulerSendFailureMarker,\n): void => {\n if (marker.pending.length === 0 && marker.active.length === 0) {\n Reflect.deleteProperty(error, SCHEDULER_SEND_FAILURE)\n }\n}\n\n/**\n * Classify an adapter error before throwing it. Unclassified adapter failures\n * are retryable because the caller cannot know whether a remote send landed.\n */\nexport function classifySchedulerSendFailure(\n error: unknown,\n disposition: SchedulerSendFailureDisposition,\n): unknown {\n const classified = schedulerFailureObject(error)\n schedulerSendFailureState(classified).pending.push(disposition)\n return classified\n}\n\n/** Consume a pending adapter classification and count this rejected send. */\nexport function markSchedulerSendFailure(error: unknown): unknown {\n const marked = schedulerFailureObject(error)\n const marker = schedulerSendFailureState(marked)\n marker.active.push(marker.pending.pop() ?? 'retryable')\n return marked\n}\n\nexport function consumeSchedulerSendFailure(\n error: unknown,\n): SchedulerSendFailureDisposition | undefined {\n if (!objectLike(error)) return undefined\n const marker = schedulerSendFailureMarker(error)\n const disposition = marker?.active.pop()\n if (marker !== undefined) clearSchedulerSendFailureState(error, marker)\n return disposition\n}\n\nconst isDrainableServer = (value: unknown): value is DrainableServer => {\n if (value === null || typeof value !== 'object') return false\n const contract = Reflect.get(value, 'contract')\n return (\n contract !== null &&\n typeof contract === 'object' &&\n typeof Reflect.get(contract, 'name') === 'string' &&\n typeof Reflect.get(value, 'drain') === 'function'\n )\n}\n\n/**\n * Validate the server list captured by a scheduler route at construction time.\n * Built-in adapters keep their low-level target seam structural for custom\n * integrations. The application-facing `schedulerHandler` uses strict mode\n * so only servers made by this A2 copy reach a configured adapter.\n */\nexport function assertSchedulerTargets(\n servers: readonly unknown[],\n options: { allowStructural: boolean },\n): asserts servers is readonly DrainableServer[] {\n const contracts = new Set<string>()\n for (const target of servers) {\n if (!isDrainableServer(target)) {\n throw new TypeError('a2 scheduler: expected a server from createServer()')\n }\n const name = target.contract.name\n if (contracts.has(name)) {\n throw new TypeError(\n `a2 scheduler: duplicate server contract '${name}' in one handler`,\n )\n }\n contracts.add(name)\n\n const binding = serverSchedulerBindings.get(target)\n if (!binding) {\n if (!options.allowStructural) {\n throw new TypeError(\n `a2 scheduler: server for contract '${name}' was not created by createServer()`,\n )\n }\n continue\n }\n }\n}\n\n/** Require every real server to share one exact configured scheduler. */\nexport function assertServerSchedulerBindings(\n scheduler: A2Scheduler,\n servers: readonly DrainableServer[],\n): void {\n for (const server of servers) {\n const binding = serverSchedulerBindings.get(server)\n if (!binding?.scheduler) {\n throw new TypeError(\n `a2 scheduler: server for contract '${server.contract.name}' has no scheduler configured`,\n )\n }\n if (binding.scheduler !== scheduler) {\n throw new TypeError(\n `a2 scheduler: server for contract '${server.contract.name}' is configured with a different scheduler instance`,\n )\n }\n }\n}\n\n/** Resolve the scheduler privately bound to a real A2 server. */\nexport function schedulerForServer(server: unknown): A2Scheduler {\n if (!isDrainableServer(server)) {\n throw new TypeError('a2 scheduler: expected a server from createServer()')\n }\n const binding = serverSchedulerBindings.get(server)\n if (!binding) {\n throw new TypeError(\n `a2 scheduler: server for contract '${server.contract.name}' was not created by createServer()`,\n )\n }\n if (!binding.scheduler) {\n throw new TypeError(\n `a2 scheduler: server for contract '${server.contract.name}' has no scheduler configured`,\n )\n }\n return binding.scheduler\n}\n\n/**\n * Drain timing knobs. Mutable only as a white-box test seam. Claim\n * heartbeats run on real timers, so tests shrink these to keep suites\n * fast. Production always uses the defaults.\n */\nexport const DRAIN_TIMINGS: {\n /** How long a claim lives without renewal. */\n claimTtlMs: number\n /** Heartbeat cadence while a handler is running. */\n claimHeartbeatMs: number\n /** Base delay before retrying a failed renewal while the lease lives. */\n renewRetryMs: number\n /** Delay between a claim's expiry and its scheduler watchdog. */\n watchdogGraceMs: number\n /** Maximum time a top-level append waits for its initial scheduler arm. */\n schedulerArmTimeoutMs: number\n} = {\n claimTtlMs: 5_000,\n claimHeartbeatMs: 2_000,\n renewRetryMs: 400,\n watchdogGraceMs: 1_000,\n schedulerArmTimeoutMs: 2_000,\n}\n\n/**\n * Stream liveness knobs — the server-side SSE heartbeat and the\n * client-side stall watchdog. Paired by design: the client declares a\n * connection dead after roughly two missed heartbeats. Mutable only as\n * a white-box test seam; production always uses the defaults.\n */\n/**\n * Poll cadence for the poll-based store streams (sqlite, postgres) —\n * owned, not configurable: adaptive polling removed the operator's\n * reason to tune it (the floor keeps active streams smooth; the\n * ceiling bounds idle cost). Mutable only as a white-box test seam so\n * suites don't wait out real idle gaps.\n */\nexport const POLL_TIMINGS: {\n /** Gap between polls while a session is producing events. */\n activeFloorMs: number\n /** The idle ceiling — the longest gap between polls. */\n idleCeilingMs: number\n} = {\n activeFloorMs: 25,\n idleCeilingMs: 250,\n}\n\n/**\n * Safety-read cadence for the notify-driven redis live stream. A\n * parked feed re-reads the store at this interval even without a\n * pub/sub wake-up, so a lost notification (subscriber reconnect gap,\n * dropped PUBLISH) costs latency, never an event. Mutable only as a\n * white-box test seam.\n */\nexport const NOTIFY_TIMINGS: {\n safetyReadMs: number\n} = {\n safetyReadMs: 10_000,\n}\n\nexport const STREAM_TIMINGS: {\n /** Cadence of `: ping` comment frames from `sseResponse`. */\n sseHeartbeatMs: number\n /** How far ahead of an invocation deadline an SSE response closes. */\n sseDeadlineGraceMs: number\n /** Client: silence longer than this means the connection is dead. */\n stallTimeoutMs: number\n /** Client reconnect backoff: first retry delay, doubling per attempt. */\n reconnectBaseMs: number\n /** Client reconnect backoff ceiling. */\n reconnectMaxMs: number\n} = {\n sseHeartbeatMs: 15_000,\n sseDeadlineGraceMs: 1_000,\n stallTimeoutMs: 35_000,\n reconnectBaseMs: 500,\n reconnectMaxMs: 5_000,\n}\n\n/**\n * WebSocket down-pump backpressure — `sessionsSocket` parks its pull\n * loop while the socket reports more buffered bytes than the\n * high-water mark and re-checks on the poll cadence, so a slow reader\n * applies backpressure to the store subscription instead of buffering\n * without bound in the socket. Acks and heartbeats stay exempt: they\n * are small and correctness-relevant. Mutable only as a white-box test\n * seam.\n */\nexport const SOCKET_TIMINGS: {\n highWaterMarkBytes: number\n resumePollMs: number\n /** Up-lane bound: concurrent push/presence handlers per socket. POST\n * gets per-request platform limits for free; the socket must bring\n * its own. Over the cap, pushes are answered with a retryable error\n * ack and presence frames are shed (that plane repaints). */\n maxInFlightMessages: number\n /** Hard ceiling on `bufferedAmount`: past it the peer is a\n * non-reader (acks and pings are park-exempt, so only a peer that\n * never reads can reach it) and the socket closes 1008. */\n disconnectWaterMarkBytes: number\n} = {\n highWaterMarkBytes: 1_048_576,\n resumePollMs: 25,\n maxInFlightMessages: 32,\n disconnectWaterMarkBytes: 8_388_608,\n}\n\n/**\n * Client presence send cadence — a leading send immediately, then at\n * most one merged send per interval (~15Hz). Owned, not configurable\n * (decision #36's rule): coupling the cadence to the POST ack tied\n * remote cursor smoothness to RTT, and the ack serves no correctness\n * purpose on a fire-and-forget LWW plane. Mutable only as a white-box\n * test seam.\n */\nexport const PRESENCE_TIMINGS: {\n sendIntervalMs: number\n} = {\n sendIntervalMs: 66,\n}\n"],"mappings":"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"}
|
package/dist/otel.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel.d.ts","names":[],"sources":["../src/otel.ts"],"mappings":";;;KAiBY;;EAEV,SAAS;;EAET;;iBAac,KAAK,UAAS,cAAmB"}
|
package/dist/otel.js
CHANGED
package/dist/otel.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel.js","names":[],"sources":["../src/otel.ts"],"sourcesContent":["/**\n * experimental-a2/otel — OpenTelemetry adapter for the A2Telemetry interface.\n *\n * Peer-depends on `@opentelemetry/api` (optional). Spans nest through\n * the active context, so with a context manager registered (any real\n * OTel setup, e.g. `@vercel/otel`) the happy path lands in one trace:\n * request → a2.append → a2.drain → a2.event → the handler's own spans.\n */\n\nimport {\n SpanStatusCode,\n trace,\n type Span,\n type Tracer,\n} from '@opentelemetry/api'\nimport type { A2Telemetry } from './telemetry.ts'\n\nexport type OtelOptions = {\n /** Bring your own tracer; defaults to `trace.getTracer(tracerName)`. */\n tracer?: Tracer\n /** Tracer name for the global provider. Default `'a2'`. */\n tracerName?: string\n}\n\nconst fail = (span: Span, error: unknown): void => {\n span.recordException(\n error instanceof Error ? error : new Error(String(error)),\n )\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n })\n}\n\nexport function otel(options: OtelOptions = {}): A2Telemetry {\n const tracer = options.tracer ?? trace.getTracer(options.tracerName ?? 'a2')\n\n return {\n span(name, attributes, fn) {\n return tracer.startActiveSpan(name, { attributes }, async (span) => {\n try {\n return await fn({\n setAttribute: (key, value) => {\n span.setAttribute(key, value)\n },\n recordError: (error) => {\n fail(span, error)\n },\n })\n } catch (error) {\n fail(span, error)\n throw error\n } finally {\n span.end()\n }\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;;AAwBA,MAAM,QAAQ,MAAY,UAAyB;CACjD,KAAK,gBACH,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAC1D;CACA,KAAK,UAAU;EACb,MAAM,eAAe;EACrB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CAChE,CAAC;AACH;AAEA,SAAgB,KAAK,UAAuB,CAAC,GAAgB;CAC3D,MAAM,SAAS,QAAQ,UAAU,MAAM,UAAU,QAAQ,cAAc,IAAI;CAE3E,OAAO,EACL,KAAK,MAAM,YAAY,IAAI;EACzB,OAAO,OAAO,gBAAgB,MAAM,EAAE,WAAW,GAAG,OAAO,SAAS;GAClE,IAAI;IACF,OAAO,MAAM,GAAG;KACd,eAAe,KAAK,UAAU;MAC5B,KAAK,aAAa,KAAK,KAAK;KAC9B;KACA,cAAc,UAAU;MACtB,KAAK,MAAM,KAAK;KAClB;IACF,CAAC;GACH,SAAS,OAAO;IACd,KAAK,MAAM,KAAK;IAChB,MAAM;GACR,UAAU;IACR,KAAK,IAAI;GACX;EACF,CAAC;CACH,EACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-B4TnJtWu.js","names":[],"sources":["../src/platform.ts"],"sourcesContent":["/**\n * Platform seams, read from the ambient request context — the same\n * channel `@vercel/functions` uses (`Symbol.for('@vercel/request-context')`)\n * — with no dependency on that package. Where no context exists (local\n * dev, long-lived servers, other clouds) everything degrades cleanly:\n * `waitUntil` becomes a no-op (background work is tracked in-process\n * anyway) and the deadline is unknown (claim watchdogs use their ordinary\n * TTL window).\n *\n * Tests exercise these paths by installing a real context object on the\n * symbol — simulating the platform contract, not mocking a2.\n */\n\ntype RequestContext = {\n headers?: Readonly<Record<string, string>>\n waitUntil?: (promise: Promise<unknown>) => void\n /**\n * When the platform will terminate this invocation (epoch ms, ISO\n * string, or Date) — on Vercel this includes `waitUntil` work.\n */\n deadline?: number | string | Date\n}\n\nconst SYMBOL_FOR_REQ_CONTEXT = Symbol.for('@vercel/request-context')\n\nfunction requestContext(): RequestContext {\n try {\n const holder = Reflect.get(globalThis, SYMBOL_FOR_REQ_CONTEXT) as\n { get?: () => RequestContext | undefined } | undefined\n return holder?.get?.() ?? {}\n } catch {\n return {}\n }\n}\n\n/** The current invocation's Vercel deployment identity, when available. */\nexport function platformVercelOidcToken(): string | null {\n try {\n const token = requestContext().headers?.['x-vercel-oidc-token']\n return typeof token === 'string' && token.length > 0 ? token : null\n } catch {\n return null\n }\n}\n\nexport function platformWaitUntil(promise: Promise<unknown>): void {\n try {\n requestContext().waitUntil?.(promise)\n } catch {\n // Best-effort — the promise is tracked in-process either way.\n }\n}\n\n/** The invocation's termination time in epoch ms, or null if unknowable. */\nexport function invocationDeadlineMs(): number | null {\n const deadline = requestContext().deadline\n if (deadline === undefined || deadline === null) return null\n const ms = new Date(deadline as number | string | Date).getTime()\n return Number.isNaN(ms) ? null : ms\n}\n"],"mappings":";AAuBA,MAAM,yBAAyB,OAAO,IAAI,yBAAyB;AAEnE,SAAS,iBAAiC;CACxC,IAAI;EAGF,OAFe,QAAQ,IAAI,YAAY,sBAE3B,CAAC,EAAE,MAAM,KAAK,CAAC;CAC7B,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAGA,SAAgB,0BAAyC;CACvD,IAAI;EACF,MAAM,QAAQ,eAAe,CAAC,CAAC,UAAU;EACzC,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;CACjE,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,kBAAkB,SAAiC;CACjE,IAAI;EACF,eAAe,CAAC,CAAC,YAAY,OAAO;CACtC,QAAQ,CAER;AACF;;AAGA,SAAgB,uBAAsC;CACpD,MAAM,WAAW,eAAe,CAAC,CAAC;CAClC,IAAI,aAAa,KAAA,KAAa,aAAa,MAAM,OAAO;CACxD,MAAM,KAAK,IAAI,KAAK,QAAkC,CAAC,CAAC,QAAQ;CAChE,OAAO,OAAO,MAAM,EAAE,IAAI,OAAO;AACnC"}
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as EventDefs, c as PresencePatch, d as WithPresence, o as PresenceDefs, p as Reducer, r as ContractEvent, s as PresenceMap, t as AppendInput } from "./contract-jIfaR085.js";
|
|
2
|
-
import { a as Connection, c as
|
|
2
|
+
import { a as Connection, c as HistoryState, l as LoadHistoryOptions, t as A2Client, u as PushResult } from "./client-D7mvIXrF.js";
|
|
3
3
|
import { ReactElement, ReactNode } from "react";
|
|
4
4
|
//#region src/react.d.ts
|
|
5
5
|
type SessionProviderProps<D extends EventDefs, S> = {
|
|
@@ -42,7 +42,7 @@ type UseSessionPresence<P extends PresenceDefs> = {
|
|
|
42
42
|
type UseSessionResult<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = WithPresence<P, UseSessionPresence<P>> & {
|
|
43
43
|
/** The live view, folded through the shared reducer. */
|
|
44
44
|
state: S;
|
|
45
|
-
/** The raw
|
|
45
|
+
/** The raw event feed — observed, seeded, or backscrolled. */
|
|
46
46
|
events: ContractEvent<D>[];
|
|
47
47
|
/** The stream frontier — `lastSeenIndex` for cancellation. */
|
|
48
48
|
index: number;
|
|
@@ -58,6 +58,14 @@ type UseSessionResult<D extends EventDefs, S, P extends PresenceDefs = Record<ne
|
|
|
58
58
|
* back. Rejects with the server's A2Error codes.
|
|
59
59
|
*/
|
|
60
60
|
push: (...events: AppendInput<D>[]) => PushResult<D>;
|
|
61
|
+
/**
|
|
62
|
+
* Backscroll: fetch a bounded slice of older events into `events`.
|
|
63
|
+
* Defaults page backward 50 at a time from the oldest loaded event.
|
|
64
|
+
* Display data only — `state` and the optimistic overlay never move.
|
|
65
|
+
*/
|
|
66
|
+
loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>;
|
|
67
|
+
/** Backscroll progress: `complete` means the feed reaches index 1. */
|
|
68
|
+
history: HistoryState;
|
|
61
69
|
};
|
|
62
70
|
type A2React<D extends EventDefs, S, P extends PresenceDefs = Record<never, never>> = {
|
|
63
71
|
SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement;
|
|
@@ -74,4 +82,5 @@ declare function createReact<D extends EventDefs, S, P extends PresenceDefs = Re
|
|
|
74
82
|
reducer: Reducer<D, S, P>;
|
|
75
83
|
}): A2React<D, S, P>;
|
|
76
84
|
//#endregion
|
|
77
|
-
export { A2React, BoundA2React, BoundSessionProviderProps, SessionProviderProps, UseSessionPresence, UseSessionResult, createReact };
|
|
85
|
+
export { A2React, BoundA2React, BoundSessionProviderProps, SessionProviderProps, UseSessionPresence, UseSessionResult, createReact };
|
|
86
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;;KA8CY,qBAAqB,UAAU,WAAW;;EAEpD;;EAEA;;EAEA,cAAc;;EAEd;;EAEA,gBAAgB,cAAc;;;;;;EAM9B;EACA,WAAW;;KAGD,0BAA0B,UAAU,WAAW,KAAK,KAC9D,qBAAqB,GAAG;;;;;;KASd,mBAAmB,UAAU;;EAEvC,UAAU,YAAY;;;;;;;;EAQtB,cAAc,QAAQ,cAAc;;KAG1B,iBACV,UAAU,WACV,GACA,UAAU,eAAe,wBACvB,aAAa,GAAG,mBAAmB;;EAErC,OAAO;;EAEP,QAAQ,cAAc;;EAEtB;;;;;;EAMA,YAAY;;;;;;EAMZ,UAAU,QAAQ,YAAY,SAAS,WAAW;;;;;;EAMlD,cAAc,UAAU,uBAAuB,QAAQ,cAAc;;EAErE,SAAS;;KAGC,QACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,qBAAqB,GAAG,OAAO;EACxD,kBAAkB,iBAAiB,GAAG,GAAG;;KAG/B,aACV,UAAU,WACV,GACA,UAAU,eAAe;EAEzB,kBAAkB,OAAO,0BAA0B,GAAG,OAAO;EAC7D,kBAAkB,iBAAiB,GAAG,GAAG;;iBAG3B,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,QAAQ,SAAS,GAAG,GAAG;IAAO,aAAa,GAAG,GAAG;iBAC9C,YACd,UAAU,WACV,GACA,UAAU,eAAe,sBACzB;EAAW,SAAS,QAAQ,GAAG,GAAG;IAAO,QAAQ,GAAG,GAAG"}
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as createClient } from "./client-
|
|
2
|
+
import { t as createClient } from "./client-BKlyLiOU.js";
|
|
3
3
|
import { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
4
4
|
//#region src/react.ts
|
|
5
5
|
/**
|
|
@@ -80,6 +80,8 @@ function createReact(options) {
|
|
|
80
80
|
index: snapshot.index,
|
|
81
81
|
connection: snapshot.connection,
|
|
82
82
|
push: client.push,
|
|
83
|
+
loadHistory: client.loadHistory,
|
|
84
|
+
history: snapshot.history,
|
|
83
85
|
...setPresence && presence ? {
|
|
84
86
|
presence,
|
|
85
87
|
setPresence
|
|
@@ -94,3 +96,5 @@ function createReact(options) {
|
|
|
94
96
|
}
|
|
95
97
|
//#endregion
|
|
96
98
|
export { createReact };
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["'use client'\n/**\n * experimental-a2/react — React bindings over experimental-a2/client.\n *\n * `createReact({ client })` is a factory (like createContext): call it\n * once in a `'use client'` module with a shared A2 client and export the\n * bound pair. Server components import `SessionProvider` from that module\n * as a client reference; client components import `useSession` from the\n * same file. The shared client also makes imperative pushes and provider\n * renders resolve the same live session. Everything is typed by the\n * reducer value — no type arguments, and nothing here ever touches the\n * machine module.\n */\n\nimport {\n createContext,\n createElement,\n useContext,\n useEffect,\n useMemo,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport {\n createClient,\n type A2Client,\n type Connection,\n type HistoryState,\n type LoadHistoryOptions,\n type PushResult,\n type SessionClient,\n type SessionClientPresence,\n type SessionSnapshot,\n} from './client.ts'\nimport type {\n AppendInput,\n ContractEvent,\n EventDefs,\n PresenceDefs,\n PresenceMap,\n PresencePatch,\n WithPresence,\n} from './contract.ts'\nimport type { Reducer } from './reducer.ts'\n\nexport type SessionProviderProps<D extends EventDefs, S> = {\n /** Which session to subscribe to. */\n sessionId: string\n /** Base path of the route exposing GET (stream) and POST (push). */\n api: string\n /** The server-rendered fold — the first paint, no client JS needed. */\n initialState: S\n /** The fold's frontier — the stream resumes exactly there. */\n initialIndex: number\n /** Server-rendered event history through `initialIndex`. */\n initialEvents?: ContractEvent<D>[]\n /**\n * This client's presence identity — required to call `setPresence`.\n * Caller-supplied (a user id, a tab nonce, a guest name): A2 does\n * not invent an identity story.\n */\n participant?: string\n children?: ReactNode\n}\n\nexport type BoundSessionProviderProps<D extends EventDefs, S> = Omit<\n SessionProviderProps<D, S>,\n 'api'\n>\n\n/**\n * The presence members of the hook result — intersected in via\n * `WithPresence`, so they exist exactly when the contract declares\n * presence fields (the reducer is the client's typed handle on it).\n */\nexport type UseSessionPresence<P extends PresenceDefs> = {\n /** The replicated presence map, this client included (local echo). */\n presence: PresenceMap<P>\n /**\n * Fire-and-forget: validated locally, echoed into the map\n * immediately, coalesced on the wire to a fixed cadence (a leading\n * send immediately, then at most one merged send per interval;\n * later values win per field), stamped with the frontier at send\n * time as `seen`. Requires the provider's `participant`.\n */\n setPresence: (values: PresencePatch<P>['values']) => void\n}\n\nexport type UseSessionResult<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = WithPresence<P, UseSessionPresence<P>> & {\n /** The live view, folded through the shared reducer. */\n state: S\n /** The raw event feed — observed, seeded, or backscrolled. */\n events: ContractEvent<D>[]\n /** The stream frontier — `lastSeenIndex` for cancellation. */\n index: number\n /**\n * The connection, as a discriminated union — `error` exists only\n * while disconnected; \"reconnecting…\" is\n * `status === 'connecting' && reconnects > 0`.\n */\n connection: Connection\n /**\n * Typed optimistic append. Awaiting it gives the server ack;\n * `.confirmed` resolves when the live stream has delivered the batch\n * back. Rejects with the server's A2Error codes.\n */\n push: (...events: AppendInput<D>[]) => PushResult<D>\n /**\n * Backscroll: fetch a bounded slice of older events into `events`.\n * Defaults page backward 50 at a time from the oldest loaded event.\n * Display data only — `state` and the optimistic overlay never move.\n */\n loadHistory: (options?: LoadHistoryOptions) => Promise<ContractEvent<D>[]>\n /** Backscroll progress: `complete` means the feed reaches index 1. */\n history: HistoryState\n}\n\nexport type A2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n SessionProvider: (props: SessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, S, P>\n}\n\nexport type BoundA2React<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n> = {\n SessionProvider: (props: BoundSessionProviderProps<D, S>) => ReactElement\n useSession: () => UseSessionResult<D, S, P>\n}\n\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(options: { client: A2Client<D, S, P> }): BoundA2React<D, S, P>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(options: { reducer: Reducer<D, S, P> }): A2React<D, S, P>\nexport function createReact<\n D extends EventDefs,\n S,\n P extends PresenceDefs = Record<never, never>,\n>(\n options: { client: A2Client<D, S, P> } | { reducer: Reducer<D, S, P> },\n): BoundA2React<D, S, P> | A2React<D, S, P> {\n const Context = createContext<SessionClient<D, S, P> | null>(null)\n const providerMounts = new WeakMap<SessionClient<D, S, P> & object, number>()\n const resolveSession: (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => SessionClient<D, S, P> =\n 'client' in options\n ? (\n sessionId,\n _api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ) =>\n options.client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n : (() => {\n const clients = new Map<string, A2Client<D, S, P>>()\n return (\n sessionId: string,\n api: string | undefined,\n initialState: S,\n initialIndex: number,\n initialEvents: ContractEvent<D>[] | undefined,\n participant: string | undefined,\n ) => {\n if (api === undefined) {\n throw new TypeError(\n 'SessionProvider requires api when createReact binds a reducer',\n )\n }\n let client = clients.get(api)\n if (!client) {\n client = createClient({ reducer: options.reducer, api })\n clients.set(api, client)\n }\n return client.session(sessionId, {\n initialState,\n initialIndex,\n ...(initialEvents === undefined ? {} : { initialEvents }),\n ...(participant === undefined ? {} : { participant }),\n })\n }\n })()\n\n function SessionProvider(\n props: SessionProviderProps<D, S> | BoundSessionProviderProps<D, S>,\n ): ReactElement {\n const {\n sessionId,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n } = props\n const api = 'api' in props ? props.api : undefined\n const client = useMemo(\n () =>\n resolveSession(\n sessionId,\n api,\n initialState,\n initialIndex,\n initialEvents,\n participant,\n ),\n [sessionId, api, initialState, initialIndex, initialEvents, participant],\n )\n useEffect(() => {\n const mounts = providerMounts.get(client) ?? 0\n providerMounts.set(client, mounts + 1)\n if (mounts === 0) client.connect()\n return () => {\n const remaining = (providerMounts.get(client) ?? 1) - 1\n if (remaining === 0) {\n providerMounts.delete(client)\n client.close()\n } else {\n providerMounts.set(client, remaining)\n }\n }\n }, [client])\n return createElement(Context.Provider, { value: client }, props.children)\n }\n\n function useSession(): UseSessionResult<D, S, P> {\n const client = useContext(Context)\n if (!client) {\n throw new Error(\n 'useSession must be rendered inside its matching SessionProvider',\n )\n }\n const snapshot: SessionSnapshot<D, S, P> = useSyncExternalStore(\n client.subscribe,\n client.getSnapshot,\n client.getSnapshot,\n )\n return useMemo(() => {\n // Runtime mirror of the WithPresence surface: the client attaches\n // setPresence (and the snapshot carries presence) exactly when the\n // reducer declares presence fields — no runtime-inert members.\n const { setPresence } = client as Partial<SessionClientPresence<P>>\n const { presence } = snapshot as Partial<{ presence: PresenceMap<P> }>\n return {\n state: snapshot.state,\n events: snapshot.events,\n index: snapshot.index,\n connection: snapshot.connection,\n push: client.push,\n loadHistory: client.loadHistory,\n history: snapshot.history,\n ...(setPresence && presence ? { presence, setPresence } : {}),\n } as UseSessionResult<D, S, P>\n }, [snapshot, client])\n }\n\n return { SessionProvider, useSession }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAsJA,SAAgB,YAKd,SAC0C;CAC1C,MAAM,UAAU,cAA6C,IAAI;CACjE,MAAM,iCAAiB,IAAI,QAAiD;CAC5E,MAAM,iBAQJ,YAAY,WAEN,WACA,MACA,cACA,cACA,eACA,gBAEA,QAAQ,OAAO,QAAQ,WAAW;EAChC;EACA;EACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;EACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CACrD,CAAC,WACI;EACL,MAAM,0BAAU,IAAI,IAA+B;EACnD,QACE,WACA,KACA,cACA,cACA,eACA,gBACG;GACH,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,UACR,+DACF;GAEF,IAAI,SAAS,QAAQ,IAAI,GAAG;GAC5B,IAAI,CAAC,QAAQ;IACX,SAAS,aAAa;KAAE,SAAS,QAAQ;KAAS;IAAI,CAAC;IACvD,QAAQ,IAAI,KAAK,MAAM;GACzB;GACA,OAAO,OAAO,QAAQ,WAAW;IAC/B;IACA;IACA,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;IACvD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;GACrD,CAAC;EACH;CACF,EAAA,CAAG;CAET,SAAS,gBACP,OACc;EACd,MAAM,EACJ,WACA,cACA,cACA,eACA,gBACE;EACJ,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,KAAA;EACzC,MAAM,SAAS,cAEX,eACE,WACA,KACA,cACA,cACA,eACA,WACF,GACF;GAAC;GAAW;GAAK;GAAc;GAAc;GAAe;EAAW,CACzE;EACA,gBAAgB;GACd,MAAM,SAAS,eAAe,IAAI,MAAM,KAAK;GAC7C,eAAe,IAAI,QAAQ,SAAS,CAAC;GACrC,IAAI,WAAW,GAAG,OAAO,QAAQ;GACjC,aAAa;IACX,MAAM,aAAa,eAAe,IAAI,MAAM,KAAK,KAAK;IACtD,IAAI,cAAc,GAAG;KACnB,eAAe,OAAO,MAAM;KAC5B,OAAO,MAAM;IACf,OACE,eAAe,IAAI,QAAQ,SAAS;GAExC;EACF,GAAG,CAAC,MAAM,CAAC;EACX,OAAO,cAAc,QAAQ,UAAU,EAAE,OAAO,OAAO,GAAG,MAAM,QAAQ;CAC1E;CAEA,SAAS,aAAwC;EAC/C,MAAM,SAAS,WAAW,OAAO;EACjC,IAAI,CAAC,QACH,MAAM,IAAI,MACR,iEACF;EAEF,MAAM,WAAqC,qBACzC,OAAO,WACP,OAAO,aACP,OAAO,WACT;EACA,OAAO,cAAc;GAInB,MAAM,EAAE,gBAAgB;GACxB,MAAM,EAAE,aAAa;GACrB,OAAO;IACL,OAAO,SAAS;IAChB,QAAQ,SAAS;IACjB,OAAO,SAAS;IAChB,YAAY,SAAS;IACrB,MAAM,OAAO;IACb,aAAa,OAAO;IACpB,SAAS,SAAS;IAClB,GAAI,eAAe,WAAW;KAAE;KAAU;IAAY,IAAI,CAAC;GAC7D;EACF,GAAG,CAAC,UAAU,MAAM,CAAC;CACvB;CAEA,OAAO;EAAE;EAAiB;CAAW;AACvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retryable-lazy-DZWmHpii.js","names":[],"sources":["../src/retryable-lazy.ts"],"sourcesContent":["/**\n * One lazy in-flight attempt: concurrent callers share it, fulfilled values\n * stay cached, and rejected attempts are evicted so a later call can retry.\n */\nexport type RetryableLazy<T> = {\n get(): Promise<T>\n peek(): Promise<T> | null\n}\n\nexport const retryableLazy = <T>(\n initialize: () => Promise<T>,\n): RetryableLazy<T> => {\n let current: Promise<T> | null = null\n\n const get = (): Promise<T> => {\n if (current) return current\n\n const attempt = Promise.resolve().then(initialize)\n current = attempt\n void attempt.catch(() => {\n if (current === attempt) current = null\n })\n return attempt\n }\n\n return { get, peek: () => current }\n}\n"],"mappings":";AASA,MAAa,iBACX,eACqB;CACrB,IAAI,UAA6B;CAEjC,MAAM,YAAwB;EAC5B,IAAI,SAAS,OAAO;EAEpB,MAAM,UAAU,QAAQ,QAAQ,CAAC,CAAC,KAAK,UAAU;EACjD,UAAU;EACV,QAAa,YAAY;GACvB,IAAI,YAAY,SAAS,UAAU;EACrC,CAAC;EACD,OAAO;CACT;CAEA,OAAO;EAAE;EAAK,YAAY;CAAQ;AACpC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as Clock } from "./store-
|
|
2
|
-
import { D as SchedulerTask, t as A2Scheduler } from "./server-
|
|
1
|
+
import { i as Clock } from "./store-DysUkTH3.js";
|
|
2
|
+
import { D as SchedulerTask, t as A2Scheduler } from "./server-DUF9pjsx.js";
|
|
3
3
|
//#region src/scheduler-qstash.d.ts
|
|
4
4
|
type QStashStoreOutage = {
|
|
5
5
|
version: 1;
|
|
@@ -75,4 +75,5 @@ type QStashSchedulerOptions = {
|
|
|
75
75
|
};
|
|
76
76
|
declare function qstash(options?: QStashSchedulerOptions): A2Scheduler;
|
|
77
77
|
//#endregion
|
|
78
|
-
export { QStashClientPublishRequest, QStashPublishRequest, QStashPublishingClient, QStashSchedulerOptions, QStashTransport, QStashVerifyRequest, qstash };
|
|
78
|
+
export { QStashClientPublishRequest, QStashPublishRequest, QStashPublishingClient, QStashSchedulerOptions, QStashTransport, QStashVerifyRequest, qstash };
|
|
79
|
+
//# sourceMappingURL=scheduler-qstash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-qstash.d.ts","names":[],"sources":["../src/scheduler-qstash.ts"],"mappings":";;;KAmCK;EACH;;EAEA;;EAEA;;KAGG,gBAAgB;;EAEnB,YAAY;;KAGF;EACV;EACA,MAAM;;EAEN,UAAU,SAAS;;EAEnB;IACE;;;EAGF;EACA;;EAEA;;;KAIU,6BAA6B,KACvC;EAGA,UAAU;EACV;IACE;;;;KAKQ;EACV,YAAY,SAAS,6BAA6B;;KAGxC;EACV;;EAEA;;EAEA;EACA;;;KAIU;EACV,QAAQ,SAAS,uBAAuB;EACxC,OAAO,SAAS,sBAAsB;;KAG5B;;EAEV;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,UAAU,SAAS;;EAEnB,SAAS;;EAET,YAAY;;EAEZ,QAAQ;;iBA4eM,OAAO,UAAS,yBAA8B"}
|
package/dist/scheduler-qstash.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as A2Error } from "./errors-BQuJpe82.js";
|
|
2
|
-
import { f as classifySchedulerSendFailure, t as DRAIN_TIMINGS, u as assertSchedulerTargets, v as serverInternals } from "./internal-
|
|
3
|
-
import { n as deliverSchedulerAppend } from "./server-
|
|
2
|
+
import { f as classifySchedulerSendFailure, t as DRAIN_TIMINGS, u as assertSchedulerTargets, v as serverInternals } from "./internal-DstsI6Re.js";
|
|
3
|
+
import { n as deliverSchedulerAppend } from "./server-C72KOw51.js";
|
|
4
4
|
import { n as platformVercelOidcToken } from "./platform-B4TnJtWu.js";
|
|
5
5
|
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
|
|
6
6
|
import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
|
|
@@ -497,3 +497,5 @@ function qstash(options = {}) {
|
|
|
497
497
|
}
|
|
498
498
|
//#endregion
|
|
499
499
|
export { qstash };
|
|
500
|
+
|
|
501
|
+
//# sourceMappingURL=scheduler-qstash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-qstash.js","names":[],"sources":["../src/scheduler-qstash.ts"],"sourcesContent":["/**\n * experimental-a2/scheduler-qstash — HTTP scheduler over Upstash QStash.\n *\n * QStash publishes one signed HTTP delivery per scheduler task. Drain tasks\n * use an absolute, whole-second `notBefore` and a fixed-size deduplication id;\n * append tasks hash their stable task id with the callback URL.\n * Store-outage successors carry a signed private generation and back off to\n * one day without deriving their deduplication identity from the local clock.\n * `handler(...servers)` verifies the signature over the raw request body\n * before parsing it, then applies the same drain protocol as the Vercel\n * scheduler adapter.\n *\n * `@upstash/qstash` is an optional peer dependency, imported lazily. The\n * structural `transport` option keeps its types out of A2's public surface and\n * gives tests a provider-faithful injection seam.\n */\n\nimport {\n assertSchedulerTargets,\n classifySchedulerSendFailure,\n DRAIN_TIMINGS,\n serverInternals,\n type DrainResult,\n} from './internal.ts'\nimport { A2Error } from './errors.ts'\nimport { platformVercelOidcToken } from './platform.ts'\nimport { retryableLazy } from './retryable-lazy.ts'\nimport { normalizeSchedulerTask, type SchedulerTask } from './scheduler-task.ts'\nimport {\n deliverSchedulerAppend,\n type A2Scheduler,\n type DrainableServer,\n} from './server.ts'\nimport { SYSTEM_CLOCK, type Clock } from './store.ts'\n\ntype QStashStoreOutage = {\n version: 1\n /** Zero is the first successor created for a store outage. */\n generation: number\n /** The first failing drain task's due time. Append identity lives on the task. */\n rootDueAt?: number\n}\n\ntype QStashMessage = SchedulerTask & {\n /** Provider-private state authenticated as part of QStash's signed body. */\n _a2QStash?: QStashStoreOutage\n}\n\nexport type QStashPublishRequest = {\n url: string\n body: QStashMessage\n /** Headers the transport must deliver to the callback. */\n headers?: Readonly<Record<string, string>>\n /** Header names the transport must redact from provider dashboard/API views. */\n redact?: {\n header: readonly string[]\n }\n /** Whole-second Unix timestamp. Omitted for an immediate delivery. */\n notBefore?: number\n deduplicationId: string\n /** Delivery retries after the initial attempt. */\n retries?: number\n}\n\n/** The request shape passed to an injected `@upstash/qstash` client. */\nexport type QStashClientPublishRequest = Omit<\n QStashPublishRequest,\n 'headers' | 'redact'\n> & {\n headers?: Record<string, string>\n redact?: {\n header: string[]\n }\n}\n\n/** The outbound part of `@upstash/qstash` used by the built-in transport. */\nexport type QStashPublishingClient = {\n publishJSON(request: QStashClientPublishRequest): Promise<unknown>\n}\n\nexport type QStashVerifyRequest = {\n signature: string\n /** The untouched request body covered by the signature. */\n body: string\n /** Exact destination URL, checked against the signature subject. */\n url: string\n upstashRegion?: string\n}\n\n/** The small part of `@upstash/qstash` this adapter needs. */\nexport type QStashTransport = {\n publish(request: QStashPublishRequest): Promise<unknown>\n verify(request: QStashVerifyRequest): Promise<boolean>\n}\n\nexport type QStashSchedulerOptions = {\n /** Exact URL mounted with `schedulerHandler(...)`. Inferred in local dev and on Vercel. */\n url?: string\n /** QStash API token. Default: `QSTASH_TOKEN`. */\n token?: string\n /** QStash signing key. Default: `QSTASH_CURRENT_SIGNING_KEY`. */\n currentSigningKey?: string\n /** Next QStash signing key. Default: `QSTASH_NEXT_SIGNING_KEY`. */\n nextSigningKey?: string\n /** Delivery retries after the initial attempt. Default: provider default. */\n retries?: number\n /** Maximum callback body and outbound task with outage headroom. Default 1 MiB. */\n maxBodyBytes?: number\n /** Static callback headers. QStash receives them; A2 requests dashboard/API redaction. */\n headers?: Readonly<Record<string, string>>\n /** Injectable publishing client. The built-in receiver still verifies callbacks. */\n client?: QStashPublishingClient\n /** Injectable transport (tests). Default: `@upstash/qstash`. */\n transport?: QStashTransport\n /** Injectable clock for due-time calculations (tests). */\n clock?: Clock\n}\n\nconst qstashTransport = async (\n options: Pick<\n QStashSchedulerOptions,\n 'token' | 'currentSigningKey' | 'nextSigningKey' | 'client'\n >,\n developmentMode: boolean | undefined,\n): Promise<QStashTransport> => {\n const sdk = await import('@upstash/qstash').catch(() => {\n throw classifySchedulerSendFailure(\n new Error(\n \"experimental-a2/scheduler-qstash needs the '@upstash/qstash' package (optional peer dependency) — install it, or inject a transport\",\n ),\n 'terminal',\n )\n })\n const developmentOverride =\n developmentMode === undefined ? {} : { devMode: developmentMode }\n const client: QStashPublishingClient =\n options.client ??\n new sdk.Client({\n ...(options.token === undefined ? {} : { token: options.token }),\n ...developmentOverride,\n })\n const receiver = new sdk.Receiver({\n ...(options.currentSigningKey === undefined\n ? {}\n : { currentSigningKey: options.currentSigningKey }),\n ...(options.nextSigningKey === undefined\n ? {}\n : { nextSigningKey: options.nextSigningKey }),\n ...developmentOverride,\n })\n return {\n publish: (request) => {\n const clientRequest: QStashClientPublishRequest = {\n url: request.url,\n body: request.body,\n ...(request.headers === undefined\n ? {}\n : { headers: { ...request.headers } }),\n ...(request.redact === undefined\n ? {}\n : { redact: { header: [...request.redact.header] } }),\n deduplicationId: request.deduplicationId,\n ...(request.retries === undefined ? {} : { retries: request.retries }),\n ...(request.notBefore === undefined\n ? {}\n : { notBefore: request.notBefore }),\n }\n return client.publishJSON(clientRequest)\n },\n verify: (request) => receiver.verify(request),\n }\n}\n\nconst digestId = async (\n parts: readonly (string | number)[],\n): Promise<string> => {\n const digest = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(JSON.stringify(parts)),\n )\n const hex = Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, '0'),\n ).join('')\n return `a2_${hex}`\n}\n\ntype ParsedQStashMessage = {\n task: SchedulerTask\n outage?: QStashStoreOutage\n}\n\nconst recordOf = (value: unknown): Record<string, unknown> | null =>\n value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null\n\nconst TRUSTED_SOURCE_HEADER = 'x-vercel-trusted-oidc-idp-token'\nconst TRUSTED_SOURCE_DELIVERY_MARGIN_MS = 60_000\nconst RESERVED_CALLBACK_HEADERS = new Set([\n 'connection',\n 'content-length',\n 'host',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n 'x-vercel-oidc-token',\n TRUSTED_SOURCE_HEADER,\n])\n\nconst callbackHeaders = (\n value: Readonly<Record<string, string>> | undefined,\n): Readonly<Record<string, string>> | undefined => {\n if (value === undefined) return undefined\n\n let entries: [string, string][]\n try {\n entries = Object.entries(value)\n } catch {\n throw new TypeError('qstash() headers must be an HTTP header record')\n }\n\n const normalized: [string, string][] = []\n const seen = new Set<string>()\n for (const [name, headerValue] of entries) {\n if (typeof headerValue !== 'string') {\n throw new TypeError('qstash() header values must be strings')\n }\n let entry: [string, string] | undefined\n try {\n entry = [...new Headers([[name, headerValue]]).entries()][0]\n } catch {\n throw new TypeError(\n 'qstash() headers must contain valid HTTP header names and values',\n )\n }\n if (entry === undefined) {\n throw new TypeError(\n 'qstash() headers must contain valid HTTP header names and values',\n )\n }\n const [normalizedName, normalizedValue] = entry\n if (seen.has(normalizedName)) {\n throw new TypeError(\n `qstash() headers contain duplicate name '${normalizedName}'`,\n )\n }\n if (\n normalizedName.startsWith('upstash-') ||\n normalizedName.startsWith('content-type') ||\n RESERVED_CALLBACK_HEADERS.has(normalizedName)\n ) {\n throw new TypeError(\n `qstash() headers cannot set reserved header '${normalizedName}'`,\n )\n }\n seen.add(normalizedName)\n normalized.push([normalizedName, normalizedValue])\n }\n\n return normalized.length === 0\n ? undefined\n : Object.freeze(Object.fromEntries(normalized))\n}\n\nconst publishHeaders = (\n configured: Readonly<Record<string, string>> | undefined,\n generated: Pick<QStashPublishRequest, 'headers' | 'redact'> | undefined,\n): Pick<QStashPublishRequest, 'headers' | 'redact'> | undefined => {\n if (configured === undefined && generated === undefined) return undefined\n\n const headers = new Headers()\n const redacted = new Set<string>()\n for (const [name, value] of Object.entries(configured ?? {})) {\n headers.set(name, value)\n redacted.add(name)\n }\n for (const [name, value] of Object.entries(generated?.headers ?? {})) {\n headers.set(name, value)\n redacted.add(name)\n }\n for (const name of generated?.redact?.header ?? []) {\n const normalized = [...new Headers([[name, 'redacted']]).keys()][0]\n if (normalized === undefined) {\n throw new TypeError('qstash generated an invalid redaction header')\n }\n redacted.add(normalized)\n }\n\n const headerEntries = [...headers.entries()]\n return {\n ...(headerEntries.length === 0\n ? {}\n : { headers: Object.fromEntries(headerEntries) }),\n ...(redacted.size === 0\n ? {}\n : { redact: { header: [...redacted].toSorted() } }),\n }\n}\n\nconst jwtExpirationMs = (token: string): number | null => {\n const parts = token.split('.')\n const payloadPart = parts.length === 3 ? parts[1] : undefined\n if (\n payloadPart === undefined ||\n payloadPart.length === 0 ||\n !/^[A-Za-z0-9_-]+$/.test(payloadPart) ||\n payloadPart.length % 4 === 1\n ) {\n return null\n }\n\n try {\n const decode = globalThis.atob\n if (typeof decode !== 'function') return null\n const base64 = payloadPart\n .replaceAll('-', '+')\n .replaceAll('_', '/')\n .padEnd(payloadPart.length + ((4 - (payloadPart.length % 4)) % 4), '=')\n const binary = decode(base64)\n const bytes = Uint8Array.from(binary, (character) =>\n character.charCodeAt(0),\n )\n const payload = recordOf(JSON.parse(new TextDecoder().decode(bytes)))\n const expiration = payload?.['exp']\n if (\n typeof expiration !== 'number' ||\n !Number.isSafeInteger(expiration) ||\n expiration <= 0 ||\n expiration > Number.MAX_SAFE_INTEGER / 1_000\n ) {\n return null\n }\n return expiration * 1_000\n } catch {\n return null\n }\n}\n\nconst previewTrustedSource = (\n firstDeliveryAt: number,\n): Pick<QStashPublishRequest, 'headers' | 'redact'> | undefined => {\n const token = platformVercelOidcToken()\n if (token === null) return undefined\n const expiration = jwtExpirationMs(token)\n if (\n expiration === null ||\n firstDeliveryAt + TRUSTED_SOURCE_DELIVERY_MARGIN_MS > expiration\n ) {\n return undefined\n }\n return {\n headers: { [TRUSTED_SOURCE_HEADER]: token },\n redact: { header: [TRUSTED_SOURCE_HEADER] },\n }\n}\n\nconst RETRYABLE_HTTP_CLIENT_STATUSES = new Set([408, 409, 425, 429])\n\nconst httpStatusOf = (error: unknown): number | undefined => {\n try {\n const record = recordOf(error)\n if (!record) return undefined\n const response = recordOf(record['response'])\n const statuses = [\n record['status'],\n record['statusCode'],\n response?.['status'],\n ].filter(\n (value): value is number =>\n typeof value === 'number' &&\n Number.isInteger(value) &&\n value >= 100 &&\n value <= 599,\n )\n const [status] = statuses\n if (status === undefined) return undefined\n return statuses.every((candidate) => candidate === status)\n ? status\n : undefined\n } catch {\n return undefined\n }\n}\n\nconst classifyQStashPublishFailure = (error: unknown): unknown => {\n const status = httpStatusOf(error)\n const terminal =\n status !== undefined &&\n status >= 400 &&\n status < 500 &&\n !RETRYABLE_HTTP_CLIENT_STATUSES.has(status)\n return classifySchedulerSendFailure(\n error,\n terminal ? 'terminal' : 'retryable',\n )\n}\n\nconst parseTask = (body: string): ParsedQStashMessage | null => {\n let value: unknown\n try {\n value = JSON.parse(body)\n } catch {\n return null\n }\n const task = normalizeSchedulerTask(value)\n const record = recordOf(value)\n if (!task || !record) return null\n\n const encodedOutage = record['_a2QStash']\n if (encodedOutage === undefined) return { task }\n const outage = recordOf(encodedOutage)\n const generation = outage?.['generation']\n if (\n !outage ||\n outage['version'] !== 1 ||\n typeof generation !== 'number' ||\n !Number.isSafeInteger(generation) ||\n generation < 0 ||\n generation >= Number.MAX_SAFE_INTEGER\n ) {\n return null\n }\n\n const rootDueAt = outage['rootDueAt']\n if (task.kind === 'drain') {\n if (\n typeof rootDueAt !== 'number' ||\n !Number.isInteger(rootDueAt) ||\n rootDueAt % 1_000 !== 0\n ) {\n return null\n }\n } else if (rootDueAt !== undefined) {\n return null\n }\n\n return {\n task,\n outage: {\n version: 1,\n generation,\n ...(rootDueAt === undefined ? {} : { rootDueAt }),\n },\n }\n}\n\nconst DEFAULT_MAX_BODY_BYTES = 1024 * 1024\nconst STORE_OUTAGE_BASE_DELAY_MS = 30_000\nconst STORE_OUTAGE_MAX_DELAY_MS = 24 * 60 * 60 * 1_000\n\nconst readBoundedBody = async (\n request: Request,\n maxBytes: number,\n): Promise<string | null> => {\n const declared = request.headers.get('content-length')\n if (\n declared !== null &&\n /^\\d+$/.test(declared) &&\n Number(declared) > maxBytes\n ) {\n return null\n }\n\n if (request.body === null) return ''\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let length = 0\n try {\n for (;;) {\n // oxlint-disable-next-line no-await-in-loop\n const { done, value } = await reader.read()\n if (done) break\n length += value.byteLength\n if (length > maxBytes) {\n // oxlint-disable-next-line no-await-in-loop\n await reader.cancel().catch(() => {})\n return null\n }\n chunks.push(value)\n }\n } finally {\n reader.releaseLock()\n }\n\n const bytes = new Uint8Array(length)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(bytes)\n}\n\nconst storeUnavailable = (error: unknown): error is A2Error =>\n error instanceof A2Error && error.code === 'STORE_UNAVAILABLE'\n\nconst response = {\n ok: (): Response => new Response('ok'),\n unauthorized: (): Response => new Response('unauthorized', { status: 401 }),\n malformed: (): Response =>\n new Response('invalid a2 scheduler message', {\n status: 489,\n headers: { 'Upstash-NonRetryable-Error': 'true' },\n }),\n tooLarge: (): Response =>\n new Response('a2 scheduler message is too large', {\n status: 489,\n headers: { 'Upstash-NonRetryable-Error': 'true' },\n }),\n}\n\nconst CALLBACK_PATH = '/api/a2/scheduler'\nconst DEFAULT_DEVELOPMENT_PORT = 3_000\n\nconst developmentCallbackUrl = (portValue: string | undefined): string => {\n const port =\n portValue === undefined || portValue === ''\n ? DEFAULT_DEVELOPMENT_PORT\n : Number(portValue)\n if (\n !Number.isSafeInteger(port) ||\n port < 1 ||\n port > 65_535 ||\n (portValue !== undefined && portValue !== '' && !/^\\d+$/.test(portValue))\n ) {\n throw new TypeError(\n 'qstash() could not infer url: PORT must be an integer from 1 to 65535 for local development',\n )\n }\n return `http://localhost:${String(port)}${CALLBACK_PATH}`\n}\n\ntype CallbackResolution = {\n callbackUrl: string\n /** An SDK override. Undefined preserves the SDK's environment behavior. */\n developmentMode?: boolean\n inferredVercelPreview?: true\n}\n\nconst inferCallbackUrl = (): CallbackResolution => {\n const env = typeof process === 'undefined' ? undefined : process.env\n const vercelEnv = env?.['VERCEL_ENV']\n const variable =\n env?.['VERCEL'] !== '1'\n ? null\n : vercelEnv === 'production'\n ? 'VERCEL_PROJECT_PRODUCTION_URL'\n : vercelEnv === 'preview'\n ? 'VERCEL_BRANCH_URL'\n : null\n if (variable !== null) {\n const host = env?.[variable]\n if (!host) {\n throw new TypeError(\n `qstash() could not infer url: ${variable} is missing`,\n )\n }\n let origin: URL\n try {\n origin = new URL(`https://${host}`)\n } catch {\n throw new TypeError(\n `qstash() could not infer url: ${variable} must be a hostname`,\n )\n }\n if (\n origin.host !== host ||\n origin.pathname !== '/' ||\n origin.search !== '' ||\n origin.hash !== '' ||\n origin.username !== '' ||\n origin.password !== '' ||\n origin.port !== ''\n ) {\n throw new TypeError(\n `qstash() could not infer url: ${variable} must be a hostname`,\n )\n }\n return {\n callbackUrl: new URL(CALLBACK_PATH, origin).toString(),\n developmentMode: false,\n ...(vercelEnv === 'preview' ? { inferredVercelPreview: true } : {}),\n }\n }\n\n if (env === undefined) {\n throw new TypeError(\n 'qstash() needs url when the runtime cannot identify local development',\n )\n }\n\n if (env['NODE_ENV'] === 'production') {\n throw new TypeError(\n 'qstash() needs url in production outside a Vercel production or preview deployment',\n )\n }\n\n return {\n callbackUrl: developmentCallbackUrl(env['PORT']),\n developmentMode: true,\n }\n}\n\nexport function qstash(options: QStashSchedulerOptions = {}): A2Scheduler {\n const env = typeof process === 'undefined' ? undefined : process.env\n const resolution: CallbackResolution =\n options.url === undefined\n ? inferCallbackUrl()\n : {\n callbackUrl: options.url,\n ...(env?.['NODE_ENV'] === 'production'\n ? { developmentMode: false }\n : {}),\n }\n const { callbackUrl, developmentMode, inferredVercelPreview } = resolution\n let parsedUrl: URL\n try {\n parsedUrl = new URL(callbackUrl)\n } catch {\n throw new TypeError('qstash() needs an absolute callback url')\n }\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError('qstash() callback url must use http or https')\n }\n if (options.client !== undefined && options.transport !== undefined) {\n throw new TypeError('qstash() cannot combine client and transport')\n }\n if (options.client !== undefined && options.token !== undefined) {\n throw new TypeError('qstash() cannot combine client and token')\n }\n const url = parsedUrl.toString()\n const configuredHeaders = callbackHeaders(options.headers)\n const { retries } = options\n if (\n retries !== undefined &&\n (!Number.isSafeInteger(retries) || retries < 0)\n ) {\n throw new TypeError('qstash() retries must be a non-negative integer')\n }\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES\n if (!Number.isSafeInteger(maxBodyBytes) || maxBodyBytes < 1) {\n throw new TypeError('qstash() maxBodyBytes must be a positive integer')\n }\n const clock = options.clock ?? SYSTEM_CLOCK\n\n // Lazy: construction does no I/O and does not require the optional peer.\n const transport = retryableLazy(() =>\n options.transport\n ? Promise.resolve(options.transport)\n : qstashTransport(options, developmentMode),\n ).get\n\n const storeOutageDeduplicationId = (\n task: SchedulerTask,\n outage: QStashStoreOutage,\n ): Promise<string> => {\n if (task.kind === 'append') {\n return digestId([\n 'a2:scheduler-qstash:store-outage:append:v1',\n url,\n task.contract,\n task.sessionId,\n task.id,\n outage.generation,\n ])\n }\n const { rootDueAt } = outage\n if (rootDueAt === undefined) {\n throw new TypeError('qstash store-outage drain needs a root dueAt')\n }\n return digestId([\n 'a2:scheduler-qstash:store-outage:drain:v1',\n url,\n task.contract,\n task.sessionId,\n rootDueAt,\n outage.generation,\n ])\n }\n\n const sendTask = async (\n task: SchedulerTask,\n outage?: QStashStoreOutage,\n ): Promise<void> => {\n const nowMs = clock.now().getTime()\n const requestedDueAt = task.dueAt\n if (!Number.isFinite(requestedDueAt)) {\n throw classifySchedulerSendFailure(\n new TypeError('qstash scheduler dueAt must be a finite number'),\n 'terminal',\n )\n }\n const dueAt =\n requestedDueAt <= nowMs\n ? Math.floor(nowMs / 1_000) * 1_000\n : Math.ceil(requestedDueAt / 1_000) * 1_000\n const normalizedTask: SchedulerTask =\n task.kind === 'append'\n ? {\n version: 1,\n kind: 'append',\n id: task.id,\n contract: task.contract,\n sessionId: task.sessionId,\n dueAt,\n events: task.events,\n }\n : {\n version: 1,\n kind: 'drain',\n contract: task.contract,\n sessionId: task.sessionId,\n dueAt,\n }\n const message: QStashMessage = {\n ...normalizedTask,\n ...(outage === undefined ? {} : { _a2QStash: outage }),\n }\n const sizeCheckedMessage: QStashMessage =\n outage === undefined\n ? {\n ...normalizedTask,\n _a2QStash:\n normalizedTask.kind === 'append'\n ? { version: 1, generation: Number.MAX_SAFE_INTEGER }\n : {\n version: 1,\n generation: Number.MAX_SAFE_INTEGER,\n rootDueAt: dueAt,\n },\n }\n : message\n let bodyBytes: number\n try {\n bodyBytes = new TextEncoder().encode(\n JSON.stringify(sizeCheckedMessage),\n ).byteLength\n } catch (error) {\n throw classifySchedulerSendFailure(error, 'terminal')\n }\n if (bodyBytes > maxBodyBytes) {\n throw classifySchedulerSendFailure(\n new RangeError('qstash scheduler message exceeds maxBodyBytes'),\n 'terminal',\n )\n }\n const deduplicationId =\n outage !== undefined\n ? await storeOutageDeduplicationId(task, outage)\n : task.kind === 'append'\n ? await digestId(['a2:scheduler-qstash:append:v1', url, task.id])\n : await digestId([\n 'a2:scheduler-qstash:v1',\n url,\n task.contract,\n task.sessionId,\n dueAt,\n ])\n const trustedSource =\n inferredVercelPreview === true\n ? previewTrustedSource(Math.max(nowMs, dueAt))\n : undefined\n const deliveryHeaders = publishHeaders(configuredHeaders, trustedSource)\n const t = await transport()\n try {\n await t.publish({\n url,\n body: message,\n ...deliveryHeaders,\n ...(dueAt <= nowMs ? {} : { notBefore: dueAt / 1_000 }),\n deduplicationId,\n ...(retries === undefined ? {} : { retries }),\n })\n } catch (error) {\n throw classifyQStashPublishFailure(error)\n }\n }\n\n const sendStoreOutageSuccessor = async (\n task: SchedulerTask,\n outage?: QStashStoreOutage,\n ): Promise<void> => {\n const generation = (outage?.generation ?? -1) + 1\n const exponent = Math.min(generation, 6)\n const delayMs = Math.min(\n STORE_OUTAGE_MAX_DELAY_MS,\n STORE_OUTAGE_BASE_DELAY_MS * 4 ** exponent,\n )\n const nowMs = clock.now().getTime()\n const dueAt = Math.ceil((nowMs + delayMs) / 1_000) * 1_000\n const nextOutage: QStashStoreOutage =\n task.kind === 'append'\n ? { version: 1, generation }\n : { version: 1, generation, rootDueAt: outage?.rootDueAt ?? task.dueAt }\n await sendTask({ ...task, dueAt }, nextOutage)\n }\n\n const successorDueAt = (message: { dueAt: number }): number => {\n const minimumDueAt =\n clock.now().getTime() +\n DRAIN_TIMINGS.claimTtlMs +\n DRAIN_TIMINGS.watchdogGraceMs\n const beats = Math.max(\n 1,\n Math.ceil(\n (minimumDueAt - message.dueAt) / DRAIN_TIMINGS.claimHeartbeatMs,\n ),\n )\n return message.dueAt + beats * DRAIN_TIMINGS.claimHeartbeatMs\n }\n\n return {\n async schedule(task) {\n await sendTask(task)\n },\n\n handler(...servers: DrainableServer[]) {\n assertSchedulerTargets(servers, { allowStructural: true })\n const byContract = new Map<string, DrainableServer>()\n for (const server of servers) {\n byContract.set(server.contract.name, server)\n }\n return async (req: Request): Promise<Response> => {\n const signature = req.headers.get('upstash-signature')\n if (!signature) return response.unauthorized()\n\n const body = await readBoundedBody(req, maxBodyBytes)\n if (body === null) return response.tooLarge()\n const upstashRegion = req.headers.get('upstash-region')\n const t = await transport()\n try {\n const valid = await t.verify({\n signature,\n body,\n url,\n ...(upstashRegion === null ? {} : { upstashRegion }),\n })\n if (!valid) return response.unauthorized()\n } catch {\n return response.unauthorized()\n }\n\n const parsed = parseTask(body)\n if (!parsed) return response.malformed()\n const { task, outage } = parsed\n\n const server = byContract.get(task.contract)\n if (!server) {\n throw new Error(\n `a2 scheduler: no server for contract '${task.contract}' — pass it to schedulerHandler(...)`,\n )\n }\n const internals = serverInternals.get(server)\n if (task.kind === 'append') {\n try {\n await deliverSchedulerAppend(server, task)\n } catch (error) {\n if (!storeUnavailable(error)) throw error\n await sendStoreOutageSuccessor(task, outage)\n }\n return response.ok()\n }\n\n const nextDueAt = successorDueAt(task)\n let result: DrainResult | { settled: boolean; outcome: 'unknown' }\n try {\n result = internals\n ? await internals.schedulerDrain(task.sessionId, {\n watchdogDueAt: nextDueAt,\n })\n : {\n ...(await server.drain(task.sessionId)),\n outcome: 'unknown' as const,\n }\n } catch (error) {\n if (!storeUnavailable(error)) throw error\n await sendStoreOutageSuccessor(task, outage)\n return response.ok()\n }\n if (result.settled) return response.ok()\n\n if (result.outcome === 'busy') {\n if (result.schedulerArm) await result.schedulerArm\n else {\n await sendTask({\n version: 1,\n kind: 'drain',\n contract: task.contract,\n sessionId: task.sessionId,\n dueAt: nextDueAt,\n })\n }\n return response.ok()\n }\n\n // QStash's per-message retry default can be lower than A2's failure\n // budget. Hand stalled work to a fresh durable message so recovery\n // continues until the store reports success or dead-letter. If this\n // publish fails, the throw keeps the current delivery retryable.\n await sendTask({\n version: 1,\n kind: 'drain',\n contract: task.contract,\n sessionId: task.sessionId,\n dueAt: nextDueAt,\n })\n return response.ok()\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-task-BpzhPnRS.js","names":[],"sources":["../src/scheduler-task.ts"],"sourcesContent":["/** One event carried by a delayed append task. */\nexport type ScheduledEvent = {\n id: string\n type: string\n payload: unknown\n}\n\n/** A durable wake-up for pending handler work. */\nexport type SchedulerDrainTask = {\n version: 1\n kind: 'drain'\n contract: string\n sessionId: string\n /** Earliest useful delivery as epoch milliseconds. */\n dueAt: number\n}\n\n/** A durable delayed append. */\nexport type SchedulerAppendTask = {\n version: 1\n kind: 'append'\n /** Stable identity for provider-level deduplication. */\n id: string\n contract: string\n sessionId: string\n /** Earliest useful delivery as epoch milliseconds. */\n dueAt: number\n events: readonly ScheduledEvent[]\n}\n\n/** The versioned message protocol shared by scheduler adapters. */\nexport type SchedulerTask = SchedulerDrainTask | SchedulerAppendTask\n\nconst recordOf = (value: unknown): Record<string, unknown> | null =>\n value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null\n\nconst validName = (value: unknown): value is string =>\n typeof value === 'string' && value.length > 0\n\nconst validDueAt = (value: unknown): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value % 1_000 === 0\n\nconst baseTask = (\n record: Record<string, unknown>,\n): { contract: string; sessionId: string } | null =>\n Object.hasOwn(record, 'contract') &&\n Object.hasOwn(record, 'sessionId') &&\n validName(record['contract']) &&\n validName(record['sessionId'])\n ? { contract: record['contract'], sessionId: record['sessionId'] }\n : null\n\n/**\n * Validate one delivered task.\n * Adapters call this only after their transport authentication has succeeded.\n */\nexport function normalizeSchedulerTask(value: unknown): SchedulerTask | null {\n const record = recordOf(value)\n if (!record) return null\n const base = baseTask(record)\n if (!base) return null\n\n if (record['version'] !== 1) return null\n if (record['kind'] === 'drain') {\n const dueAt = record['dueAt']\n if (!validDueAt(dueAt)) return null\n return { version: 1, kind: 'drain', ...base, dueAt }\n }\n if (record['kind'] !== 'append') return null\n\n const { dueAt, events, id } = record\n if (!validName(id) || !validDueAt(dueAt)) return null\n if (!Array.isArray(events) || events.length === 0) return null\n const parsedEvents: ScheduledEvent[] = []\n for (const event of events) {\n const item = recordOf(event)\n if (\n !item ||\n typeof item['id'] !== 'string' ||\n typeof item['type'] !== 'string' ||\n !Object.hasOwn(item, 'payload')\n ) {\n return null\n }\n parsedEvents.push({\n id: item['id'],\n type: item['type'],\n payload: item['payload'],\n })\n }\n if (\n new Set(parsedEvents.map((event) => event.id)).size !== parsedEvents.length\n ) {\n return null\n }\n return {\n version: 1,\n kind: 'append',\n id,\n ...base,\n dueAt,\n events: parsedEvents,\n }\n}\n"],"mappings":";AAiCA,MAAM,YAAY,UAChB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC9D,QACD;AAEN,MAAM,aAAa,UACjB,OAAO,UAAU,YAAY,MAAM,SAAS;AAE9C,MAAM,cAAc,UAClB,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ,QAAU;AAE5E,MAAM,YACJ,WAEA,OAAO,OAAO,QAAQ,UAAU,KAChC,OAAO,OAAO,QAAQ,WAAW,KACjC,UAAU,OAAO,WAAW,KAC5B,UAAU,OAAO,YAAY,IACzB;CAAE,UAAU,OAAO;CAAa,WAAW,OAAO;AAAa,IAC/D;;;;;AAMN,SAAgB,uBAAuB,OAAsC;CAC3E,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,OAAO,SAAS,MAAM;CAC5B,IAAI,CAAC,MAAM,OAAO;CAElB,IAAI,OAAO,eAAe,GAAG,OAAO;CACpC,IAAI,OAAO,YAAY,SAAS;EAC9B,MAAM,QAAQ,OAAO;EACrB,IAAI,CAAC,WAAW,KAAK,GAAG,OAAO;EAC/B,OAAO;GAAE,SAAS;GAAG,MAAM;GAAS,GAAG;GAAM;EAAM;CACrD;CACA,IAAI,OAAO,YAAY,UAAU,OAAO;CAExC,MAAM,EAAE,OAAO,QAAQ,OAAO;CAC9B,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,WAAW,KAAK,GAAG,OAAO;CACjD,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG,OAAO;CAC1D,MAAM,eAAiC,CAAC;CACxC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,SAAS,KAAK;EAC3B,IACE,CAAC,QACD,OAAO,KAAK,UAAU,YACtB,OAAO,KAAK,YAAY,YACxB,CAAC,OAAO,OAAO,MAAM,SAAS,GAE9B,OAAO;EAET,aAAa,KAAK;GAChB,IAAI,KAAK;GACT,MAAM,KAAK;GACX,SAAS,KAAK;EAChB,CAAC;CACH;CACA,IACE,IAAI,IAAI,aAAa,KAAK,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,aAAa,QAErE,OAAO;CAET,OAAO;EACL,SAAS;EACT,MAAM;EACN;EACA,GAAG;EACH;EACA,QAAQ;CACV;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as Clock } from "./store-
|
|
2
|
-
import { D as SchedulerTask, t as A2Scheduler } from "./server-
|
|
1
|
+
import { i as Clock } from "./store-DysUkTH3.js";
|
|
2
|
+
import { D as SchedulerTask, t as A2Scheduler } from "./server-DUF9pjsx.js";
|
|
3
3
|
//#region src/scheduler-vercel.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* The "not settled yet" signal for a stalled handler — an expected state,
|
|
@@ -50,4 +50,5 @@ type VercelQueuesOptions = {
|
|
|
50
50
|
};
|
|
51
51
|
declare function vercelQueues(options?: VercelQueuesOptions): A2Scheduler;
|
|
52
52
|
//#endregion
|
|
53
|
-
export { DrainUnsettled, QueueDeliveryMetadata, QueueTransport, VercelQueuesOptions, vercelQueues };
|
|
53
|
+
export { DrainUnsettled, QueueDeliveryMetadata, QueueTransport, VercelQueuesOptions, vercelQueues };
|
|
54
|
+
//# sourceMappingURL=scheduler-vercel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-vercel.d.ts","names":[],"sources":["../src/scheduler-vercel.ts"],"mappings":";;;;;;;;;;;;cAqDa,uBAAuB;KAExB;EACV;EACA;;;KAYU;EACV,KACE,eACA,SAAS,eACT;IACE;IACA;IACA;KAGF;IACE;MAED;;;;;;;;EAQH,eACE,UACE,kBACA,UAAU,0BACP,iBACH,KAAK,YAAY,QAAQ;;EAE7B,YAAY;;KAGF;;EAEV;;;;;EAKA;;EAEA,YAAY;;EAEZ,QAAQ;;iBA+JM,aAAa,UAAS,sBAA2B"}
|
package/dist/scheduler-vercel.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { f as classifySchedulerSendFailure, t as DRAIN_TIMINGS, u as assertSchedulerTargets, v as serverInternals } from "./internal-
|
|
2
|
-
import { n as deliverSchedulerAppend } from "./server-
|
|
1
|
+
import { f as classifySchedulerSendFailure, t as DRAIN_TIMINGS, u as assertSchedulerTargets, v as serverInternals } from "./internal-DstsI6Re.js";
|
|
2
|
+
import { n as deliverSchedulerAppend } from "./server-C72KOw51.js";
|
|
3
3
|
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
|
|
4
4
|
import { n as SYSTEM_CLOCK } from "./store-N8PXxDAS.js";
|
|
5
5
|
import { t as normalizeSchedulerTask } from "./scheduler-task-BpzhPnRS.js";
|
|
@@ -224,3 +224,5 @@ function vercelQueues(options = {}) {
|
|
|
224
224
|
}
|
|
225
225
|
//#endregion
|
|
226
226
|
export { DrainUnsettled, vercelQueues };
|
|
227
|
+
|
|
228
|
+
//# sourceMappingURL=scheduler-vercel.js.map
|