dynamodb-reactive 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -43
- package/dist/{client-Zb67giI5.d.ts → client-wZQ9XC9S.d.ts} +11 -0
- package/dist/client.cjs +53 -55
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +53 -55
- package/dist/client.js.map +1 -1
- package/dist/cloudflare.cjs +461 -0
- package/dist/cloudflare.cjs.map +1 -0
- package/dist/cloudflare.d.cts +129 -0
- package/dist/cloudflare.d.ts +129 -0
- package/dist/cloudflare.js +456 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/cluster.cjs +66 -66
- package/dist/cluster.cjs.map +1 -1
- package/dist/cluster.d.cts +2 -78
- package/dist/cluster.d.ts +2 -78
- package/dist/cluster.js +66 -66
- package/dist/cluster.js.map +1 -1
- package/dist/core.cjs +1 -3
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +1 -3
- package/dist/core.js.map +1 -1
- package/dist/hub.cjs +164 -0
- package/dist/hub.cjs.map +1 -0
- package/dist/hub.d.cts +91 -0
- package/dist/hub.d.ts +91 -0
- package/dist/hub.js +157 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.cjs +1 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/infra.cjs +19 -33
- package/dist/infra.cjs.map +1 -1
- package/dist/infra.js +19 -33
- package/dist/infra.js.map +1 -1
- package/dist/protocol-C4Syt_Bl.d.ts +78 -0
- package/dist/react.cjs +56 -65
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +56 -65
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +46 -139
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +46 -139
- package/dist/server.js.map +1 -1
- package/package.json +42 -24
package/dist/hub.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../client/src/hub.ts"],"names":[],"mappings":"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","file":"hub.js","sourcesContent":["import type {\n JsonPatch,\n SnapshotResponse,\n SubscribeRequest,\n UnsubscribeRequest,\n WebSocketEvent,\n} from './protocol.js';\n\n/**\n * Protocol core for a reactive hub: the server half of the wire protocol for\n * backends that hold WebSockets themselves (for example a Cloudflare Durable\n * Object). It keeps every piece of per-socket state on the socket's attachment so\n * the state survives hibernation, and it has no runtime imports, so it runs and\n * tests in Node.\n */\n\n/** Hibernation limits a socket attachment to 2,048 bytes of serialized JSON. */\nexport const DEFAULT_MAX_ATTACHMENT_BYTES = 2_048;\n\n/** One subscription held by a socket. */\nexport interface HubSubscription {\n subscriptionId: string;\n generation: number;\n path: string;\n}\n\n/** State kept on each socket's attachment. */\nexport interface HubSocketState {\n connectionId: string;\n principal: string;\n subscriptions: HubSubscription[];\n /** Epoch ms the socket was accepted; the liveness baseline before its first ping. */\n connectedAt?: number;\n}\n\n/** The parts of a hibernatable WebSocket the hub uses; faked in tests. */\nexport interface HubSocket {\n send(message: string): void;\n deserializeAttachment(): unknown;\n serializeAttachment(value: unknown): void;\n}\n\nexport interface HubSockets {\n all(): HubSocket[];\n byConnectionId(connectionId: string): HubSocket | undefined;\n}\n\n/** Monotonic per-path versions. Versions only need to grow while sockets stay open. */\nexport interface HubVersions {\n get(path: string): Promise<number>;\n set(path: string, version: number): Promise<void>;\n}\n\nexport interface HubPathContext {\n path: string;\n principal: string;\n input: unknown;\n}\n\nexport interface HubPath {\n /**\n * Runs before the subscription is stored. Return false to reject it. Every\n * subscriber of a path receives its pushes, so authorise here, not in `snapshot`.\n */\n authorize?(context: HubPathContext): boolean | Promise<boolean>;\n /** Current data for the path, sent as the subscription snapshot. */\n snapshot(context: HubPathContext): unknown;\n}\n\n/** Fixed paths by name, or a resolver for parameterised paths such as `drafts.<id>`. */\nexport type HubPaths = Record<string, HubPath> | ((path: string) => HubPath | undefined);\n\nexport interface ReactiveHubOptions {\n sockets: HubSockets;\n versions: HubVersions;\n paths: HubPaths;\n /** Rejects subscribes that would grow an attachment past this size. */\n maxAttachmentBytes?: number;\n}\n\n/** Thrown for requests the caller is not allowed to make. */\nexport class ReactiveHubError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReactiveHubError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Validates a reactive HTTP body for the subscribe and unsubscribe requests a hub serves. */\nexport function isHubRequest(value: unknown): value is SubscribeRequest | UnsubscribeRequest {\n if (!isRecord(value)) return false;\n return (\n (value.type === 'subscribe' || value.type === 'unsubscribe') &&\n typeof value.connectionId === 'string' &&\n typeof value.subscriptionId === 'string' &&\n typeof value.generation === 'number' &&\n (value.type === 'unsubscribe' || typeof value.path === 'string')\n );\n}\n\n/** Returns the bearer token from an `Authorization` header, or an empty string. */\nexport function bearerToken(request: Request): string {\n const match = /^Bearer ([^\\s]+)$/u.exec(request.headers.get('authorization') ?? '');\n return match?.[1] ?? '';\n}\n\n/** Initial attachment for a newly accepted socket. */\nexport function createHubSocketState(\n connectionId: string,\n principal: string,\n connectedAt?: number,\n): HubSocketState {\n return {\n connectionId,\n principal,\n subscriptions: [],\n ...(connectedAt === undefined ? {} : { connectedAt }),\n };\n}\n\nfunction socketState(socket: HubSocket): HubSocketState {\n const state = socket.deserializeAttachment() as HubSocketState;\n // Attachments written before subscriptions carried a path have no path; they\n // are dropped rather than fanned out to every notification.\n return { ...state, subscriptions: state.subscriptions.filter((entry) => entry.path) };\n}\n\nfunction send(socket: HubSocket, event: WebSocketEvent): void {\n try {\n socket.send(JSON.stringify(event));\n } catch {\n // A socket closing mid-broadcast is cleaned up by its close handler.\n }\n}\n\n/** Creates the protocol core for one hub instance. */\nexport function createReactiveHub(options: ReactiveHubOptions) {\n const { sockets, versions } = options;\n const maxAttachmentBytes = options.maxAttachmentBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES;\n const resolve = (path: string): HubPath | undefined =>\n typeof options.paths === 'function'\n ? options.paths(path)\n : Object.hasOwn(options.paths, path)\n ? options.paths[path]\n : undefined;\n\n const ownedSocket = (connectionId: string, principal: string): HubSocket | undefined => {\n const socket = sockets.byConnectionId(connectionId);\n // The HTTP caller must own the WebSocket it registers against.\n return socket && socketState(socket).principal === principal ? socket : undefined;\n };\n\n const subscribersOf = (path: string) =>\n sockets.all().flatMap((socket) =>\n socketState(socket)\n .subscriptions.filter((subscription) => subscription.path === path)\n .map((subscription) => ({ socket, subscription })),\n );\n\n const bump = async (path: string) => {\n const fromVersion = await versions.get(path);\n const toVersion = fromVersion + 1;\n await versions.set(path, toVersion);\n return { fromVersion, toVersion };\n };\n\n return {\n /** Answers the client's `init` and `ping` frames. */\n message(socket: HubSocket, raw: string): void {\n let frame: unknown;\n try {\n frame = JSON.parse(raw);\n } catch {\n return;\n }\n const type = isRecord(frame) ? frame.type : undefined;\n if (type === 'init') {\n send(socket, { type: 'connected', connectionId: socketState(socket).connectionId });\n } else if (type === 'ping') {\n send(socket, { type: 'pong' });\n }\n },\n\n async subscribe(request: SubscribeRequest, principal: string): Promise<SnapshotResponse> {\n const handler = resolve(request.path);\n if (!handler) throw new ReactiveHubError('Unknown subscription path');\n if (!ownedSocket(request.connectionId, principal)) {\n throw new ReactiveHubError('Unknown connection');\n }\n const context: HubPathContext = { path: request.path, principal, input: request.input };\n if (handler.authorize && !(await handler.authorize(context))) {\n throw new ReactiveHubError('Subscription not allowed');\n }\n // Re-read after the awaits so a concurrent subscribe on the same socket is kept.\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) throw new ReactiveHubError('Unknown connection');\n const state = socketState(socket);\n const next: HubSocketState = {\n ...state,\n subscriptions: [\n ...state.subscriptions.filter((entry) => entry.subscriptionId !== request.subscriptionId),\n {\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n path: request.path,\n },\n ],\n };\n if (new TextEncoder().encode(JSON.stringify(next)).length > maxAttachmentBytes) {\n throw new ReactiveHubError('Too many subscriptions on this connection');\n }\n socket.serializeAttachment(next);\n // Read the version before the data so a concurrent notify can only make the\n // client re-fetch, never miss a change.\n const version = await versions.get(request.path);\n const data = await handler.snapshot(context);\n return {\n type: 'snapshot',\n subscriptionId: request.subscriptionId,\n generation: request.generation,\n version,\n data,\n };\n },\n\n unsubscribe(request: UnsubscribeRequest, principal: string): void {\n const socket = ownedSocket(request.connectionId, principal);\n if (!socket) return;\n const state = socketState(socket);\n socket.serializeAttachment({\n ...state,\n subscriptions: state.subscriptions.filter(\n (entry) => entry.subscriptionId !== request.subscriptionId,\n ),\n } satisfies HubSocketState);\n },\n\n /** Bumps the path's version and asks each of its subscribers to re-fetch. */\n async notify(path: string): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'resync',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n });\n }\n },\n\n /**\n * Bumps the path's version and sends the same JSON patches to each subscriber.\n * Use it only when every subscriber of the path sees the same data.\n */\n async publish(path: string, patches: JsonPatch[]): Promise<void> {\n const { fromVersion, toVersion } = await bump(path);\n for (const { socket, subscription } of subscribersOf(path)) {\n send(socket, {\n type: 'patch',\n subscriptionId: subscription.subscriptionId,\n generation: subscription.generation,\n fromVersion,\n toVersion,\n messageId: crypto.randomUUID(),\n patches,\n });\n }\n },\n };\n}\n\nexport type ReactiveHub = ReturnType<typeof createReactiveHub>;\n"]}
|
package/dist/index.cjs
CHANGED
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../core/src/schema.ts","../../core/src/table.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import type { z } from 'zod';\n\nimport type {\n DynamoTableConfig,\n FieldRef,\n IndexDefinition,\n SupportedSchema,\n} from './types.js';\n\n/**\n * Extract all field names from a schema (handles both ZodObject and ZodDiscriminatedUnion)\n */\nfunction getSchemaFields(schema: SupportedSchema): Set<string> {\n const fields = new Set<string>();\n\n // Check if it's a discriminated union by looking for 'options' property\n if ('options' in schema && Array.isArray(schema.options)) {\n // It's a discriminated union - collect fields from all options\n for (const option of schema.options) {\n if ('shape' in option && typeof option.shape === 'object') {\n for (const key of Object.keys(option.shape)) {\n fields.add(key);\n }\n }\n }\n } else if ('shape' in schema && typeof schema.shape === 'object') {\n // It's a regular ZodObject\n for (const key of Object.keys(schema.shape)) {\n fields.add(key);\n }\n }\n\n return fields;\n}\n\n/**\n * Creates field references for type-safe query building\n */\ntype FieldRefs<TSchema extends SupportedSchema> = {\n [K in keyof z.infer<TSchema>]: FieldRef<K & string, z.infer<TSchema>[K]>;\n};\n\n/**\n * DynamoTable - Defines a DynamoDB table with type-safe schema\n *\n * Supports both ZodObject and ZodDiscriminatedUnion schemas for single-table design patterns.\n *\n * @example\n * ```ts\n * // Simple schema\n * const TodoTable = new DynamoTable({\n * tableName: 'prod-todo-table',\n * schema: z.object({\n * PK: z.string(),\n * SK: z.string(),\n * text: z.string(),\n * isDone: z.boolean(),\n * }),\n * pk: 'PK',\n * sk: 'SK',\n * });\n *\n * // Discriminated union for single-table design\n * const MultiEntityTable = new DynamoTable({\n * tableName: 'prod-entities',\n * schema: z.discriminatedUnion('entityType', [\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('USER'), name: z.string() }),\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('ITEM'), title: z.string() }),\n * ]),\n * pk: 'PK',\n * sk: 'SK',\n * });\n * ```\n */\nexport class DynamoTable<\n TSchema extends SupportedSchema,\n TPk extends keyof z.infer<TSchema>,\n TSk extends keyof z.infer<TSchema> | undefined = undefined,\n TIndexes extends Record<string, IndexDefinition> = Record<\n string,\n IndexDefinition\n >,\n> {\n public readonly tableName: string;\n public readonly schema: TSchema;\n public readonly pk: TPk;\n public readonly sk: TSk;\n public readonly indexes: TIndexes;\n public readonly field: FieldRefs<TSchema>;\n\n constructor(config: DynamoTableConfig<TSchema, TPk, TSk, TIndexes>) {\n this.tableName = config.tableName;\n this.schema = config.schema;\n this.pk = config.pk;\n this.sk = config.sk as TSk;\n this.indexes = (config.indexes ?? {}) as TIndexes;\n\n // Create field references for query building\n this.field = this.createFieldRefs();\n }\n\n private createFieldRefs(): FieldRefs<TSchema> {\n const fields = getSchemaFields(this.schema);\n const refs: Record<string, FieldRef<string, unknown>> = {};\n\n for (const key of fields) {\n refs[key] = {\n fieldName: key,\n _type: undefined as unknown,\n };\n }\n\n return refs as FieldRefs<TSchema>;\n }\n\n /**\n * Validate an item against the schema\n */\n validate(item: unknown): z.infer<TSchema> {\n return this.schema.parse(item) as z.infer<TSchema>;\n }\n\n /**\n * Safely validate an item, returning a result object\n */\n safeParse(item: unknown): z.ZodSafeParseResult<z.infer<TSchema>> {\n return this.schema.safeParse(item) as z.ZodSafeParseResult<\n z.infer<TSchema>\n >;\n }\n\n /**\n * Get the list of field names in the schema\n */\n getFieldNames(): (keyof z.infer<TSchema>)[] {\n return Array.from(\n getSchemaFields(this.schema),\n ) as (keyof z.infer<TSchema>)[];\n }\n\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../core/src/schema.ts","../../core/src/table.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import type { z } from 'zod';\n\nimport type { DynamoTableConfig, FieldRef, IndexDefinition, SupportedSchema } from './types.js';\n\n/**\n * Extract all field names from a schema (handles both ZodObject and ZodDiscriminatedUnion)\n */\nfunction getSchemaFields(schema: SupportedSchema): Set<string> {\n const fields = new Set<string>();\n\n // Check if it's a discriminated union by looking for 'options' property\n if ('options' in schema && Array.isArray(schema.options)) {\n // It's a discriminated union - collect fields from all options\n for (const option of schema.options) {\n if ('shape' in option && typeof option.shape === 'object') {\n for (const key of Object.keys(option.shape)) {\n fields.add(key);\n }\n }\n }\n } else if ('shape' in schema && typeof schema.shape === 'object') {\n // It's a regular ZodObject\n for (const key of Object.keys(schema.shape)) {\n fields.add(key);\n }\n }\n\n return fields;\n}\n\n/**\n * Creates field references for type-safe query building\n */\ntype FieldRefs<TSchema extends SupportedSchema> = {\n [K in keyof z.infer<TSchema>]: FieldRef<K & string, z.infer<TSchema>[K]>;\n};\n\n/**\n * DynamoTable - Defines a DynamoDB table with type-safe schema\n *\n * Supports both ZodObject and ZodDiscriminatedUnion schemas for single-table design patterns.\n *\n * @example\n * ```ts\n * // Simple schema\n * const TodoTable = new DynamoTable({\n * tableName: 'prod-todo-table',\n * schema: z.object({\n * PK: z.string(),\n * SK: z.string(),\n * text: z.string(),\n * isDone: z.boolean(),\n * }),\n * pk: 'PK',\n * sk: 'SK',\n * });\n *\n * // Discriminated union for single-table design\n * const MultiEntityTable = new DynamoTable({\n * tableName: 'prod-entities',\n * schema: z.discriminatedUnion('entityType', [\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('USER'), name: z.string() }),\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('ITEM'), title: z.string() }),\n * ]),\n * pk: 'PK',\n * sk: 'SK',\n * });\n * ```\n */\nexport class DynamoTable<\n TSchema extends SupportedSchema,\n TPk extends keyof z.infer<TSchema>,\n TSk extends keyof z.infer<TSchema> | undefined = undefined,\n TIndexes extends Record<string, IndexDefinition> = Record<string, IndexDefinition>,\n> {\n public readonly tableName: string;\n public readonly schema: TSchema;\n public readonly pk: TPk;\n public readonly sk: TSk;\n public readonly indexes: TIndexes;\n public readonly field: FieldRefs<TSchema>;\n\n constructor(config: DynamoTableConfig<TSchema, TPk, TSk, TIndexes>) {\n this.tableName = config.tableName;\n this.schema = config.schema;\n this.pk = config.pk;\n this.sk = config.sk as TSk;\n this.indexes = (config.indexes ?? {}) as TIndexes;\n\n // Create field references for query building\n this.field = this.createFieldRefs();\n }\n\n private createFieldRefs(): FieldRefs<TSchema> {\n const fields = getSchemaFields(this.schema);\n const refs: Record<string, FieldRef<string, unknown>> = {};\n\n for (const key of fields) {\n refs[key] = {\n fieldName: key,\n _type: undefined as unknown,\n };\n }\n\n return refs as FieldRefs<TSchema>;\n }\n\n /**\n * Validate an item against the schema\n */\n validate(item: unknown): z.infer<TSchema> {\n return this.schema.parse(item) as z.infer<TSchema>;\n }\n\n /**\n * Safely validate an item, returning a result object\n */\n safeParse(item: unknown): z.ZodSafeParseResult<z.infer<TSchema>> {\n return this.schema.safeParse(item) as z.ZodSafeParseResult<z.infer<TSchema>>;\n }\n\n /**\n * Get the list of field names in the schema\n */\n getFieldNames(): (keyof z.infer<TSchema>)[] {\n return Array.from(getSchemaFields(this.schema)) as (keyof z.infer<TSchema>)[];\n }\n}\n"]}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../core/src/schema.ts","../../core/src/table.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import type { z } from 'zod';\n\nimport type {\n DynamoTableConfig,\n FieldRef,\n IndexDefinition,\n SupportedSchema,\n} from './types.js';\n\n/**\n * Extract all field names from a schema (handles both ZodObject and ZodDiscriminatedUnion)\n */\nfunction getSchemaFields(schema: SupportedSchema): Set<string> {\n const fields = new Set<string>();\n\n // Check if it's a discriminated union by looking for 'options' property\n if ('options' in schema && Array.isArray(schema.options)) {\n // It's a discriminated union - collect fields from all options\n for (const option of schema.options) {\n if ('shape' in option && typeof option.shape === 'object') {\n for (const key of Object.keys(option.shape)) {\n fields.add(key);\n }\n }\n }\n } else if ('shape' in schema && typeof schema.shape === 'object') {\n // It's a regular ZodObject\n for (const key of Object.keys(schema.shape)) {\n fields.add(key);\n }\n }\n\n return fields;\n}\n\n/**\n * Creates field references for type-safe query building\n */\ntype FieldRefs<TSchema extends SupportedSchema> = {\n [K in keyof z.infer<TSchema>]: FieldRef<K & string, z.infer<TSchema>[K]>;\n};\n\n/**\n * DynamoTable - Defines a DynamoDB table with type-safe schema\n *\n * Supports both ZodObject and ZodDiscriminatedUnion schemas for single-table design patterns.\n *\n * @example\n * ```ts\n * // Simple schema\n * const TodoTable = new DynamoTable({\n * tableName: 'prod-todo-table',\n * schema: z.object({\n * PK: z.string(),\n * SK: z.string(),\n * text: z.string(),\n * isDone: z.boolean(),\n * }),\n * pk: 'PK',\n * sk: 'SK',\n * });\n *\n * // Discriminated union for single-table design\n * const MultiEntityTable = new DynamoTable({\n * tableName: 'prod-entities',\n * schema: z.discriminatedUnion('entityType', [\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('USER'), name: z.string() }),\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('ITEM'), title: z.string() }),\n * ]),\n * pk: 'PK',\n * sk: 'SK',\n * });\n * ```\n */\nexport class DynamoTable<\n TSchema extends SupportedSchema,\n TPk extends keyof z.infer<TSchema>,\n TSk extends keyof z.infer<TSchema> | undefined = undefined,\n TIndexes extends Record<string, IndexDefinition> = Record<\n string,\n IndexDefinition\n >,\n> {\n public readonly tableName: string;\n public readonly schema: TSchema;\n public readonly pk: TPk;\n public readonly sk: TSk;\n public readonly indexes: TIndexes;\n public readonly field: FieldRefs<TSchema>;\n\n constructor(config: DynamoTableConfig<TSchema, TPk, TSk, TIndexes>) {\n this.tableName = config.tableName;\n this.schema = config.schema;\n this.pk = config.pk;\n this.sk = config.sk as TSk;\n this.indexes = (config.indexes ?? {}) as TIndexes;\n\n // Create field references for query building\n this.field = this.createFieldRefs();\n }\n\n private createFieldRefs(): FieldRefs<TSchema> {\n const fields = getSchemaFields(this.schema);\n const refs: Record<string, FieldRef<string, unknown>> = {};\n\n for (const key of fields) {\n refs[key] = {\n fieldName: key,\n _type: undefined as unknown,\n };\n }\n\n return refs as FieldRefs<TSchema>;\n }\n\n /**\n * Validate an item against the schema\n */\n validate(item: unknown): z.infer<TSchema> {\n return this.schema.parse(item) as z.infer<TSchema>;\n }\n\n /**\n * Safely validate an item, returning a result object\n */\n safeParse(item: unknown): z.ZodSafeParseResult<z.infer<TSchema>> {\n return this.schema.safeParse(item) as z.ZodSafeParseResult<\n z.infer<TSchema>\n >;\n }\n\n /**\n * Get the list of field names in the schema\n */\n getFieldNames(): (keyof z.infer<TSchema>)[] {\n return Array.from(\n getSchemaFields(this.schema),\n ) as (keyof z.infer<TSchema>)[];\n }\n\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../core/src/schema.ts","../../core/src/table.ts"],"names":[],"mappings":";;;AAIO,IAAM,qBAAA,GAAoD,CAAA,CAAE,IAAA,CAAK,MAAM;AAC5E,EAAA,MAAM,UAAA,GAAa,EAChB,MAAA,CAAO;AAAA,IACN,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,YAAY,CAAA;AAAA,IAC5B,QAAA,EAAU,CAAA,CAAE,IAAA,CAAK,CAAC,IAAA,EAAM,MAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,KAAK,CAAC,CAAA;AAAA,IACvD,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IACvB,KAAA,EAAO,EAAE,OAAA;AAAQ,GAClB,EACA,MAAA,EAAO;AACV,EAAA,MAAM,aAAA,GAAgB,EAAE,KAAA,CAAM;AAAA,IAC5B,EACG,MAAA,CAAO;AAAA,MACN,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,UAAU,CAAA;AAAA,MAC1B,QAAA,EAAU,CAAA,CAAE,OAAA,CAAQ,SAAS,CAAA;AAAA,MAC7B,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,MACvB,KAAA,EAAO,EAAE,OAAA,EAAQ;AAAA,MACjB,MAAA,EAAQ,EAAE,OAAA;AAAQ,KACnB,EACA,MAAA,EAAO;AAAA,IACV,EACG,MAAA,CAAO;AAAA,MACN,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,UAAU,CAAA;AAAA,MAC1B,UAAU,CAAA,CAAE,IAAA,CAAK,CAAC,YAAA,EAAc,UAAU,CAAC,CAAA;AAAA,MAC3C,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,MACvB,KAAA,EAAO,EAAE,OAAA;AAAQ,KAClB,EACA,MAAA;AAAO,GACX,CAAA;AACD,EAAA,MAAM,OAAA,GAAU,EACb,MAAA,CAAO;AAAA,IACN,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,SAAS,CAAA;AAAA,IACzB,UAAU,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,IAAA,EAAM,KAAK,CAAC,CAAA;AAAA,IACrC,YAAY,CAAA,CAAE,KAAA,CAAM,qBAAqB,CAAA,CAAE,IAAI,CAAC;AAAA,GACjD,CAAA,CACA,MAAA,GACA,WAAA,CAAY,CAAC,WAAW,OAAA,KAAY;AACnC,IAAA,IAAI,UAAU,QAAA,KAAa,KAAA,IAAS,SAAA,CAAU,UAAA,CAAW,WAAW,CAAA,EAAG;AACrE,MAAA,OAAA,CAAQ,QAAA,CAAS;AAAA,QACf,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,YAAY,CAAA;AAAA,QACnB,OAAA,EAAS;AAAA,OACV,CAAA;AAAA,IACH;AAAA,EACF,CAAC,CAAA;AACH,EAAA,OAAO,EAAE,KAAA,CAAM,CAAC,UAAA,EAAY,aAAA,EAAe,OAAO,CAAC,CAAA;AACrD,CAAC;AAEM,IAAM,mBAAA,GAAsB,EAChC,MAAA,CAAO;AAAA,EACN,WAAA,EAAa,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,EACxB,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC3B,WAAW,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EACtC,cAAc,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EACzC,cAAc,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EACzC,gBAAA,EAAkB,CAAA,CAAE,KAAA,CAAM,qBAAqB,CAAA;AAAA,EAC/C,WAAW,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EACtC,SAAA,EAAW,EAAE,IAAA,CAAK,CAAC,OAAO,MAAM,CAAC,EAAE,QAAA,EAAS;AAAA,EAC5C,KAAA,EAAO,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,GAAW,QAAA;AACrC,CAAC,EACA,MAAA,EAAO,CACP,WAAA,CAAY,CAAC,UAAU,OAAA,KAAY;AAClC,EAAA,IAAI,QAAA,CAAS,SAAA,IAAa,CAAC,QAAA,CAAS,YAAA,EAAc;AAChD,IAAA,OAAA,CAAQ,QAAA,CAAS;AAAA,MACf,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,CAAC,cAAc,CAAA;AAAA,MACrB,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AACA,EAAA,IAAI,CAAC,QAAA,CAAS,SAAA,KAAc,QAAA,CAAS,YAAA,IAAgB,SAAS,YAAA,CAAA,EAAe;AAC3E,IAAA,OAAA,CAAQ,QAAA,CAAS;AAAA,MACf,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,CAAC,WAAW,CAAA;AAAA,MAClB,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AACA,EAAA,IAAI,QAAQ,QAAA,CAAS,SAAS,MAAM,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,EAAG;AAC/D,IAAA,OAAA,CAAQ,QAAA,CAAS;AAAA,MACf,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,CAAC,WAAW,CAAA;AAAA,MAClB,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AACF,CAAC;AAEH,IAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EACrC,YAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EACzC,aAAa,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EAC1C,WAAW,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,QAAA,EAAS;AAAA,EACrC,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAC7B,CAAC,CAAA;AAEM,IAAM,qBAAA,GAAwB,CAAA,CAAE,kBAAA,CAAmB,MAAA,EAAQ;AAAA,EAChE,sBAAsB,MAAA,CAAO;AAAA,IAC3B,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,OAAO,CAAA;AAAA,IACvB,SAAS,CAAA,CAAE,KAAA;AAAA,MACT,EAAE,MAAA,CAAO;AAAA,QACP,EAAA,EAAI,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,UAAU,SAAA,EAAW,MAAA,EAAQ,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,QAC/D,IAAA,EAAM,EAAE,MAAA,EAAO;AAAA,QACf,KAAA,EAAO,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,QAC5B,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,OAC3B;AAAA;AACH,GACD,CAAA;AAAA,EACD,sBAAsB,MAAA,CAAO;AAAA,IAC3B,IAAA,EAAM,CAAA,CAAE,OAAA,CAAQ,QAAQ;AAAA,GACzB,EAAE,MAAA;AACL,CAAC;AAKM,IAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA,EAI3B,WAAA,EAAa,EAAE,MAAA,CAAO;AAAA,IACpB,YAAA,EAAc,EAAE,MAAA,EAAO;AAAA,IACvB,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAAA,IACjC,OAAA,EAAS,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,CAAA,CAAE,OAAA,EAAS,CAAA,CAAE,QAAA,EAAS;AAAA,IACpD,iBAAA,EAAmB,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,CAAA,CAAE,OAAA,EAAS,CAAA,CAAE,QAAA,EAAS;AAAA,IAC9D,eAAA,EAAiB,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,CAAA,CAAE,OAAA,EAAS,CAAA,CAAE,QAAA,EAAS;AAAA,IAC5D,WAAA,EAAa,EAAE,MAAA,EAAO;AAAA,IACtB,GAAA,EAAK,EAAE,MAAA;AAAO,GACf,CAAA;AAAA;AAAA;AAAA;AAAA,EAKD,YAAA,EAAc,EAAE,MAAA,CAAO;AAAA,IACrB,EAAA,EAAI,EAAE,MAAA,EAAO;AAAA;AAAA,IACb,EAAA,EAAI,EAAE,MAAA,EAAO;AAAA;AAAA,IACb,YAAA,EAAc,EAAE,MAAA,EAAO;AAAA,IACvB,cAAA,EAAgB,EAAE,MAAA,EAAO;AAAA,IACzB,SAAA,EAAW,EAAE,MAAA,EAAO;AAAA,IACpB,SAAA,EAAW,EAAE,MAAA,EAAO;AAAA,IACpB,UAAA,EAAY,EAAE,MAAA,EAAO;AAAA,IACrB,YAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IACzC,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IAChC,GAAA,EAAK,EAAE,MAAA;AAAO,GACf,CAAA;AAAA;AAAA;AAAA;AAAA,EAKD,OAAA,EAAS,EAAE,MAAA,CAAO;AAAA,IAChB,EAAA,EAAI,EAAE,MAAA,EAAO;AAAA;AAAA,IACb,EAAA,EAAI,EAAE,MAAA,EAAO;AAAA;AAAA,IACb,YAAA,EAAc,EAAE,MAAA,EAAO;AAAA,IACvB,cAAA,EAAgB,EAAE,MAAA,EAAO;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,CAAK,CAAC,eAAe,QAAA,EAAU,WAAA,EAAa,SAAS,CAAC,CAAA;AAAA,IAChE,YAAY,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IACzC,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IAChC,aAAA,EAAe,mBAAA;AAAA,IACf,UAAA,EAAY,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,SAAS,CAAA;AAAA,IAC/B,YAAA,EAAc,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA;AAAA,IAChC,SAAS,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IACtC,eAAA,EAAiB,sBAAsB,QAAA,EAAS;AAAA,IAChD,SAAA,EAAW,EAAE,MAAA,EAAO;AAAA,IACpB,SAAA,EAAW,EAAE,MAAA,EAAO;AAAA,IACpB,GAAA,EAAK,EAAE,MAAA;AAAO,GACf;AACH;AAGO,IAAM,6BAAA,GAAgC;AAMtC,IAAM,gBAAA,GAAmB;AAAA,EAC9B,WAAA,EAAa,uBAAA;AAAA,EACb,YAAA,EAAc,wBAAA;AAAA,EACd,OAAA,EAAS;AACX;;;AC3KA,SAAS,gBAAgB,MAAA,EAAsC;AAC7D,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAG/B,EAAA,IAAI,aAAa,MAAA,IAAU,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAA,EAAG;AAExD,IAAA,KAAA,MAAW,MAAA,IAAU,OAAO,OAAA,EAAS;AACnC,MAAA,IAAI,OAAA,IAAW,MAAA,IAAU,OAAO,MAAA,CAAO,UAAU,QAAA,EAAU;AACzD,QAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,EAAG;AAC3C,UAAA,MAAA,CAAO,IAAI,GAAG,CAAA;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,OAAA,IAAW,MAAA,IAAU,OAAO,MAAA,CAAO,UAAU,QAAA,EAAU;AAEhE,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,EAAG;AAC3C,MAAA,MAAA,CAAO,IAAI,GAAG,CAAA;AAAA,IAChB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAyCO,IAAM,cAAN,MAKL;AAAA,EACgB,SAAA;AAAA,EACA,MAAA;AAAA,EACA,EAAA;AAAA,EACA,EAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EAEhB,YAAY,MAAA,EAAwD;AAClE,IAAA,IAAA,CAAK,YAAY,MAAA,CAAO,SAAA;AACxB,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AACrB,IAAA,IAAA,CAAK,KAAK,MAAA,CAAO,EAAA;AACjB,IAAA,IAAA,CAAK,KAAK,MAAA,CAAO,EAAA;AACjB,IAAA,IAAA,CAAK,OAAA,GAAW,MAAA,CAAO,OAAA,IAAW,EAAC;AAGnC,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAK,eAAA,EAAgB;AAAA,EACpC;AAAA,EAEQ,eAAA,GAAsC;AAC5C,IAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA;AAC1C,IAAA,MAAM,OAAkD,EAAC;AAEzD,IAAA,KAAA,MAAW,OAAO,MAAA,EAAQ;AACxB,MAAA,IAAA,CAAK,GAAG,CAAA,GAAI;AAAA,QACV,SAAA,EAAW,GAAA;AAAA,QACX,KAAA,EAAO;AAAA,OACT;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,IAAA,EAAiC;AACxC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,IAAA,EAAuD;AAC/D,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,IAAI,CAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,GAA4C;AAC1C,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EAChD;AACF","file":"index.js","sourcesContent":["import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import type { z } from 'zod';\n\nimport type { DynamoTableConfig, FieldRef, IndexDefinition, SupportedSchema } from './types.js';\n\n/**\n * Extract all field names from a schema (handles both ZodObject and ZodDiscriminatedUnion)\n */\nfunction getSchemaFields(schema: SupportedSchema): Set<string> {\n const fields = new Set<string>();\n\n // Check if it's a discriminated union by looking for 'options' property\n if ('options' in schema && Array.isArray(schema.options)) {\n // It's a discriminated union - collect fields from all options\n for (const option of schema.options) {\n if ('shape' in option && typeof option.shape === 'object') {\n for (const key of Object.keys(option.shape)) {\n fields.add(key);\n }\n }\n }\n } else if ('shape' in schema && typeof schema.shape === 'object') {\n // It's a regular ZodObject\n for (const key of Object.keys(schema.shape)) {\n fields.add(key);\n }\n }\n\n return fields;\n}\n\n/**\n * Creates field references for type-safe query building\n */\ntype FieldRefs<TSchema extends SupportedSchema> = {\n [K in keyof z.infer<TSchema>]: FieldRef<K & string, z.infer<TSchema>[K]>;\n};\n\n/**\n * DynamoTable - Defines a DynamoDB table with type-safe schema\n *\n * Supports both ZodObject and ZodDiscriminatedUnion schemas for single-table design patterns.\n *\n * @example\n * ```ts\n * // Simple schema\n * const TodoTable = new DynamoTable({\n * tableName: 'prod-todo-table',\n * schema: z.object({\n * PK: z.string(),\n * SK: z.string(),\n * text: z.string(),\n * isDone: z.boolean(),\n * }),\n * pk: 'PK',\n * sk: 'SK',\n * });\n *\n * // Discriminated union for single-table design\n * const MultiEntityTable = new DynamoTable({\n * tableName: 'prod-entities',\n * schema: z.discriminatedUnion('entityType', [\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('USER'), name: z.string() }),\n * z.object({ PK: z.string(), SK: z.string(), entityType: z.literal('ITEM'), title: z.string() }),\n * ]),\n * pk: 'PK',\n * sk: 'SK',\n * });\n * ```\n */\nexport class DynamoTable<\n TSchema extends SupportedSchema,\n TPk extends keyof z.infer<TSchema>,\n TSk extends keyof z.infer<TSchema> | undefined = undefined,\n TIndexes extends Record<string, IndexDefinition> = Record<string, IndexDefinition>,\n> {\n public readonly tableName: string;\n public readonly schema: TSchema;\n public readonly pk: TPk;\n public readonly sk: TSk;\n public readonly indexes: TIndexes;\n public readonly field: FieldRefs<TSchema>;\n\n constructor(config: DynamoTableConfig<TSchema, TPk, TSk, TIndexes>) {\n this.tableName = config.tableName;\n this.schema = config.schema;\n this.pk = config.pk;\n this.sk = config.sk as TSk;\n this.indexes = (config.indexes ?? {}) as TIndexes;\n\n // Create field references for query building\n this.field = this.createFieldRefs();\n }\n\n private createFieldRefs(): FieldRefs<TSchema> {\n const fields = getSchemaFields(this.schema);\n const refs: Record<string, FieldRef<string, unknown>> = {};\n\n for (const key of fields) {\n refs[key] = {\n fieldName: key,\n _type: undefined as unknown,\n };\n }\n\n return refs as FieldRefs<TSchema>;\n }\n\n /**\n * Validate an item against the schema\n */\n validate(item: unknown): z.infer<TSchema> {\n return this.schema.parse(item) as z.infer<TSchema>;\n }\n\n /**\n * Safely validate an item, returning a result object\n */\n safeParse(item: unknown): z.ZodSafeParseResult<z.infer<TSchema>> {\n return this.schema.safeParse(item) as z.ZodSafeParseResult<z.infer<TSchema>>;\n }\n\n /**\n * Get the list of field names in the schema\n */\n getFieldNames(): (keyof z.infer<TSchema>)[] {\n return Array.from(getSchemaFields(this.schema)) as (keyof z.infer<TSchema>)[];\n }\n}\n"]}
|
package/dist/infra.cjs
CHANGED
|
@@ -356,15 +356,11 @@ var ReactiveEngine = class extends constructs.Construct {
|
|
|
356
356
|
this.webSocketApi = new apigatewayv2__namespace.WebSocketApi(this, "WebSocketApi", {
|
|
357
357
|
apiName: resourceName("ReactiveWebSocket")
|
|
358
358
|
});
|
|
359
|
-
this.webSocketStage = new apigatewayv2__namespace.WebSocketStage(
|
|
360
|
-
this,
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
stageName: "prod",
|
|
365
|
-
autoDeploy: true
|
|
366
|
-
}
|
|
367
|
-
);
|
|
359
|
+
this.webSocketStage = new apigatewayv2__namespace.WebSocketStage(this, "WebSocketStage", {
|
|
360
|
+
webSocketApi: this.webSocketApi,
|
|
361
|
+
stageName: "prod",
|
|
362
|
+
autoDeploy: true
|
|
363
|
+
});
|
|
368
364
|
this.webSocketUrl = this.webSocketStage.url;
|
|
369
365
|
this.callbackUrl = this.webSocketStage.callbackUrl;
|
|
370
366
|
const cdkOutDir = path__namespace.join(process.cwd(), "cdk.out", ".generated");
|
|
@@ -396,21 +392,17 @@ var ReactiveEngine = class extends constructs.Construct {
|
|
|
396
392
|
timeout,
|
|
397
393
|
logGroup: this.createLogGroup("ConnectLogGroup")
|
|
398
394
|
});
|
|
399
|
-
this.disconnectHandler = new nodejs__namespace.NodejsFunction(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
timeout,
|
|
411
|
-
logGroup: this.createLogGroup("DisconnectLogGroup")
|
|
412
|
-
}
|
|
413
|
-
);
|
|
395
|
+
this.disconnectHandler = new nodejs__namespace.NodejsFunction(this, "DisconnectHandler", {
|
|
396
|
+
functionName: resourceName("ReactiveDisconnect"),
|
|
397
|
+
runtime: lambda__namespace.Runtime.NODEJS_24_X,
|
|
398
|
+
handler: "disconnectHandler",
|
|
399
|
+
entry: entryPointPath,
|
|
400
|
+
bundling,
|
|
401
|
+
environment,
|
|
402
|
+
memorySize,
|
|
403
|
+
timeout,
|
|
404
|
+
logGroup: this.createLogGroup("DisconnectLogGroup")
|
|
405
|
+
});
|
|
414
406
|
this.messageHandler = new nodejs__namespace.NodejsFunction(this, "MessageHandler", {
|
|
415
407
|
functionName: resourceName("ReactiveMessage"),
|
|
416
408
|
runtime: lambda__namespace.Runtime.NODEJS_24_X,
|
|
@@ -473,11 +465,7 @@ var ReactiveEngine = class extends constructs.Construct {
|
|
|
473
465
|
});
|
|
474
466
|
iam__namespace.Grant.addToPrincipal({
|
|
475
467
|
grantee: this.streamHandler,
|
|
476
|
-
actions: [
|
|
477
|
-
"dynamodb:GetItem",
|
|
478
|
-
"dynamodb:Query",
|
|
479
|
-
"dynamodb:UpdateItem"
|
|
480
|
-
],
|
|
468
|
+
actions: ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:UpdateItem"],
|
|
481
469
|
resourceArns: [this.systemTables.queriesTable.tableArn]
|
|
482
470
|
});
|
|
483
471
|
const managementPolicy = new iam__namespace.PolicyStatement({
|
|
@@ -598,16 +586,14 @@ var ReactiveEngine = class extends constructs.Construct {
|
|
|
598
586
|
if (!(child instanceof dynamodb__namespace.CfnTable) && !(child instanceof dynamodb__namespace.CfnGlobalTable)) {
|
|
599
587
|
return;
|
|
600
588
|
}
|
|
601
|
-
const specification = cdk2__namespace.Stack.of(this).resolve(
|
|
602
|
-
child.streamSpecification
|
|
603
|
-
);
|
|
589
|
+
const specification = cdk2__namespace.Stack.of(this).resolve(child.streamSpecification);
|
|
604
590
|
if (!specification || cdk2__namespace.Token.isUnresolved(specification) || typeof specification !== "object" || !("streamViewType" in specification)) {
|
|
605
591
|
return;
|
|
606
592
|
}
|
|
607
593
|
const streamViewType = specification.streamViewType;
|
|
608
594
|
if (!cdk2__namespace.Token.isUnresolved(streamViewType) && streamViewType !== dynamodb__namespace.StreamViewType.NEW_AND_OLD_IMAGES) {
|
|
609
595
|
throw new Error(
|
|
610
|
-
`Table ${definition.tableName} uses stream view ${streamViewType}; NEW_AND_OLD_IMAGES is required.`
|
|
596
|
+
`Table ${definition.tableName} uses stream view ${String(streamViewType)}; NEW_AND_OLD_IMAGES is required.`
|
|
611
597
|
);
|
|
612
598
|
}
|
|
613
599
|
}
|
package/dist/infra.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../infra/src/handlers/lambda-handlers.ts","../../core/src/schema.ts","../../infra/src/tables.ts","../../infra/src/engine.ts"],"names":["z","Construct","cdk","dynamodb","cdk2","apigatewayv2","path","fs","nodejs","lambda","iam","apigatewayv2Integrations","logs","dynamodb2"],"mappings":"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","file":"infra.cjs","sourcesContent":["/**\n * Entry point code generator for the reactive Lambda handlers.\n *\n * No user code is imported - the handlers use only environment variables\n * and stored query metadata for all operations.\n */\n\n/**\n * Generate the entry point code for Lambda functions.\n * This is called by the CDK construct to create the bundled entry file.\n */\nexport function generateEntryPointCode(): string {\n return `// Auto-generated entry point for reactive Lambda handlers\n// No user code required - all configuration comes from environment variables\nimport { createLambdaHandlers } from 'dynamodb-reactive/server';\n\nconst handlers = createLambdaHandlers();\n\nexport const connectHandler = handlers.connectHandler;\nexport const disconnectHandler = handlers.disconnectHandler;\nexport const messageHandler = handlers.messageHandler;\nexport const streamHandler = handlers.streamHandler;\n`;\n}\n","import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import { SystemTableNames } from '@dynamodb-reactive/core';\nimport * as cdk from 'aws-cdk-lib';\nimport * as dynamodb from 'aws-cdk-lib/aws-dynamodb';\nimport { Construct } from 'constructs';\n\n/**\n * Provisioning mode for DynamoDB tables\n * - \"ON_DEMAND\": Pay-per-request billing (default)\n * - { readWrite: number }: Provisioned capacity with specified RCU/WCU\n */\nexport type ProvisioningMode = 'ON_DEMAND' | { readWrite: number };\n\n/**\n * Props for ReactiveSystemTables\n */\nexport interface ReactiveSystemTablesProps {\n /**\n * Nonempty, concrete prefix for the fixed physical table names.\n * Use the same value as the application's `tablePrefix`.\n */\n tablePrefix: string;\n\n /**\n * Provisioning mode for tables\n * @default \"ON_DEMAND\"\n */\n provisioningMode?: ProvisioningMode;\n\n /**\n * Policy applied when a system table is removed or replaced.\n * Set to RemovalPolicy.DESTROY only for disposable environments.\n * Retained tables have fixed names, so recreating a stack with the same\n * prefix requires importing or manually removing any retained tables first.\n * @default RemovalPolicy.RETAIN\n */\n removalPolicy?: cdk.RemovalPolicy;\n}\n\n/**\n * Creates the three system tables for the reactive engine\n */\nexport class ReactiveSystemTables extends Construct {\n /**\n * ReactiveConnections table - tracks WebSocket connections\n */\n public readonly connectionsTable: dynamodb.Table;\n\n /**\n * ReactiveDependencies table - the inverted index\n */\n public readonly dependenciesTable: dynamodb.Table;\n\n /**\n * ReactiveConnectionQueries table - stores subscription state\n */\n public readonly queriesTable: dynamodb.Table;\n\n constructor(\n scope: Construct,\n id: string,\n props: ReactiveSystemTablesProps,\n ) {\n super(scope, id);\n\n if (\n cdk.Token.isUnresolved(props.tablePrefix) ||\n !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(props.tablePrefix) ||\n props.tablePrefix.length > 229\n ) {\n throw new Error(\n 'ReactiveSystemTables tablePrefix must be a nonempty concrete value of at most 229 letters, numbers, hyphens, or underscores, starting with a letter or number.',\n );\n }\n\n const tableName = (name: string): string =>\n `${props.tablePrefix}-${name}`;\n const provisioningMode = props.provisioningMode ?? 'ON_DEMAND';\n const removalPolicy = props.removalPolicy ?? cdk.RemovalPolicy.RETAIN;\n\n // Determine billing/capacity settings\n const isOnDemand = provisioningMode === 'ON_DEMAND';\n const capacityProps = isOnDemand\n ? { billingMode: dynamodb.BillingMode.PAY_PER_REQUEST }\n : {\n billingMode: dynamodb.BillingMode.PROVISIONED,\n readCapacity: provisioningMode.readWrite,\n writeCapacity: provisioningMode.readWrite,\n };\n\n // ReactiveConnections Table\n // Tracks active WebSocket connections and user context\n this.connectionsTable = new dynamodb.Table(this, 'ConnectionsTable', {\n tableName: tableName(SystemTableNames.connections),\n partitionKey: {\n name: 'connectionId',\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n\n // ReactiveDependencies Table (The Inverted Index)\n // Maps Field#Value -> ConnectionID for O(1) lookups\n this.dependenciesTable = new dynamodb.Table(this, 'DependenciesTable', {\n tableName: tableName(SystemTableNames.dependencies),\n partitionKey: {\n name: 'pk', // Format: \"TableName#FieldName#FieldValue\"\n type: dynamodb.AttributeType.STRING,\n },\n sortKey: {\n name: 'sk', // Format: \"ConnectionID#SubscriptionID\"\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n\n // Add GSI for querying by connectionId (for cleanup)\n // For provisioned mode, GSI needs its own capacity\n const gsiProps = isOnDemand\n ? {}\n : {\n readCapacity: provisioningMode.readWrite,\n writeCapacity: provisioningMode.readWrite,\n };\n\n this.dependenciesTable.addGlobalSecondaryIndex({\n indexName: 'byConnectionId',\n partitionKey: {\n name: 'connectionId',\n type: dynamodb.AttributeType.STRING,\n },\n projectionType: dynamodb.ProjectionType.KEYS_ONLY,\n ...gsiProps,\n });\n\n // ReactiveConnectionQueries Table\n // Stores subscription state for diffing\n this.queriesTable = new dynamodb.Table(this, 'QueriesTable', {\n tableName: tableName(SystemTableNames.queries),\n partitionKey: {\n name: 'pk', // ConnectionID\n type: dynamodb.AttributeType.STRING,\n },\n sortKey: {\n name: 'sk', // SubscriptionID\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { AnyDynamoTable } from '@dynamodb-reactive/core';\nimport * as cdk from 'aws-cdk-lib';\nimport * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';\nimport * as apigatewayv2Integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';\nimport * as dynamodb from 'aws-cdk-lib/aws-dynamodb';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs';\nimport * as logs from 'aws-cdk-lib/aws-logs';\nimport { Construct } from 'constructs';\n\nimport { generateEntryPointCode } from './handlers/lambda-handlers.js';\nimport {\n ReactiveSystemTables,\n type ReactiveSystemTablesProps,\n} from './tables.js';\n\nconst PARTIQL_ACTIONS = [\n 'dynamodb:PartiQLInsert',\n 'dynamodb:PartiQLUpdate',\n 'dynamodb:PartiQLDelete',\n];\nconst DEFAULT_STREAM_BATCH_SIZE = 100;\nconst MAX_STREAM_BATCH_SIZE = 1_000;\n\n/** Resource limits that can be tuned for the DynamoDB stream processor. */\nexport type ReactiveStreamHandlerProps = Pick<\n nodejs.NodejsFunctionProps,\n 'memorySize' | 'timeout'\n>;\n\n/** Associates an application table definition with its deployed CDK table. */\nexport interface ReactiveTableBinding {\n readonly definition: AnyDynamoTable;\n\n /**\n * Deployed table with DynamoDB Streams configured for NEW_AND_OLD_IMAGES.\n * Imported ITable resources must supply tableStreamArn; CDK cannot expose\n * their stream view type, so callers remain responsible for that setting.\n */\n readonly table: dynamodb.ITable;\n}\n\n/** Props for ReactiveEngine construct. */\nexport interface ReactiveEngineProps {\n /** User-defined tables and their deployed CDK resources. */\n tables: readonly ReactiveTableBinding[];\n\n /**\n * Authorizer applied to the WebSocket $connect route.\n * WebSocketIamAuthorizer is supported; its requestContext.identity is\n * persisted with the connection. Lambda authorizer context is also persisted.\n * Authorizers whose bind result uses NONE authorization are rejected.\n */\n connectAuthorizer?: apigatewayv2.IWebSocketRouteAuthorizer;\n\n /**\n * Explicitly permit unauthenticated WebSocket connections.\n * Cannot be combined with connectAuthorizer.\n * @default false\n */\n allowUnauthenticatedConnections?: boolean;\n\n /**\n * Nonempty, concrete prefix for physical resource names. Pass this same value\n * as the server application's `tablePrefix`.\n */\n resourcePrefix: string;\n\n /** System tables configuration. */\n systemTablesProps?: Omit<ReactiveSystemTablesProps, 'tablePrefix'>;\n\n /** Environment variables for Lambda functions. */\n environment?: Record<string, string>;\n\n /** Memory and timeout overrides for the DynamoDB stream processor. */\n streamHandlerProps?: ReactiveStreamHandlerProps;\n\n /** Number of DynamoDB stream records delivered in one invocation. @default 100 */\n streamBatchSize?: number;\n}\n\n/** Main CDK construct for the reactive DynamoDB system. */\nexport class ReactiveEngine extends Construct {\n public readonly tables: readonly ReactiveTableBinding[];\n public readonly systemTables: ReactiveSystemTables;\n public readonly webSocketApi: apigatewayv2.WebSocketApi;\n public readonly webSocketStage: apigatewayv2.WebSocketStage;\n public readonly connectHandler: lambda.Function;\n public readonly disconnectHandler: lambda.Function;\n public readonly messageHandler: lambda.Function;\n public readonly streamHandler: lambda.Function;\n public readonly webSocketUrl: string;\n public readonly callbackUrl: string;\n\n constructor(scope: Construct, id: string, props: ReactiveEngineProps) {\n super(scope, id);\n\n if (\n cdk.Token.isUnresolved(props.resourcePrefix) ||\n !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(props.resourcePrefix) ||\n props.resourcePrefix.length > 45\n ) {\n throw new Error(\n 'ReactiveEngine resourcePrefix must be a nonempty concrete value of at most 45 letters, numbers, hyphens, or underscores, starting with a letter or number.',\n );\n }\n\n if (props.connectAuthorizer && props.allowUnauthenticatedConnections) {\n throw new Error(\n 'Specify either connectAuthorizer or allowUnauthenticatedConnections, not both.',\n );\n }\n if (\n props.connectAuthorizer instanceof apigatewayv2.WebSocketNoneAuthorizer\n ) {\n throw new Error(\n 'WebSocketNoneAuthorizer does not authenticate connections. Set allowUnauthenticatedConnections to true instead.',\n );\n }\n if (!props.connectAuthorizer && !props.allowUnauthenticatedConnections) {\n throw new Error(\n 'WebSocket authentication is required. Provide connectAuthorizer or explicitly set allowUnauthenticatedConnections to true.',\n );\n }\n if (\n props.streamBatchSize !== undefined &&\n (!Number.isInteger(props.streamBatchSize) ||\n props.streamBatchSize < 1 ||\n props.streamBatchSize > MAX_STREAM_BATCH_SIZE)\n ) {\n throw new Error(\n `ReactiveEngine streamBatchSize must be an integer from 1 to ${MAX_STREAM_BATCH_SIZE}.`,\n );\n }\n\n for (const binding of props.tables) {\n this.validateTableName(binding);\n this.validateStream(binding);\n }\n this.tables = [...props.tables];\n\n const resourceName = (name: string): string =>\n `${props.resourcePrefix}-${name}`;\n const memorySize = 128;\n const timeout = cdk.Duration.seconds(30);\n\n this.systemTables = new ReactiveSystemTables(this, 'SystemTables', {\n ...props.systemTablesProps,\n tablePrefix: props.resourcePrefix,\n });\n\n this.webSocketApi = new apigatewayv2.WebSocketApi(this, 'WebSocketApi', {\n apiName: resourceName('ReactiveWebSocket'),\n });\n\n this.webSocketStage = new apigatewayv2.WebSocketStage(\n this,\n 'WebSocketStage',\n {\n webSocketApi: this.webSocketApi,\n stageName: 'prod',\n autoDeploy: true,\n },\n );\n\n this.webSocketUrl = this.webSocketStage.url;\n this.callbackUrl = this.webSocketStage.callbackUrl;\n\n // Every engine uses the same generated source, so its path and asset are stable.\n const cdkOutDir = path.join(process.cwd(), 'cdk.out', '.generated');\n fs.mkdirSync(cdkOutDir, { recursive: true });\n const entryPointPath = path.join(cdkOutDir, 'reactive-entry.ts');\n fs.writeFileSync(entryPointPath, generateEntryPointCode());\n\n const environment: Record<string, string> = {\n ...props.environment,\n CONNECTIONS_TABLE: this.systemTables.connectionsTable.tableName,\n DEPENDENCIES_TABLE: this.systemTables.dependenciesTable.tableName,\n QUERIES_TABLE: this.systemTables.queriesTable.tableName,\n WEBSOCKET_ENDPOINT: this.callbackUrl,\n LOG_LEVEL: props.environment?.LOG_LEVEL ?? 'INFO',\n };\n const bundling: nodejs.BundlingOptions = {\n format: nodejs.OutputFormat.CJS,\n minify: true,\n sourceMap: false,\n target: 'node24',\n };\n\n this.connectHandler = new nodejs.NodejsFunction(this, 'ConnectHandler', {\n functionName: resourceName('ReactiveConnect'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'connectHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('ConnectLogGroup'),\n });\n\n this.disconnectHandler = new nodejs.NodejsFunction(\n this,\n 'DisconnectHandler',\n {\n functionName: resourceName('ReactiveDisconnect'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'disconnectHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('DisconnectLogGroup'),\n },\n );\n\n this.messageHandler = new nodejs.NodejsFunction(this, 'MessageHandler', {\n functionName: resourceName('ReactiveMessage'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'messageHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('MessageLogGroup'),\n });\n\n this.streamHandler = new nodejs.NodejsFunction(this, 'StreamHandler', {\n functionName: resourceName('ReactiveStream'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'streamHandler',\n entry: entryPointPath,\n bundling,\n environment: {\n ...environment,\n USER_TABLES: props.tables\n .map(({ definition }) => definition.tableName)\n .join(','),\n },\n memorySize: props.streamHandlerProps?.memorySize ?? memorySize,\n timeout: props.streamHandlerProps?.timeout ?? timeout,\n logGroup: this.createLogGroup('StreamLogGroup'),\n });\n\n iam.Grant.addToPrincipal({\n grantee: this.connectHandler,\n actions: ['dynamodb:PutItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:Query', 'dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.queriesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:DeleteItem'],\n resourceArns: [this.systemTables.dependenciesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.messageHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:Query', 'dynamodb:DeleteItem'],\n resourceArns: [this.systemTables.dependenciesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: [\n 'dynamodb:GetItem',\n 'dynamodb:Query',\n 'dynamodb:UpdateItem',\n ],\n resourceArns: [this.systemTables.queriesTable.tableArn],\n });\n\n const managementPolicy = new iam.PolicyStatement({\n actions: ['execute-api:ManageConnections'],\n resources: [\n cdk.Stack.of(this).formatArn({\n service: 'execute-api',\n resource: this.webSocketApi.apiId,\n resourceName: `${this.webSocketStage.stageName}/POST/@connections/*`,\n arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME,\n }),\n ],\n });\n this.messageHandler.addToRolePolicy(managementPolicy);\n this.streamHandler.addToRolePolicy(managementPolicy);\n\n const connectAuthorizer = props.connectAuthorizer\n ? {\n bind: (\n options: apigatewayv2.WebSocketRouteAuthorizerBindOptions,\n ): apigatewayv2.WebSocketRouteAuthorizerConfig => {\n const config = props.connectAuthorizer!.bind(options);\n if (config.authorizationType === 'NONE') {\n throw new Error(\n 'The connectAuthorizer bind result uses NONE authorization. Set allowUnauthenticatedConnections to true instead.',\n );\n }\n return config;\n },\n }\n : undefined;\n\n this.webSocketApi.addRoute('$connect', {\n authorizer: connectAuthorizer,\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'ConnectIntegration',\n this.connectHandler,\n ),\n });\n this.webSocketApi.addRoute('$disconnect', {\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'DisconnectIntegration',\n this.disconnectHandler,\n ),\n });\n this.webSocketApi.addRoute('$default', {\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'DefaultIntegration',\n this.messageHandler,\n ),\n });\n\n for (const [index, binding] of props.tables.entries()) {\n this.setupStreamProcessing(\n binding,\n index,\n props.streamBatchSize ?? DEFAULT_STREAM_BATCH_SIZE,\n );\n }\n\n new cdk.CfnOutput(this, 'WebSocketUrl', {\n value: this.webSocketUrl,\n description: 'WebSocket URL for reactive connections',\n });\n }\n\n /** Grant an external application handler access to reactive and user data. */\n public grantApplicationAccess(grantee: iam.IGrantable): void {\n this.systemTables.connectionsTable.grantReadWriteData(grantee);\n this.systemTables.dependenciesTable.grantReadWriteData(grantee);\n this.systemTables.queriesTable.grantReadWriteData(grantee);\n iam.Grant.addToPrincipal({\n grantee,\n actions: ['dynamodb:TransactWriteItems'],\n resourceArns: [\n this.systemTables.connectionsTable.tableArn,\n this.systemTables.dependenciesTable.tableArn,\n this.systemTables.queriesTable.tableArn,\n ],\n });\n\n for (const { table } of this.tables) {\n table.grantReadWriteData(grantee);\n iam.Grant.addToPrincipal({\n grantee,\n actions: ['dynamodb:PartiQLSelect'],\n resourceArns: [table.tableArn, `${table.tableArn}/index/*`],\n });\n iam.Grant.addToPrincipal({\n grantee,\n actions: PARTIQL_ACTIONS,\n resourceArns: [table.tableArn],\n });\n }\n }\n\n private setupStreamProcessing(\n binding: ReactiveTableBinding,\n index: number,\n batchSize: number,\n ): void {\n const { table } = binding;\n table.grantStreamRead(this.streamHandler);\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:PartiQLSelect'],\n resourceArns: [table.tableArn, `${table.tableArn}/index/*`],\n });\n\n new lambda.EventSourceMapping(this, `UserTableStream${index}`, {\n target: this.streamHandler,\n eventSourceArn: table.tableStreamArn!,\n startingPosition: lambda.StartingPosition.TRIM_HORIZON,\n batchSize,\n maxBatchingWindow: cdk.Duration.seconds(5),\n bisectBatchOnError: true,\n reportBatchItemFailures: true,\n });\n }\n\n private createLogGroup(id: string): logs.LogGroup {\n return new logs.LogGroup(this, id, {\n retention: logs.RetentionDays.ONE_DAY,\n removalPolicy: cdk.RemovalPolicy.DESTROY,\n });\n }\n\n private validateStream(binding: ReactiveTableBinding): void {\n const { definition, table } = binding;\n if (!table.tableStreamArn) {\n throw new Error(\n `Table ${definition.tableName} does not have DynamoDB Streams enabled. NEW_AND_OLD_IMAGES is required.`,\n );\n }\n\n const child = table.node.defaultChild;\n if (\n !(child instanceof dynamodb.CfnTable) &&\n !(child instanceof dynamodb.CfnGlobalTable)\n ) {\n return;\n }\n\n const specification = cdk.Stack.of(this).resolve(\n child.streamSpecification,\n ) as unknown;\n if (\n !specification ||\n cdk.Token.isUnresolved(specification) ||\n typeof specification !== 'object' ||\n !('streamViewType' in specification)\n ) {\n return;\n }\n\n const streamViewType = specification.streamViewType;\n if (\n !cdk.Token.isUnresolved(streamViewType) &&\n streamViewType !== dynamodb.StreamViewType.NEW_AND_OLD_IMAGES\n ) {\n throw new Error(\n `Table ${definition.tableName} uses stream view ${streamViewType}; NEW_AND_OLD_IMAGES is required.`,\n );\n }\n }\n\n private validateTableName(binding: ReactiveTableBinding): void {\n const { definition, table } = binding;\n const child = table.node.defaultChild;\n const childTableName =\n child && typeof child === 'object' && 'tableName' in child\n ? (child as { tableName?: string }).tableName\n : undefined;\n const physicalTableName = !cdk.Token.isUnresolved(table.tableName)\n ? table.tableName\n : childTableName && !cdk.Token.isUnresolved(childTableName)\n ? childTableName\n : table.tableName;\n\n if (!physicalTableName || cdk.Token.isUnresolved(physicalTableName)) {\n throw new Error(\n `Table ${definition.tableName} has an unresolved physical table name. Set an explicit tableName equal to definition.tableName; separately bundled application code cannot coordinate CloudFormation-generated names.`,\n );\n }\n if (physicalTableName !== definition.tableName) {\n throw new Error(\n `Table binding name mismatch: definition.tableName is \"${definition.tableName}\" but the physical table name is \"${physicalTableName}\". Set an explicit matching tableName.`,\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../infra/src/handlers/lambda-handlers.ts","../../core/src/schema.ts","../../infra/src/tables.ts","../../infra/src/engine.ts"],"names":["z","Construct","cdk","dynamodb","cdk2","apigatewayv2","path","fs","nodejs","lambda","iam","apigatewayv2Integrations","logs","dynamodb2"],"mappings":"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","file":"infra.cjs","sourcesContent":["/**\n * Entry point code generator for the reactive Lambda handlers.\n *\n * No user code is imported - the handlers use only environment variables\n * and stored query metadata for all operations.\n */\n\n/**\n * Generate the entry point code for Lambda functions.\n * This is called by the CDK construct to create the bundled entry file.\n */\nexport function generateEntryPointCode(): string {\n return `// Auto-generated entry point for reactive Lambda handlers\n// No user code required - all configuration comes from environment variables\nimport { createLambdaHandlers } from 'dynamodb-reactive/server';\n\nconst handlers = createLambdaHandlers();\n\nexport const connectHandler = handlers.connectHandler;\nexport const disconnectHandler = handlers.disconnectHandler;\nexport const messageHandler = handlers.messageHandler;\nexport const streamHandler = handlers.streamHandler;\n`;\n}\n","import { z } from 'zod';\n\nimport type { FilterCondition } from './types.js';\n\nexport const FilterConditionSchema: z.ZodType<FilterCondition> = z.lazy(() => {\n const comparison = z\n .object({\n type: z.literal('comparison'),\n operator: z.enum(['eq', 'ne', 'gt', 'gte', 'lt', 'lte']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict();\n const queryFunction = z.union([\n z\n .object({\n type: z.literal('function'),\n operator: z.literal('between'),\n field: z.string().min(1),\n value: z.unknown(),\n value2: z.unknown(),\n })\n .strict(),\n z\n .object({\n type: z.literal('function'),\n operator: z.enum(['beginsWith', 'contains']),\n field: z.string().min(1),\n value: z.unknown(),\n })\n .strict(),\n ]);\n const logical = z\n .object({\n type: z.literal('logical'),\n operator: z.enum(['and', 'or', 'not']),\n conditions: z.array(FilterConditionSchema).min(1),\n })\n .strict()\n .superRefine((condition, context) => {\n if (condition.operator === 'not' && condition.conditions.length !== 1) {\n context.addIssue({\n code: 'custom',\n path: ['conditions'],\n message: 'not requires exactly one condition',\n });\n }\n });\n return z.union([comparison, queryFunction, logical]);\n});\n\nexport const QueryMetadataSchema = z\n .object({\n planVersion: z.literal(1),\n tableName: z.string().min(1),\n indexName: z.string().min(1).optional(),\n indexPkField: z.string().min(1).optional(),\n indexSkField: z.string().min(1).optional(),\n filterConditions: z.array(FilterConditionSchema),\n sortField: z.string().min(1).optional(),\n sortOrder: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().positive().optional(),\n })\n .strict()\n .superRefine((metadata, context) => {\n if (metadata.indexName && !metadata.indexPkField) {\n context.addIssue({\n code: 'custom',\n path: ['indexPkField'],\n message: 'Index metadata requires its partition key field',\n });\n }\n if (!metadata.indexName && (metadata.indexPkField || metadata.indexSkField)) {\n context.addIssue({\n code: 'custom',\n path: ['indexName'],\n message: 'Index key fields require an index name',\n });\n }\n if (Boolean(metadata.sortField) !== Boolean(metadata.sortOrder)) {\n context.addIssue({\n code: 'custom',\n path: ['sortOrder'],\n message: 'Sort field and order must be specified together',\n });\n }\n });\n\nconst DeliveryVersionSchema = z.object({\n generation: z.number().int().nonnegative(),\n fromVersion: z.number().int().nonnegative(),\n toVersion: z.number().int().positive(),\n messageId: z.string().min(1),\n});\n\nexport const PendingDeliverySchema = z.discriminatedUnion('type', [\n DeliveryVersionSchema.extend({\n type: z.literal('patch'),\n patches: z.array(\n z.object({\n op: z.enum(['add', 'remove', 'replace', 'move', 'copy', 'test']),\n path: z.string(),\n value: z.unknown().optional(),\n from: z.string().optional(),\n }),\n ),\n }),\n DeliveryVersionSchema.extend({\n type: z.literal('resync'),\n }).strict(),\n]);\n\n/**\n * System table schemas for the reactive engine\n */\nexport const SystemSchemas = {\n /**\n * ReactiveConnectionsV2 - Tracks active WebSocket connections\n */\n connections: z.object({\n connectionId: z.string(),\n status: z.enum(['open', 'closed']),\n context: z.record(z.string(), z.unknown()).optional(),\n authorizerContext: z.record(z.string(), z.unknown()).optional(),\n requestIdentity: z.record(z.string(), z.unknown()).optional(),\n connectedAt: z.number(),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveDependenciesV2 - The inverted index for O(1) lookups\n */\n dependencies: z.object({\n pk: z.string(), // Versioned encoded dependency key\n sk: z.string(), // ConnectionID#SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n tableName: z.string(),\n fieldName: z.string(),\n fieldValue: z.string(),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n ttl: z.number(),\n }),\n\n /**\n * ReactiveConnectionQueriesV2 - Stores subscription state for diffing\n */\n queries: z.object({\n pk: z.string(), // ConnectionID\n sk: z.string(), // SubscriptionID\n connectionId: z.string(),\n subscriptionId: z.string(),\n status: z.enum(['registering', 'active', 'cancelled', 'faulted']),\n generation: z.number().int().nonnegative(),\n registrationId: z.string().min(1),\n queryMetadata: QueryMetadataSchema,\n lastResult: z.array(z.unknown()),\n dependencies: z.array(z.string()),\n version: z.number().int().nonnegative(),\n pendingDelivery: PendingDeliverySchema.optional(),\n createdAt: z.number(),\n updatedAt: z.number(),\n ttl: z.number(),\n }),\n};\n\n/** Storage/protocol generation used by all reactive system tables. */\nexport const SYSTEM_TABLE_PROTOCOL_VERSION = 2 as const;\n\n/**\n * Versioned system table names. The V2 suffix intentionally prevents state\n * written by the incompatible 0.1.x dependency protocol from being reused.\n */\nexport const SystemTableNames = {\n connections: 'ReactiveConnectionsV2',\n dependencies: 'ReactiveDependenciesV2',\n queries: 'ReactiveConnectionQueriesV2',\n} as const;\n","import { SystemTableNames } from '@dynamodb-reactive/core';\nimport * as cdk from 'aws-cdk-lib';\nimport * as dynamodb from 'aws-cdk-lib/aws-dynamodb';\nimport { Construct } from 'constructs';\n\n/**\n * Provisioning mode for DynamoDB tables\n * - \"ON_DEMAND\": Pay-per-request billing (default)\n * - { readWrite: number }: Provisioned capacity with specified RCU/WCU\n */\nexport type ProvisioningMode = 'ON_DEMAND' | { readWrite: number };\n\n/**\n * Props for ReactiveSystemTables\n */\nexport interface ReactiveSystemTablesProps {\n /**\n * Nonempty, concrete prefix for the fixed physical table names.\n * Use the same value as the application's `tablePrefix`.\n */\n tablePrefix: string;\n\n /**\n * Provisioning mode for tables\n * @default \"ON_DEMAND\"\n */\n provisioningMode?: ProvisioningMode;\n\n /**\n * Policy applied when a system table is removed or replaced.\n * Set to RemovalPolicy.DESTROY only for disposable environments.\n * Retained tables have fixed names, so recreating a stack with the same\n * prefix requires importing or manually removing any retained tables first.\n * @default RemovalPolicy.RETAIN\n */\n removalPolicy?: cdk.RemovalPolicy;\n}\n\n/**\n * Creates the three system tables for the reactive engine\n */\nexport class ReactiveSystemTables extends Construct {\n /**\n * ReactiveConnections table - tracks WebSocket connections\n */\n public readonly connectionsTable: dynamodb.Table;\n\n /**\n * ReactiveDependencies table - the inverted index\n */\n public readonly dependenciesTable: dynamodb.Table;\n\n /**\n * ReactiveConnectionQueries table - stores subscription state\n */\n public readonly queriesTable: dynamodb.Table;\n\n constructor(scope: Construct, id: string, props: ReactiveSystemTablesProps) {\n super(scope, id);\n\n if (\n cdk.Token.isUnresolved(props.tablePrefix) ||\n !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(props.tablePrefix) ||\n props.tablePrefix.length > 229\n ) {\n throw new Error(\n 'ReactiveSystemTables tablePrefix must be a nonempty concrete value of at most 229 letters, numbers, hyphens, or underscores, starting with a letter or number.',\n );\n }\n\n const tableName = (name: string): string => `${props.tablePrefix}-${name}`;\n const provisioningMode = props.provisioningMode ?? 'ON_DEMAND';\n const removalPolicy = props.removalPolicy ?? cdk.RemovalPolicy.RETAIN;\n\n // Determine billing/capacity settings\n const isOnDemand = provisioningMode === 'ON_DEMAND';\n const capacityProps = isOnDemand\n ? { billingMode: dynamodb.BillingMode.PAY_PER_REQUEST }\n : {\n billingMode: dynamodb.BillingMode.PROVISIONED,\n readCapacity: provisioningMode.readWrite,\n writeCapacity: provisioningMode.readWrite,\n };\n\n // ReactiveConnections Table\n // Tracks active WebSocket connections and user context\n this.connectionsTable = new dynamodb.Table(this, 'ConnectionsTable', {\n tableName: tableName(SystemTableNames.connections),\n partitionKey: {\n name: 'connectionId',\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n\n // ReactiveDependencies Table (The Inverted Index)\n // Maps Field#Value -> ConnectionID for O(1) lookups\n this.dependenciesTable = new dynamodb.Table(this, 'DependenciesTable', {\n tableName: tableName(SystemTableNames.dependencies),\n partitionKey: {\n name: 'pk', // Format: \"TableName#FieldName#FieldValue\"\n type: dynamodb.AttributeType.STRING,\n },\n sortKey: {\n name: 'sk', // Format: \"ConnectionID#SubscriptionID\"\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n\n // Add GSI for querying by connectionId (for cleanup)\n // For provisioned mode, GSI needs its own capacity\n const gsiProps = isOnDemand\n ? {}\n : {\n readCapacity: provisioningMode.readWrite,\n writeCapacity: provisioningMode.readWrite,\n };\n\n this.dependenciesTable.addGlobalSecondaryIndex({\n indexName: 'byConnectionId',\n partitionKey: {\n name: 'connectionId',\n type: dynamodb.AttributeType.STRING,\n },\n projectionType: dynamodb.ProjectionType.KEYS_ONLY,\n ...gsiProps,\n });\n\n // ReactiveConnectionQueries Table\n // Stores subscription state for diffing\n this.queriesTable = new dynamodb.Table(this, 'QueriesTable', {\n tableName: tableName(SystemTableNames.queries),\n partitionKey: {\n name: 'pk', // ConnectionID\n type: dynamodb.AttributeType.STRING,\n },\n sortKey: {\n name: 'sk', // SubscriptionID\n type: dynamodb.AttributeType.STRING,\n },\n ...capacityProps,\n removalPolicy,\n timeToLiveAttribute: 'ttl',\n });\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { AnyDynamoTable } from '@dynamodb-reactive/core';\nimport * as cdk from 'aws-cdk-lib';\nimport * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';\nimport * as apigatewayv2Integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';\nimport * as dynamodb from 'aws-cdk-lib/aws-dynamodb';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs';\nimport * as logs from 'aws-cdk-lib/aws-logs';\nimport { Construct } from 'constructs';\n\nimport { generateEntryPointCode } from './handlers/lambda-handlers.js';\nimport { ReactiveSystemTables, type ReactiveSystemTablesProps } from './tables.js';\n\nconst PARTIQL_ACTIONS = [\n 'dynamodb:PartiQLInsert',\n 'dynamodb:PartiQLUpdate',\n 'dynamodb:PartiQLDelete',\n];\nconst DEFAULT_STREAM_BATCH_SIZE = 100;\nconst MAX_STREAM_BATCH_SIZE = 1_000;\n\n/** Resource limits that can be tuned for the DynamoDB stream processor. */\nexport type ReactiveStreamHandlerProps = Pick<nodejs.NodejsFunctionProps, 'memorySize' | 'timeout'>;\n\n/** Associates an application table definition with its deployed CDK table. */\nexport interface ReactiveTableBinding {\n readonly definition: AnyDynamoTable;\n\n /**\n * Deployed table with DynamoDB Streams configured for NEW_AND_OLD_IMAGES.\n * Imported ITable resources must supply tableStreamArn; CDK cannot expose\n * their stream view type, so callers remain responsible for that setting.\n */\n readonly table: dynamodb.ITable;\n}\n\n/** Props for ReactiveEngine construct. */\nexport interface ReactiveEngineProps {\n /** User-defined tables and their deployed CDK resources. */\n tables: readonly ReactiveTableBinding[];\n\n /**\n * Authorizer applied to the WebSocket $connect route.\n * WebSocketIamAuthorizer is supported; its requestContext.identity is\n * persisted with the connection. Lambda authorizer context is also persisted.\n * Authorizers whose bind result uses NONE authorization are rejected.\n */\n connectAuthorizer?: apigatewayv2.IWebSocketRouteAuthorizer;\n\n /**\n * Explicitly permit unauthenticated WebSocket connections.\n * Cannot be combined with connectAuthorizer.\n * @default false\n */\n allowUnauthenticatedConnections?: boolean;\n\n /**\n * Nonempty, concrete prefix for physical resource names. Pass this same value\n * as the server application's `tablePrefix`.\n */\n resourcePrefix: string;\n\n /** System tables configuration. */\n systemTablesProps?: Omit<ReactiveSystemTablesProps, 'tablePrefix'>;\n\n /** Environment variables for Lambda functions. */\n environment?: Record<string, string>;\n\n /** Memory and timeout overrides for the DynamoDB stream processor. */\n streamHandlerProps?: ReactiveStreamHandlerProps;\n\n /** Number of DynamoDB stream records delivered in one invocation. @default 100 */\n streamBatchSize?: number;\n}\n\n/** Main CDK construct for the reactive DynamoDB system. */\nexport class ReactiveEngine extends Construct {\n public readonly tables: readonly ReactiveTableBinding[];\n public readonly systemTables: ReactiveSystemTables;\n public readonly webSocketApi: apigatewayv2.WebSocketApi;\n public readonly webSocketStage: apigatewayv2.WebSocketStage;\n public readonly connectHandler: lambda.Function;\n public readonly disconnectHandler: lambda.Function;\n public readonly messageHandler: lambda.Function;\n public readonly streamHandler: lambda.Function;\n public readonly webSocketUrl: string;\n public readonly callbackUrl: string;\n\n constructor(scope: Construct, id: string, props: ReactiveEngineProps) {\n super(scope, id);\n\n if (\n cdk.Token.isUnresolved(props.resourcePrefix) ||\n !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(props.resourcePrefix) ||\n props.resourcePrefix.length > 45\n ) {\n throw new Error(\n 'ReactiveEngine resourcePrefix must be a nonempty concrete value of at most 45 letters, numbers, hyphens, or underscores, starting with a letter or number.',\n );\n }\n\n if (props.connectAuthorizer && props.allowUnauthenticatedConnections) {\n throw new Error(\n 'Specify either connectAuthorizer or allowUnauthenticatedConnections, not both.',\n );\n }\n if (props.connectAuthorizer instanceof apigatewayv2.WebSocketNoneAuthorizer) {\n throw new Error(\n 'WebSocketNoneAuthorizer does not authenticate connections. Set allowUnauthenticatedConnections to true instead.',\n );\n }\n if (!props.connectAuthorizer && !props.allowUnauthenticatedConnections) {\n throw new Error(\n 'WebSocket authentication is required. Provide connectAuthorizer or explicitly set allowUnauthenticatedConnections to true.',\n );\n }\n if (\n props.streamBatchSize !== undefined &&\n (!Number.isInteger(props.streamBatchSize) ||\n props.streamBatchSize < 1 ||\n props.streamBatchSize > MAX_STREAM_BATCH_SIZE)\n ) {\n throw new Error(\n `ReactiveEngine streamBatchSize must be an integer from 1 to ${MAX_STREAM_BATCH_SIZE}.`,\n );\n }\n\n for (const binding of props.tables) {\n this.validateTableName(binding);\n this.validateStream(binding);\n }\n this.tables = [...props.tables];\n\n const resourceName = (name: string): string => `${props.resourcePrefix}-${name}`;\n const memorySize = 128;\n const timeout = cdk.Duration.seconds(30);\n\n this.systemTables = new ReactiveSystemTables(this, 'SystemTables', {\n ...props.systemTablesProps,\n tablePrefix: props.resourcePrefix,\n });\n\n this.webSocketApi = new apigatewayv2.WebSocketApi(this, 'WebSocketApi', {\n apiName: resourceName('ReactiveWebSocket'),\n });\n\n this.webSocketStage = new apigatewayv2.WebSocketStage(this, 'WebSocketStage', {\n webSocketApi: this.webSocketApi,\n stageName: 'prod',\n autoDeploy: true,\n });\n\n this.webSocketUrl = this.webSocketStage.url;\n this.callbackUrl = this.webSocketStage.callbackUrl;\n\n // Every engine uses the same generated source, so its path and asset are stable.\n const cdkOutDir = path.join(process.cwd(), 'cdk.out', '.generated');\n fs.mkdirSync(cdkOutDir, { recursive: true });\n const entryPointPath = path.join(cdkOutDir, 'reactive-entry.ts');\n fs.writeFileSync(entryPointPath, generateEntryPointCode());\n\n const environment: Record<string, string> = {\n ...props.environment,\n CONNECTIONS_TABLE: this.systemTables.connectionsTable.tableName,\n DEPENDENCIES_TABLE: this.systemTables.dependenciesTable.tableName,\n QUERIES_TABLE: this.systemTables.queriesTable.tableName,\n WEBSOCKET_ENDPOINT: this.callbackUrl,\n LOG_LEVEL: props.environment?.LOG_LEVEL ?? 'INFO',\n };\n const bundling: nodejs.BundlingOptions = {\n format: nodejs.OutputFormat.CJS,\n minify: true,\n sourceMap: false,\n target: 'node24',\n };\n\n this.connectHandler = new nodejs.NodejsFunction(this, 'ConnectHandler', {\n functionName: resourceName('ReactiveConnect'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'connectHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('ConnectLogGroup'),\n });\n\n this.disconnectHandler = new nodejs.NodejsFunction(this, 'DisconnectHandler', {\n functionName: resourceName('ReactiveDisconnect'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'disconnectHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('DisconnectLogGroup'),\n });\n\n this.messageHandler = new nodejs.NodejsFunction(this, 'MessageHandler', {\n functionName: resourceName('ReactiveMessage'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'messageHandler',\n entry: entryPointPath,\n bundling,\n environment,\n memorySize,\n timeout,\n logGroup: this.createLogGroup('MessageLogGroup'),\n });\n\n this.streamHandler = new nodejs.NodejsFunction(this, 'StreamHandler', {\n functionName: resourceName('ReactiveStream'),\n runtime: lambda.Runtime.NODEJS_24_X,\n handler: 'streamHandler',\n entry: entryPointPath,\n bundling,\n environment: {\n ...environment,\n USER_TABLES: props.tables.map(({ definition }) => definition.tableName).join(','),\n },\n memorySize: props.streamHandlerProps?.memorySize ?? memorySize,\n timeout: props.streamHandlerProps?.timeout ?? timeout,\n logGroup: this.createLogGroup('StreamLogGroup'),\n });\n\n iam.Grant.addToPrincipal({\n grantee: this.connectHandler,\n actions: ['dynamodb:PutItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:Query', 'dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.queriesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.disconnectHandler,\n actions: ['dynamodb:DeleteItem'],\n resourceArns: [this.systemTables.dependenciesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.messageHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.connectionsTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:Query', 'dynamodb:DeleteItem'],\n resourceArns: [this.systemTables.dependenciesTable.tableArn],\n });\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:GetItem', 'dynamodb:Query', 'dynamodb:UpdateItem'],\n resourceArns: [this.systemTables.queriesTable.tableArn],\n });\n\n const managementPolicy = new iam.PolicyStatement({\n actions: ['execute-api:ManageConnections'],\n resources: [\n cdk.Stack.of(this).formatArn({\n service: 'execute-api',\n resource: this.webSocketApi.apiId,\n resourceName: `${this.webSocketStage.stageName}/POST/@connections/*`,\n arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME,\n }),\n ],\n });\n this.messageHandler.addToRolePolicy(managementPolicy);\n this.streamHandler.addToRolePolicy(managementPolicy);\n\n const connectAuthorizer = props.connectAuthorizer\n ? {\n bind: (\n options: apigatewayv2.WebSocketRouteAuthorizerBindOptions,\n ): apigatewayv2.WebSocketRouteAuthorizerConfig => {\n const config = props.connectAuthorizer!.bind(options);\n if (config.authorizationType === 'NONE') {\n throw new Error(\n 'The connectAuthorizer bind result uses NONE authorization. Set allowUnauthenticatedConnections to true instead.',\n );\n }\n return config;\n },\n }\n : undefined;\n\n this.webSocketApi.addRoute('$connect', {\n authorizer: connectAuthorizer,\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'ConnectIntegration',\n this.connectHandler,\n ),\n });\n this.webSocketApi.addRoute('$disconnect', {\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'DisconnectIntegration',\n this.disconnectHandler,\n ),\n });\n this.webSocketApi.addRoute('$default', {\n integration: new apigatewayv2Integrations.WebSocketLambdaIntegration(\n 'DefaultIntegration',\n this.messageHandler,\n ),\n });\n\n for (const [index, binding] of props.tables.entries()) {\n this.setupStreamProcessing(\n binding,\n index,\n props.streamBatchSize ?? DEFAULT_STREAM_BATCH_SIZE,\n );\n }\n\n new cdk.CfnOutput(this, 'WebSocketUrl', {\n value: this.webSocketUrl,\n description: 'WebSocket URL for reactive connections',\n });\n }\n\n /** Grant an external application handler access to reactive and user data. */\n public grantApplicationAccess(grantee: iam.IGrantable): void {\n this.systemTables.connectionsTable.grantReadWriteData(grantee);\n this.systemTables.dependenciesTable.grantReadWriteData(grantee);\n this.systemTables.queriesTable.grantReadWriteData(grantee);\n iam.Grant.addToPrincipal({\n grantee,\n actions: ['dynamodb:TransactWriteItems'],\n resourceArns: [\n this.systemTables.connectionsTable.tableArn,\n this.systemTables.dependenciesTable.tableArn,\n this.systemTables.queriesTable.tableArn,\n ],\n });\n\n for (const { table } of this.tables) {\n table.grantReadWriteData(grantee);\n iam.Grant.addToPrincipal({\n grantee,\n actions: ['dynamodb:PartiQLSelect'],\n resourceArns: [table.tableArn, `${table.tableArn}/index/*`],\n });\n iam.Grant.addToPrincipal({\n grantee,\n actions: PARTIQL_ACTIONS,\n resourceArns: [table.tableArn],\n });\n }\n }\n\n private setupStreamProcessing(\n binding: ReactiveTableBinding,\n index: number,\n batchSize: number,\n ): void {\n const { table } = binding;\n table.grantStreamRead(this.streamHandler);\n iam.Grant.addToPrincipal({\n grantee: this.streamHandler,\n actions: ['dynamodb:PartiQLSelect'],\n resourceArns: [table.tableArn, `${table.tableArn}/index/*`],\n });\n\n new lambda.EventSourceMapping(this, `UserTableStream${index}`, {\n target: this.streamHandler,\n eventSourceArn: table.tableStreamArn!,\n startingPosition: lambda.StartingPosition.TRIM_HORIZON,\n batchSize,\n maxBatchingWindow: cdk.Duration.seconds(5),\n bisectBatchOnError: true,\n reportBatchItemFailures: true,\n });\n }\n\n private createLogGroup(id: string): logs.LogGroup {\n return new logs.LogGroup(this, id, {\n retention: logs.RetentionDays.ONE_DAY,\n removalPolicy: cdk.RemovalPolicy.DESTROY,\n });\n }\n\n private validateStream(binding: ReactiveTableBinding): void {\n const { definition, table } = binding;\n if (!table.tableStreamArn) {\n throw new Error(\n `Table ${definition.tableName} does not have DynamoDB Streams enabled. NEW_AND_OLD_IMAGES is required.`,\n );\n }\n\n const child = table.node.defaultChild;\n if (!(child instanceof dynamodb.CfnTable) && !(child instanceof dynamodb.CfnGlobalTable)) {\n return;\n }\n\n const specification = cdk.Stack.of(this).resolve(child.streamSpecification) as unknown;\n if (\n !specification ||\n cdk.Token.isUnresolved(specification) ||\n typeof specification !== 'object' ||\n !('streamViewType' in specification)\n ) {\n return;\n }\n\n const streamViewType = specification.streamViewType;\n if (\n !cdk.Token.isUnresolved(streamViewType) &&\n streamViewType !== dynamodb.StreamViewType.NEW_AND_OLD_IMAGES\n ) {\n throw new Error(\n `Table ${definition.tableName} uses stream view ${String(streamViewType)}; NEW_AND_OLD_IMAGES is required.`,\n );\n }\n }\n\n private validateTableName(binding: ReactiveTableBinding): void {\n const { definition, table } = binding;\n const child = table.node.defaultChild;\n const childTableName =\n child && typeof child === 'object' && 'tableName' in child\n ? (child as { tableName?: string }).tableName\n : undefined;\n const physicalTableName = !cdk.Token.isUnresolved(table.tableName)\n ? table.tableName\n : childTableName && !cdk.Token.isUnresolved(childTableName)\n ? childTableName\n : table.tableName;\n\n if (!physicalTableName || cdk.Token.isUnresolved(physicalTableName)) {\n throw new Error(\n `Table ${definition.tableName} has an unresolved physical table name. Set an explicit tableName equal to definition.tableName; separately bundled application code cannot coordinate CloudFormation-generated names.`,\n );\n }\n if (physicalTableName !== definition.tableName) {\n throw new Error(\n `Table binding name mismatch: definition.tableName is \"${definition.tableName}\" but the physical table name is \"${physicalTableName}\". Set an explicit matching tableName.`,\n );\n }\n }\n}\n"]}
|