experimental-a2 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/dist/{ai-B4YhEnfw.d.ts → ai-D_PGS-JR.d.ts} +3 -2
- package/dist/ai-D_PGS-JR.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 +2 -2
- 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 +3 -2
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +2 -0
- package/dist/otel.js.map +1 -0
- package/dist/platform-B4TnJtWu.js +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-YtPq7hjw.d.ts → server-DpvjhdoE.d.ts} +6 -5
- package/dist/server-DpvjhdoE.d.ts.map +1 -0
- package/dist/{server-CcNnFnoW.js → server-Duw6MVlB.js} +112 -54
- package/dist/server-Duw6MVlB.js.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 → telemetry-CpeclqB2.d.ts} +4 -3
- package/dist/telemetry-CpeclqB2.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 +54 -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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler-vercel.js","names":[],"sources":["../src/scheduler-vercel.ts"],"sourcesContent":["/**\n * experimental-a2/scheduler-vercel — queue-backed scheduler over Vercel Queues.\n *\n * `vercelQueues()` returns an `A2Scheduler` (a2-implementation.md §9):\n *\n * - drain tasks round the requested watchdog time up to a one-second slot.\n * One message per `(topic, contract, session, dueAt)` coalesces appends,\n * claim renewals and racing callbacks. `DuplicateMessageError` is\n * success because that due-time slot is already durable.\n * - append tasks hash their stable task id with the topic for provider\n * deduplication. Their six-day maximum delay leaves a full day for\n * delivery and retries inside the provider's seven-day retention.\n * They target the publishing deployment unless explicitly configured\n * as deploymentless. Drain tasks always remain deployment-scoped.\n * - `handler(...servers)` is the delivery route: look up the server for\n * the contract named in the message and drain it. Settled drains ack;\n * contention arms a later watchdog before acking; handler failure keeps\n * the current message and redelivers with backoff.\n *\n * `@vercel/queue` is an optional peer dependency, imported lazily. The\n * `transport` option is the injection seam — tests run a real little\n * in-memory queue through the same interface.\n */\n\nimport { createHash } from 'node:crypto'\nimport { SYSTEM_CLOCK, type Clock } from './store.ts'\nimport {\n assertSchedulerTargets,\n classifySchedulerSendFailure,\n DRAIN_TIMINGS,\n serverInternals,\n} from './internal.ts'\nimport { retryableLazy } from './retryable-lazy.ts'\nimport {\n normalizeSchedulerTask,\n type SchedulerDrainTask,\n type SchedulerTask,\n} from './scheduler-task.ts'\nimport {\n deliverSchedulerAppend,\n type A2Scheduler,\n type DrainableServer,\n} from './server.ts'\n\n/**\n * The \"not settled yet\" signal for a stalled handler — an expected state,\n * not a transport failure. The default transport classifies it in\n * `@vercel/queue`'s `retry` option and reschedules the delivery quietly\n * (`{ afterSeconds }` → visibility change → 200 response, no error log)\n * instead of letting it surface as a thrown 500. Busy deliveries use a\n * fresh watchdog instead. Custom transports should treat this class the\n * same way when their protocol has a polite reschedule.\n */\nexport class DrainUnsettled extends Error {}\n\nexport type QueueDeliveryMetadata = {\n messageId: string\n deliveryCount: number\n}\n\ntype VercelQueueMessage = SchedulerTask & {\n /** Provider-private state used to address callback lease operations. */\n _a2Vercel?: {\n version: 1\n deployment: 'deployment' | 'deploymentless'\n }\n}\n\n/** What this module needs from a Vercel Queues-compatible transport. */\nexport type QueueTransport = {\n send(\n topic: string,\n message: SchedulerTask,\n options?: {\n delaySeconds?: number\n idempotencyKey?: string\n retentionSeconds?: number\n },\n /** Resolved A2 affinity. Custom transports own its provider mapping. */\n routing?: {\n deployment: 'deployment' | 'deploymentless'\n },\n ): Promise<unknown>\n /**\n * Wrap the delivery handler into a route. The handler throws\n * `DrainUnsettled` when the session needs redelivery — transports\n * with a polite reschedule (visibility change) should use it there\n * instead of surfacing an error; anything else thrown is a genuine\n * failure and should stay loud.\n */\n handleCallback(\n handler: (\n message: unknown,\n metadata: QueueDeliveryMetadata,\n ) => Promise<void>,\n ): (req: Request) => Promise<Response>\n /** Is this send error the idempotency-key collision? (= success) */\n isDuplicate(error: unknown): boolean\n}\n\nexport type VercelQueuesOptions = {\n /** Queue topic matching `[A-Za-z0-9_-]+`. Default `'a2'`. */\n topic?: string\n /**\n * Deployment affinity for scheduled append tasks. Recovery drain tasks\n * always target the publishing deployment. Default `'deployment'`.\n */\n scheduledAppends?: 'deployment' | 'deploymentless'\n /** Injectable transport (tests). Default: `@vercel/queue`. */\n transport?: QueueTransport\n /** Injectable clock for due-time calculations (tests). */\n clock?: Clock\n}\n\n/** Vercel Queues rejects shorter visibility changes. */\nconst MIN_VISIBILITY_SECONDS = 30\n\n/** Vercel Queues' maximum message retention and delivery delay. */\nconst MAX_RETENTION_SECONDS = 7 * 24 * 60 * 60\n\n/** Leave the final retention day for delivery and retries. */\nconst MAX_DELAY_SECONDS = 6 * 24 * 60 * 60\n\nconst TOPIC_PATTERN = /^[A-Za-z0-9_-]+$/\nconst RETRYABLE_HTTP_CLIENT_STATUSES = new Set([408, 409, 425, 429])\n/** The SDK's send path maps these statuses to name-only error instances. */\nconst TERMINAL_VERCEL_SEND_ERROR_NAMES = new Set([\n 'BadRequestError',\n 'UnauthorizedError',\n 'ForbiddenError',\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 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 errorNameOf = (error: unknown): string | undefined => {\n try {\n const name = recordOf(error)?.['name']\n return typeof name === 'string' ? name : undefined\n } catch {\n return undefined\n }\n}\n\nconst classifyVercelSendFailure = (error: unknown): unknown => {\n const status = httpStatusOf(error)\n const terminal =\n status === undefined\n ? TERMINAL_VERCEL_SEND_ERROR_NAMES.has(errorNameOf(error) ?? '')\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 digestId = (parts: readonly string[]): string =>\n `a2_${createHash('sha256').update(JSON.stringify(parts)).digest('hex')}`\n\n/** Base of the unsettled redelivery backoff, doubled per delivery. */\nconst UNSETTLED_BACKOFF_BASE_SECONDS = 5\n\n/** Redelivery backoff for retry states: base, 2×, 4×… bounded. */\nconst unsettledBackoffSeconds = (deliveryCount: number): number =>\n Math.min(\n Math.max(\n UNSETTLED_BACKOFF_BASE_SECONDS * 2 ** Math.max(deliveryCount - 1, 0),\n MIN_VISIBILITY_SECONDS,\n ),\n 60,\n )\n\nconst vercelQueueTransport = async (): Promise<QueueTransport> => {\n const queue = await import('@vercel/queue').catch(() => {\n throw classifySchedulerSendFailure(\n new Error(\n \"experimental-a2/scheduler-vercel needs the '@vercel/queue' package (optional peer dependency) — install it, or inject a transport\",\n ),\n 'terminal',\n )\n })\n const deploymentClient = new queue.QueueClient()\n const deploymentlessClient = new queue.QueueClient({ deploymentId: null })\n return {\n send: (topic, message, options, routing) => {\n const deployment = routing?.deployment ?? 'deployment'\n const body: VercelQueueMessage = {\n ...message,\n _a2Vercel: { version: 1, deployment },\n }\n return (\n deployment === 'deploymentless'\n ? deploymentlessClient\n : deploymentClient\n ).send(topic, body, options)\n },\n handleCallback: (handler) => {\n const callbackOptions = {\n // A hard-killed callback normally has a fresh claim watchdog. If\n // its successor send also failed, make the old unacked delivery's\n // fallback as prompt as Vercel Queues permits.\n visibilityTimeoutSeconds: MIN_VISIBILITY_SECONDS,\n // Not-settled-yet is an expected state: reschedule the (still\n // unacked) delivery via a visibility change — 200, no error\n // log. Everything else returns undefined → the SDK's default\n // throw path, which is loud on purpose.\n retry: (error: unknown, metadata: QueueDeliveryMetadata) =>\n error instanceof DrainUnsettled\n ? {\n afterSeconds: unsettledBackoffSeconds(metadata.deliveryCount),\n }\n : undefined,\n }\n const deploymentHandler = deploymentClient.handleCallback(\n handler,\n callbackOptions,\n ) as (req: Request) => Promise<Response>\n const deploymentlessHandler = deploymentlessClient.handleCallback(\n handler,\n callbackOptions,\n ) as (req: Request) => Promise<Response>\n return async (req: Request): Promise<Response> => {\n let delivered: unknown\n try {\n delivered = await req.clone().json()\n } catch {\n return deploymentlessHandler(req)\n }\n const metadata = recordOf(recordOf(delivered)?.['_a2Vercel'])\n const routing =\n metadata?.['version'] === 1 ? metadata['deployment'] : undefined\n return routing === 'deployment'\n ? deploymentHandler(req)\n : deploymentlessHandler(req)\n }\n },\n isDuplicate: (error) => error instanceof queue.DuplicateMessageError,\n }\n}\n\nexport function vercelQueues(options: VercelQueuesOptions = {}): A2Scheduler {\n const topic = options.topic ?? 'a2'\n if (typeof topic !== 'string' || !TOPIC_PATTERN.test(topic)) {\n throw new TypeError('vercelQueues() topic must match [A-Za-z0-9_-]+')\n }\n const scheduledAppends = options.scheduledAppends ?? 'deployment'\n if (\n scheduledAppends !== 'deployment' &&\n scheduledAppends !== 'deploymentless'\n ) {\n throw new TypeError(\n \"vercelQueues() scheduledAppends must be 'deployment' or 'deploymentless'\",\n )\n }\n const clock = options.clock ?? SYSTEM_CLOCK\n\n // Lazy: constructing the scheduler value does no I/O and works without\n // the peer dep until the first task or delivery.\n const transport = retryableLazy(() =>\n options.transport\n ? Promise.resolve(options.transport)\n : vercelQueueTransport(),\n ).get\n\n const sendTask = async (task: SchedulerTask): Promise<void> => {\n const nowMs = clock.now().getTime()\n const requestedDueAt = task.dueAt\n if (!Number.isFinite(requestedDueAt)) {\n throw classifySchedulerSendFailure(\n new TypeError('vercel 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 deliveryTargetAt = task.kind === 'append' ? requestedDueAt : dueAt\n const deliveryDelaySeconds = Math.max(\n 0,\n Math.ceil((deliveryTargetAt - nowMs) / 1_000),\n )\n if (deliveryDelaySeconds > MAX_DELAY_SECONDS) {\n throw classifySchedulerSendFailure(\n new RangeError(\n 'vercelQueues() tasks cannot be delayed more than 6 days',\n ),\n 'terminal',\n )\n }\n const deployment = task.kind === 'append' ? scheduledAppends : 'deployment'\n const message: SchedulerTask = { ...task, dueAt }\n const idempotencyKey =\n task.kind === 'drain'\n ? digestId([\n 'a2:scheduler-vercel:drain:v1',\n topic,\n task.contract,\n task.sessionId,\n String(dueAt),\n ])\n : digestId(['a2:scheduler-vercel:append:v1', topic, task.id])\n const t = await transport()\n try {\n await t.send(\n topic,\n message,\n {\n delaySeconds: deliveryDelaySeconds,\n idempotencyKey,\n retentionSeconds: MAX_RETENTION_SECONDS,\n },\n { deployment },\n )\n } catch (err) {\n if (t.isDuplicate(err)) return\n throw classifyVercelSendFailure(err)\n }\n }\n\n const successorDueAt = (message: SchedulerDrainTask): 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 t = await transport()\n return t.handleCallback(async (delivered) => {\n const task = normalizeSchedulerTask(delivered)\n if (!task) {\n throw new TypeError('a2 scheduler received an invalid task')\n }\n const server = byContract.get(task.contract)\n if (!server) {\n // A message for a contract this route doesn't serve is a\n // wiring bug — throw so it redelivers and stays visible in\n // queue observability instead of vanishing on an ack.\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 await deliverSchedulerAppend(server, task)\n return\n }\n\n const nextDueAt = successorDueAt(task)\n const 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 if (result.settled) return\n\n if (result.outcome === 'busy') {\n // The live owner normally arms its own claim windows.\n // Ensure a later slot here as well before relinquishing this\n // durable delivery. If the queue is down, the throw leaves the\n // current message unacked for visibility-timeout redelivery.\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\n }\n\n // Handler failure keeps the current durable message and uses the\n // transport retry path. Dead-lettered sessions count as settled\n // above, so retries stop there.\n throw new DrainUnsettled(\n `a2 scheduler: session '${task.sessionId}' of '${task.contract}' not settled yet (${result.outcome}) — rescheduled for redelivery`,\n )\n })(req)\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,IAAa,iBAAb,cAAoC,MAAM,CAAC;;AA8D3C,MAAM,yBAAyB;;AAG/B,MAAM,wBAAwB;;AAG9B,MAAM,oBAAoB;AAE1B,MAAM,gBAAgB;AACtB,MAAM,iDAAiC,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;AAAG,CAAC;;AAEnE,MAAM,mDAAmC,IAAI,IAAI;CAC/C;CACA;CACA;AACF,CAAC;AAED,MAAM,YAAY,UAChB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC9D,QACD;AAEN,MAAM,gBAAgB,UAAuC;CAC3D,IAAI;EACF,MAAM,SAAS,SAAS,KAAK;EAC7B,IAAI,CAAC,QAAQ,OAAO,KAAA;EACpB,MAAM,WAAW,SAAS,OAAO,WAAW;EAC5C,MAAM,WAAW;GACf,OAAO;GACP,OAAO;GACP,WAAW;EACb,CAAC,CAAC,QACC,UACC,OAAO,UAAU,YACjB,OAAO,UAAU,KAAK,KACtB,SAAS,OACT,SAAS,GACb;EACA,MAAM,CAAC,UAAU;EACjB,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;EACjC,OAAO,SAAS,OAAO,cAAc,cAAc,MAAM,IACrD,SACA,KAAA;CACN,QAAQ;EACN;CACF;AACF;AAEA,MAAM,eAAe,UAAuC;CAC1D,IAAI;EACF,MAAM,OAAO,SAAS,KAAK,CAAC,GAAG;EAC/B,OAAO,OAAO,SAAS,WAAW,OAAO,KAAA;CAC3C,QAAQ;EACN;CACF;AACF;AAEA,MAAM,6BAA6B,UAA4B;CAC7D,MAAM,SAAS,aAAa,KAAK;CACjC,MAAM,WACJ,WAAW,KAAA,IACP,iCAAiC,IAAI,YAAY,KAAK,KAAK,EAAE,IAC7D,UAAU,OACV,SAAS,OACT,CAAC,+BAA+B,IAAI,MAAM;CAChD,OAAO,6BACL,OACA,WAAW,aAAa,WAC1B;AACF;AAEA,MAAM,YAAY,UAChB,MAAM,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK;;AAGvE,MAAM,iCAAiC;;AAGvC,MAAM,2BAA2B,kBAC/B,KAAK,IACH,KAAK,IACH,iCAAiC,KAAK,KAAK,IAAI,gBAAgB,GAAG,CAAC,GACnE,sBACF,GACA,EACF;AAEF,MAAM,uBAAuB,YAAqC;CAChE,MAAM,QAAQ,MAAM,OAAO,gBAAgB,CAAC,YAAY;EACtD,MAAM,6CACJ,IAAI,MACF,mIACF,GACA,UACF;CACF,CAAC;CACD,MAAM,mBAAmB,IAAI,MAAM,YAAY;CAC/C,MAAM,uBAAuB,IAAI,MAAM,YAAY,EAAE,cAAc,KAAK,CAAC;CACzE,OAAO;EACL,OAAO,OAAO,SAAS,SAAS,YAAY;GAC1C,MAAM,aAAa,SAAS,cAAc;GAC1C,MAAM,OAA2B;IAC/B,GAAG;IACH,WAAW;KAAE,SAAS;KAAG;IAAW;GACtC;GACA,QACE,eAAe,mBACX,uBACA,iBAAA,CACJ,KAAK,OAAO,MAAM,OAAO;EAC7B;EACA,iBAAiB,YAAY;GAC3B,MAAM,kBAAkB;IAItB,0BAA0B;IAK1B,QAAQ,OAAgB,aACtB,iBAAiB,iBACb,EACE,cAAc,wBAAwB,SAAS,aAAa,EAC9D,IACA,KAAA;GACR;GACA,MAAM,oBAAoB,iBAAiB,eACzC,SACA,eACF;GACA,MAAM,wBAAwB,qBAAqB,eACjD,SACA,eACF;GACA,OAAO,OAAO,QAAoC;IAChD,IAAI;IACJ,IAAI;KACF,YAAY,MAAM,IAAI,MAAM,CAAC,CAAC,KAAK;IACrC,QAAQ;KACN,OAAO,sBAAsB,GAAG;IAClC;IACA,MAAM,WAAW,SAAS,SAAS,SAAS,CAAC,GAAG,YAAY;IAG5D,QADE,WAAW,eAAe,IAAI,SAAS,gBAAgB,KAAA,OACtC,eACf,kBAAkB,GAAG,IACrB,sBAAsB,GAAG;GAC/B;EACF;EACA,cAAc,UAAU,iBAAiB,MAAM;CACjD;AACF;AAEA,SAAgB,aAAa,UAA+B,CAAC,GAAgB;CAC3E,MAAM,QAAQ,QAAQ,SAAS;CAC/B,IAAI,OAAO,UAAU,YAAY,CAAC,cAAc,KAAK,KAAK,GACxD,MAAM,IAAI,UAAU,gDAAgD;CAEtE,MAAM,mBAAmB,QAAQ,oBAAoB;CACrD,IACE,qBAAqB,gBACrB,qBAAqB,kBAErB,MAAM,IAAI,UACR,0EACF;CAEF,MAAM,QAAQ,QAAQ,SAAS;CAI/B,MAAM,YAAY,oBAChB,QAAQ,YACJ,QAAQ,QAAQ,QAAQ,SAAS,IACjC,qBAAqB,CAC3B,CAAC,CAAC;CAEF,MAAM,WAAW,OAAO,SAAuC;EAC7D,MAAM,QAAQ,MAAM,IAAI,CAAC,CAAC,QAAQ;EAClC,MAAM,iBAAiB,KAAK;EAC5B,IAAI,CAAC,OAAO,SAAS,cAAc,GACjC,MAAM,6CACJ,IAAI,UAAU,gDAAgD,GAC9D,UACF;EAEF,MAAM,QACJ,kBAAkB,QACd,KAAK,MAAM,QAAQ,GAAK,IAAI,MAC5B,KAAK,KAAK,iBAAiB,GAAK,IAAI;EAC1C,MAAM,mBAAmB,KAAK,SAAS,WAAW,iBAAiB;EACnE,MAAM,uBAAuB,KAAK,IAChC,GACA,KAAK,MAAM,mBAAmB,SAAS,GAAK,CAC9C;EACA,IAAI,uBAAuB,mBACzB,MAAM,6CACJ,IAAI,WACF,yDACF,GACA,UACF;EAEF,MAAM,aAAa,KAAK,SAAS,WAAW,mBAAmB;EAC/D,MAAM,UAAyB;GAAE,GAAG;GAAM;EAAM;EAChD,MAAM,iBACJ,KAAK,SAAS,UACV,SAAS;GACP;GACA;GACA,KAAK;GACL,KAAK;GACL,OAAO,KAAK;EACd,CAAC,IACD,SAAS;GAAC;GAAiC;GAAO,KAAK;EAAE,CAAC;EAChE,MAAM,IAAI,MAAM,UAAU;EAC1B,IAAI;GACF,MAAM,EAAE,KACN,OACA,SACA;IACE,cAAc;IACd;IACA,kBAAkB;GACpB,GACA,EAAE,WAAW,CACf;EACF,SAAS,KAAK;GACZ,IAAI,EAAE,YAAY,GAAG,GAAG;GACxB,MAAM,0BAA0B,GAAG;EACrC;CACF;CAEA,MAAM,kBAAkB,YAAwC;EAC9D,MAAM,eACJ,MAAM,IAAI,CAAC,CAAC,QAAQ,IACpB,cAAc,aACd,cAAc;EAChB,MAAM,QAAQ,KAAK,IACjB,GACA,KAAK,MACF,eAAe,QAAQ,SAAS,cAAc,gBACjD,CACF;EACA,OAAO,QAAQ,QAAQ,QAAQ,cAAc;CAC/C;CAEA,OAAO;EACL,MAAM,SAAS,MAAM;GACnB,MAAM,SAAS,IAAI;EACrB;EAEA,QAAQ,GAAG,SAA4B;GACrC,uBAAuB,SAAS,EAAE,iBAAiB,KAAK,CAAC;GACzD,MAAM,6BAAa,IAAI,IAA6B;GACpD,KAAK,MAAM,UAAU,SACnB,WAAW,IAAI,OAAO,SAAS,MAAM,MAAM;GAE7C,OAAO,OAAO,QAAoC;IAEhD,QAAO,MADS,UAAU,EAAA,CACjB,eAAe,OAAO,cAAc;KAC3C,MAAM,OAAO,uBAAuB,SAAS;KAC7C,IAAI,CAAC,MACH,MAAM,IAAI,UAAU,uCAAuC;KAE7D,MAAM,SAAS,WAAW,IAAI,KAAK,QAAQ;KAC3C,IAAI,CAAC,QAIH,MAAM,IAAI,MACR,yCAAyC,KAAK,SAAS,qCACzD;KAEF,MAAM,YAAY,gBAAgB,IAAI,MAAM;KAC5C,IAAI,KAAK,SAAS,UAAU;MAC1B,MAAM,uBAAuB,QAAQ,IAAI;MACzC;KACF;KAEA,MAAM,YAAY,eAAe,IAAI;KACrC,MAAM,SAAS,YACX,MAAM,UAAU,eAAe,KAAK,WAAW,EAC7C,eAAe,UACjB,CAAC,IACD;MACE,GAAI,MAAM,OAAO,MAAM,KAAK,SAAS;MACrC,SAAS;KACX;KACJ,IAAI,OAAO,SAAS;KAEpB,IAAI,OAAO,YAAY,QAAQ;MAK7B,IAAI,OAAO,cAAc,MAAM,OAAO;WAEpC,MAAM,SAAS;OACb,SAAS;OACT,MAAM;OACN,UAAU,KAAK;OACf,WAAW,KAAK;OAChB,OAAO;MACT,CAAC;MAEH;KACF;KAKA,MAAM,IAAI,eACR,0BAA0B,KAAK,UAAU,QAAQ,KAAK,SAAS,qBAAqB,OAAO,QAAQ,+BACrG;IACF,CAAC,CAAC,CAAC,GAAG;GACR;EACF;CACF;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as EventDefs, c as PresencePatch, d as WithPresence, l as PresenceSnapshot, n as Contract, o as PresenceDefs, p as Reducer, r as ContractEvent, s as PresenceMap, t as AppendInput } from "./contract-jIfaR085.js";
|
|
2
|
-
import { t as A2Store } from "./store-
|
|
3
|
-
import { i as A2Telemetry } from "./telemetry-
|
|
2
|
+
import { t as A2Store } from "./store-DysUkTH3.js";
|
|
3
|
+
import { i as A2Telemetry } from "./telemetry-CpeclqB2.js";
|
|
4
4
|
//#region src/scheduler-task.d.ts
|
|
5
5
|
/** One event carried by a delayed append task. */
|
|
6
6
|
type ScheduledEvent = {
|
|
@@ -154,8 +154,8 @@ type Session<D extends EventDefs, Append = SessionAppend<D>, P extends PresenceD
|
|
|
154
154
|
}>;
|
|
155
155
|
/**
|
|
156
156
|
* A live feed of this session's events, starting after `startAfter`
|
|
157
|
-
* (exclusive). Server-side only —
|
|
158
|
-
*
|
|
157
|
+
* (exclusive). Server-side only — `handle` from experimental-a2/http
|
|
158
|
+
* exposes it over SSE as the route's stream lane.
|
|
159
159
|
*/
|
|
160
160
|
stream(opts?: {
|
|
161
161
|
startAfter?: number;
|
|
@@ -257,4 +257,5 @@ type ServerOptions<D extends EventDefs, P extends PresenceDefs = Record<never, n
|
|
|
257
257
|
/** Implement a contract: bind its vocabulary to storage and reactions. */
|
|
258
258
|
declare function createServer<D extends EventDefs, P extends PresenceDefs = Record<never, never>>(options: ServerOptions<D, P>): A2Server<D, P>;
|
|
259
259
|
//#endregion
|
|
260
|
-
export { deliverSchedulerAppend as C, SchedulerTask as D, SchedulerDrainTask as E, createServer as S, SchedulerAppendTask as T, Session as _, Handler as a, SessionPresence as b, HandlerEntry as c, PushValidationContext as d, PushedEvent as f, ServerOptions as g, ScheduleTiming as h, DrainableServer as i, Lane as l, ScheduleDelay as m, A2Server as n, HandlerAppend as o, PushedPresence as p, AbortSpec as r, HandlerContext as s, A2Scheduler as t, LaneContext as u, SessionAppend as v, ScheduledEvent as w, SessionSchedule as x, SessionDispatch as y };
|
|
260
|
+
export { deliverSchedulerAppend as C, SchedulerTask as D, SchedulerDrainTask as E, createServer as S, SchedulerAppendTask as T, Session as _, Handler as a, SessionPresence as b, HandlerEntry as c, PushValidationContext as d, PushedEvent as f, ServerOptions as g, ScheduleTiming as h, DrainableServer as i, Lane as l, ScheduleDelay as m, A2Server as n, HandlerAppend as o, PushedPresence as p, AbortSpec as r, HandlerContext as s, A2Scheduler as t, LaneContext as u, SessionAppend as v, ScheduledEvent as w, SessionSchedule as x, SessionDispatch as y };
|
|
261
|
+
//# sourceMappingURL=server-DpvjhdoE.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-DpvjhdoE.d.ts","names":[],"sources":["../src/scheduler-task.ts","../src/server.ts"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as validateSync } from "./validate-XKT4FSNn.js";
|
|
2
2
|
import { n as asStoreUnavailable, t as A2Error } from "./errors-BQuJpe82.js";
|
|
3
|
-
import { g as nullProtoRecord, h as markSchedulerSendFailure, i as POLL_TIMINGS, o as RESERVED_PARTICIPANT_IDS, t as DRAIN_TIMINGS, v as serverInternals, y as serverSchedulerBindings } from "./internal-
|
|
3
|
+
import { g as nullProtoRecord, h as markSchedulerSendFailure, i as POLL_TIMINGS, o as RESERVED_PARTICIPANT_IDS, t as DRAIN_TIMINGS, v as serverInternals, y as serverSchedulerBindings } from "./internal-DstsI6Re.js";
|
|
4
4
|
import { n as InspectionUnsupportedError, r as serverInspection } from "./inspection-DaxB5jM2.js";
|
|
5
|
-
import { t as defaultSleep } from "./store-polling-
|
|
5
|
+
import { t as defaultSleep } from "./store-polling-dSeLxzfb.js";
|
|
6
6
|
import { r as platformWaitUntil, t as invocationDeadlineMs } from "./platform-B4TnJtWu.js";
|
|
7
7
|
import { t as retryableLazy } from "./retryable-lazy-DZWmHpii.js";
|
|
8
8
|
//#region src/deterministic-id.ts
|
|
@@ -66,12 +66,18 @@ async function deterministicScheduledEventId(scheduleId, itemOrdinal) {
|
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
68
68
|
//#region src/telemetry.ts
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* The default when no telemetry is configured. Spans cost nothing, but
|
|
71
|
+
* `recordError` prints: it only ever receives errors a2 swallows by
|
|
72
|
+
* design, so without a configured sink the console is the one place
|
|
73
|
+
* they can surface. Passing `telemetry` replaces this sink wholesale.
|
|
74
|
+
*/
|
|
75
|
+
const CONSOLE_TELEMETRY = { span: (name, attributes, fn) => fn({
|
|
70
76
|
setAttribute: () => {},
|
|
71
|
-
recordError: () => {
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
recordError: (error) => {
|
|
78
|
+
console.error(`[a2] error in ${name}`, attributes, error);
|
|
79
|
+
}
|
|
80
|
+
}) };
|
|
75
81
|
//#endregion
|
|
76
82
|
//#region src/server.ts
|
|
77
83
|
/**
|
|
@@ -230,7 +236,7 @@ function createServer(options) {
|
|
|
230
236
|
};
|
|
231
237
|
}
|
|
232
238
|
const resolveStore = retryableLazy(makeStore).get;
|
|
233
|
-
const telemetry = options.telemetry ??
|
|
239
|
+
const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
|
|
234
240
|
const scheduler = options.scheduler;
|
|
235
241
|
const handlers = /* @__PURE__ */ new Map();
|
|
236
242
|
for (const [type, entry] of Object.entries(options.handlers ?? {})) {
|
|
@@ -440,59 +446,109 @@ function createServer(options) {
|
|
|
440
446
|
throw asStoreUnavailable(err);
|
|
441
447
|
}
|
|
442
448
|
};
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
"a2.session_id": sessionId,
|
|
448
|
-
"a2.state.reducer": reducer.name
|
|
449
|
-
}, async (span) => {
|
|
450
|
-
let state = cloneInitial(reducer.initialState);
|
|
451
|
-
let index = 0;
|
|
452
|
-
let snapshotOutcome = "miss";
|
|
453
|
-
let stateRead;
|
|
449
|
+
const readCachedState = async (store, sessionId, reducerName) => {
|
|
450
|
+
try {
|
|
451
|
+
return await store.readState(nsId(sessionId), reducerName);
|
|
452
|
+
} catch {
|
|
454
453
|
try {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
};
|
|
462
|
-
} catch (err) {
|
|
463
|
-
throw asStoreUnavailable(err);
|
|
464
|
-
}
|
|
454
|
+
return {
|
|
455
|
+
snapshot: null,
|
|
456
|
+
events: await store.read(nsId(sessionId))
|
|
457
|
+
};
|
|
458
|
+
} catch (err) {
|
|
459
|
+
throw asStoreUnavailable(err);
|
|
465
460
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const foldStateRead = async (store, sessionId, reducer, stateRead, span) => {
|
|
464
|
+
let state = cloneInitial(reducer.initialState);
|
|
465
|
+
let index = 0;
|
|
466
|
+
let snapshotOutcome = "miss";
|
|
467
|
+
const snap = stateRead.snapshot;
|
|
468
|
+
let rows = stateRead.events;
|
|
469
|
+
if (snap) {
|
|
470
|
+
if (reducer.stateSchema) {
|
|
471
|
+
const result = validateSync(reducer.stateSchema, snap.state, "the stateSchema");
|
|
472
|
+
if (result.issues) snapshotOutcome = "rejected";
|
|
473
|
+
else {
|
|
474
|
+
state = result.value;
|
|
479
475
|
index = snap.index;
|
|
480
476
|
snapshotOutcome = "hit";
|
|
481
477
|
}
|
|
478
|
+
} else {
|
|
479
|
+
state = snap.state;
|
|
480
|
+
index = snap.index;
|
|
481
|
+
snapshotOutcome = "hit";
|
|
482
482
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
483
|
+
}
|
|
484
|
+
span.setAttribute("a2.state.snapshot", snapshotOutcome);
|
|
485
|
+
if (snapshotOutcome === "rejected") try {
|
|
486
|
+
rows = await store.read(nsId(sessionId));
|
|
487
|
+
} catch (err) {
|
|
488
|
+
throw asStoreUnavailable(err);
|
|
489
|
+
}
|
|
490
|
+
for (const row of rows) {
|
|
491
|
+
state = reducer.fold(state, toPublic(row));
|
|
492
|
+
index = row.index;
|
|
493
|
+
}
|
|
494
|
+
span.setAttribute("a2.state.folded", rows.length);
|
|
495
|
+
span.setAttribute("a2.state.index", index);
|
|
496
|
+
return {
|
|
497
|
+
state,
|
|
498
|
+
index,
|
|
499
|
+
folded: rows.length
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
const pendingStateReads = /* @__PURE__ */ new Map();
|
|
503
|
+
const flushStateReads = async (store, reducerName, batch) => {
|
|
504
|
+
let reads;
|
|
505
|
+
try {
|
|
506
|
+
reads = store.readStates ? await store.readStates(batch.map((entry) => nsId(entry.sessionId)), reducerName) : null;
|
|
507
|
+
} catch {
|
|
508
|
+
reads = null;
|
|
509
|
+
}
|
|
510
|
+
if (!reads || reads.length !== batch.length) {
|
|
511
|
+
for (const entry of batch) entry.resolve(readCachedState(store, entry.sessionId, reducerName));
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const seen = /* @__PURE__ */ new Set();
|
|
515
|
+
for (const [position, entry] of batch.entries()) {
|
|
516
|
+
const shared = seen.has(entry.sessionId);
|
|
517
|
+
seen.add(entry.sessionId);
|
|
518
|
+
try {
|
|
519
|
+
const read = reads[position];
|
|
520
|
+
entry.resolve(shared ? structuredClone(read) : read);
|
|
521
|
+
} catch {
|
|
522
|
+
entry.resolve(readCachedState(store, entry.sessionId, reducerName));
|
|
492
523
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
const enqueueStateRead = (store, sessionId, reducerName) => new Promise((resolve) => {
|
|
527
|
+
let batch = pendingStateReads.get(reducerName);
|
|
528
|
+
if (!batch) {
|
|
529
|
+
const opened = [];
|
|
530
|
+
pendingStateReads.set(reducerName, opened);
|
|
531
|
+
queueMicrotask(() => {
|
|
532
|
+
pendingStateReads.delete(reducerName);
|
|
533
|
+
flushStateReads(store, reducerName, opened);
|
|
534
|
+
});
|
|
535
|
+
batch = opened;
|
|
536
|
+
}
|
|
537
|
+
batch.push({
|
|
538
|
+
sessionId,
|
|
539
|
+
resolve
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
const readState = async (sessionId, reducer) => {
|
|
543
|
+
const store = await resolveStore();
|
|
544
|
+
return telemetry.span("a2.state", {
|
|
545
|
+
"a2.contract": name,
|
|
546
|
+
"a2.session_id": sessionId,
|
|
547
|
+
"a2.state.reducer": reducer.name
|
|
548
|
+
}, async (span) => {
|
|
549
|
+
const stateRead = store.readStates ? await enqueueStateRead(store, sessionId, reducer.name) : await readCachedState(store, sessionId, reducer.name);
|
|
550
|
+
const { state, index, folded } = await foldStateRead(store, sessionId, reducer, stateRead, span);
|
|
551
|
+
if (folded > 0) {
|
|
496
552
|
const snapshotState = cloneInitial(state);
|
|
497
553
|
const write = Promise.resolve().then(() => store.putSnapshot(nsId(sessionId), reducer.name, index, snapshotState)).catch(() => {});
|
|
498
554
|
track(write);
|
|
@@ -1403,3 +1459,5 @@ function cloneInitial(initial) {
|
|
|
1403
1459
|
}
|
|
1404
1460
|
//#endregion
|
|
1405
1461
|
export { deliverSchedulerAppend as n, createServer as t };
|
|
1462
|
+
|
|
1463
|
+
//# sourceMappingURL=server-Duw6MVlB.js.map
|