@velajs/cloudflare 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +8 -3
  2. package/dist/index.d.ts +805 -41
  3. package/dist/index.js +1645 -39
  4. package/dist/index.js.map +1 -0
  5. package/package.json +54 -40
  6. package/dist/binding-ref.d.ts +0 -8
  7. package/dist/binding-ref.js +0 -19
  8. package/dist/cloudflare-application.d.ts +0 -125
  9. package/dist/cloudflare-application.js +0 -186
  10. package/dist/cloudflare-factory.d.ts +0 -88
  11. package/dist/cloudflare-factory.js +0 -107
  12. package/dist/decorators/env.d.ts +0 -16
  13. package/dist/decorators/env.js +0 -22
  14. package/dist/decorators/queue-consumer.d.ts +0 -23
  15. package/dist/decorators/queue-consumer.js +0 -40
  16. package/dist/decorators/scheduled.d.ts +0 -20
  17. package/dist/decorators/scheduled.js +0 -36
  18. package/dist/env-ref.d.ts +0 -10
  19. package/dist/env-ref.js +0 -13
  20. package/dist/modules/ai.module.d.ts +0 -1
  21. package/dist/modules/ai.module.js +0 -8
  22. package/dist/modules/create-binding-module.d.ts +0 -14
  23. package/dist/modules/create-binding-module.js +0 -42
  24. package/dist/modules/d1.module.d.ts +0 -1
  25. package/dist/modules/d1.module.js +0 -8
  26. package/dist/modules/durable-object.module.d.ts +0 -1
  27. package/dist/modules/durable-object.module.js +0 -8
  28. package/dist/modules/env.module.d.ts +0 -17
  29. package/dist/modules/env.module.js +0 -35
  30. package/dist/modules/hyperdrive.module.d.ts +0 -1
  31. package/dist/modules/hyperdrive.module.js +0 -8
  32. package/dist/modules/kv.module.d.ts +0 -1
  33. package/dist/modules/kv.module.js +0 -8
  34. package/dist/modules/queue.module.d.ts +0 -1
  35. package/dist/modules/queue.module.js +0 -8
  36. package/dist/modules/r2.module.d.ts +0 -1
  37. package/dist/modules/r2.module.js +0 -8
  38. package/dist/modules/vectorize.module.d.ts +0 -1
  39. package/dist/modules/vectorize.module.js +0 -8
  40. package/dist/services/ai.service.d.ts +0 -6
  41. package/dist/services/ai.service.js +0 -33
  42. package/dist/services/d1.service.d.ts +0 -6
  43. package/dist/services/d1.service.js +0 -33
  44. package/dist/services/durable-object.service.d.ts +0 -6
  45. package/dist/services/durable-object.service.js +0 -33
  46. package/dist/services/env.service.d.ts +0 -31
  47. package/dist/services/env.service.js +0 -36
  48. package/dist/services/flagship-flag.driver.d.ts +0 -57
  49. package/dist/services/flagship-flag.driver.js +0 -49
  50. package/dist/services/hyperdrive.service.d.ts +0 -12
  51. package/dist/services/hyperdrive.service.js +0 -51
  52. package/dist/services/kv-cache.store.d.ts +0 -19
  53. package/dist/services/kv-cache.store.js +0 -58
  54. package/dist/services/kv-flag.driver.d.ts +0 -48
  55. package/dist/services/kv-flag.driver.js +0 -62
  56. package/dist/services/kv.service.d.ts +0 -11
  57. package/dist/services/kv.service.js +0 -33
  58. package/dist/services/queue.service.d.ts +0 -6
  59. package/dist/services/queue.service.js +0 -33
  60. package/dist/services/r2.service.d.ts +0 -6
  61. package/dist/services/r2.service.js +0 -33
  62. package/dist/services/vectorize.service.d.ts +0 -6
  63. package/dist/services/vectorize.service.js +0 -33
  64. package/dist/storage/index.d.ts +0 -7
  65. package/dist/storage/index.js +0 -6
  66. package/dist/storage/r2-storage.driver.d.ts +0 -19
  67. package/dist/storage/r2-storage.driver.js +0 -61
  68. package/dist/storage/storage-manager.service.d.ts +0 -16
  69. package/dist/storage/storage-manager.service.js +0 -56
  70. package/dist/storage/storage.controller.d.ts +0 -16
  71. package/dist/storage/storage.controller.js +0 -88
  72. package/dist/storage/storage.module.d.ts +0 -7
  73. package/dist/storage/storage.module.js +0 -37
  74. package/dist/storage/storage.service.d.ts +0 -20
  75. package/dist/storage/storage.service.js +0 -78
  76. package/dist/storage/storage.tokens.d.ts +0 -3
  77. package/dist/storage/storage.tokens.js +0 -2
  78. package/dist/storage/storage.types.d.ts +0 -17
  79. package/dist/storage/storage.types.js +0 -1
  80. package/dist/tokens.d.ts +0 -12
  81. package/dist/tokens.js +0 -12
  82. package/dist/types.d.ts +0 -13
  83. package/dist/types.js +0 -1
  84. package/dist/websocket/broadcast.d.ts +0 -15
  85. package/dist/websocket/broadcast.js +0 -26
  86. package/dist/websocket/cf-room-registry.d.ts +0 -23
  87. package/dist/websocket/cf-room-registry.js +0 -65
  88. package/dist/websocket/cf-ws-client.d.ts +0 -28
  89. package/dist/websocket/cf-ws-client.js +0 -83
  90. package/dist/websocket/cloudflare-websocket.module.d.ts +0 -11
  91. package/dist/websocket/cloudflare-websocket.module.js +0 -27
  92. package/dist/websocket/do-bootstrap.d.ts +0 -24
  93. package/dist/websocket/do-bootstrap.js +0 -55
  94. package/dist/websocket/do-live.d.ts +0 -81
  95. package/dist/websocket/do-live.js +0 -197
  96. package/dist/websocket/do-state.d.ts +0 -35
  97. package/dist/websocket/do-state.js +0 -4
  98. package/dist/websocket/do-websocket-host.d.ts +0 -28
  99. package/dist/websocket/do-websocket-host.js +0 -69
  100. package/dist/websocket/index.d.ts +0 -15
  101. package/dist/websocket/index.js +0 -15
  102. package/dist/websocket/room-id.d.ts +0 -6
  103. package/dist/websocket/room-id.js +0 -12
  104. package/dist/websocket/websocket-routing.d.ts +0 -14
  105. package/dist/websocket/websocket-routing.js +0 -45
  106. package/dist/websocket/websocket.durable-object.d.ts +0 -15
  107. package/dist/websocket/websocket.durable-object.js +0 -83
  108. package/dist/websocket/ws-server-holder.d.ts +0 -16
  109. package/dist/websocket/ws-server-holder.js +0 -34
@@ -1,3 +0,0 @@
1
- import { InjectionToken } from '@velajs/vela';
2
- import type { StorageModuleOptions } from './storage.types';
3
- export declare const STORAGE_OPTIONS: InjectionToken<StorageModuleOptions>;
@@ -1,2 +0,0 @@
1
- import { InjectionToken } from "@velajs/vela";
2
- export const STORAGE_OPTIONS = new InjectionToken('STORAGE_OPTIONS');
@@ -1,17 +0,0 @@
1
- /** A named storage disk backed by an R2 bucket binding. */
2
- export interface DiskConfig {
3
- disk: string;
4
- /** R2Bucket binding name from wrangler.toml. */
5
- binding: string;
6
- /** Optional root prefix; supports path-template tokens ({date}/{year}/…). */
7
- root?: string;
8
- }
9
- export interface PresignedUrlConfig {
10
- defaultExpiry: number;
11
- maxExpiry: number;
12
- }
13
- export interface StorageModuleOptions {
14
- disks: DiskConfig[];
15
- defaultDisk: string;
16
- presignedUrl?: PresignedUrlConfig;
17
- }
@@ -1 +0,0 @@
1
- /** A named storage disk backed by an R2 bucket binding. */ export { };
package/dist/tokens.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { InjectionToken } from '@velajs/vela';
2
- import type { BindingRef } from './binding-ref';
3
- import type { EnvRef } from './env-ref';
4
- export declare const KV_BINDING_REF: InjectionToken<BindingRef<unknown>>;
5
- export declare const D1_BINDING_REF: InjectionToken<BindingRef<unknown>>;
6
- export declare const R2_BINDING_REF: InjectionToken<BindingRef<unknown>>;
7
- export declare const QUEUE_BINDING_REF: InjectionToken<BindingRef<unknown>>;
8
- export declare const DO_BINDING_REF: InjectionToken<BindingRef<unknown>>;
9
- export declare const AI_BINDING_REF: InjectionToken<BindingRef<unknown>>;
10
- export declare const VECTORIZE_BINDING_REF: InjectionToken<BindingRef<unknown>>;
11
- export declare const HYPERDRIVE_BINDING_REF: InjectionToken<BindingRef<unknown>>;
12
- export declare const ENV_REF: InjectionToken<EnvRef>;
package/dist/tokens.js DELETED
@@ -1,12 +0,0 @@
1
- import { InjectionToken } from "@velajs/vela";
2
- // Tokens for binding refs — used by services to receive the binding value.
3
- export const KV_BINDING_REF = new InjectionToken('CF_KV_BINDING_REF');
4
- export const D1_BINDING_REF = new InjectionToken('CF_D1_BINDING_REF');
5
- export const R2_BINDING_REF = new InjectionToken('CF_R2_BINDING_REF');
6
- export const QUEUE_BINDING_REF = new InjectionToken('CF_QUEUE_BINDING_REF');
7
- export const DO_BINDING_REF = new InjectionToken('CF_DO_BINDING_REF');
8
- export const AI_BINDING_REF = new InjectionToken('CF_AI_BINDING_REF');
9
- export const VECTORIZE_BINDING_REF = new InjectionToken('CF_VECTORIZE_BINDING_REF');
10
- export const HYPERDRIVE_BINDING_REF = new InjectionToken('CF_HYPERDRIVE_BINDING_REF');
11
- // Token for the whole-env holder (full c.env), backing EnvService.
12
- export const ENV_REF = new InjectionToken('CF_ENV_REF');
package/dist/types.d.ts DELETED
@@ -1,13 +0,0 @@
1
- export interface CloudflareEnv {
2
- [key: string]: unknown;
3
- }
4
- export interface ScheduledRegistration {
5
- instance: unknown;
6
- methodName: string;
7
- cron: string;
8
- }
9
- export interface QueueRegistration {
10
- instance: unknown;
11
- methodName: string;
12
- queueName: string;
13
- }
package/dist/types.js DELETED
@@ -1 +0,0 @@
1
- export { };
@@ -1,15 +0,0 @@
1
- /**
2
- * Push to a room from a Worker HTTP handler / cron / queue consumer (server-
3
- * initiated emit). Resolves the room's Durable Object and calls its `broadcast`
4
- * RPC method — the same canonical room→DO mapping the upgrade route uses, so it
5
- * always reaches the DO holding those sockets.
6
- *
7
- * @example
8
- * ```ts
9
- * // In a controller — ns from DurableObjectService.namespace
10
- * await broadcastToRoom(ns, `org:${id}`, 'order.created', order);
11
- * ```
12
- */
13
- export declare function broadcastToRoom(ns: DurableObjectNamespace, room: string, event: string, data?: unknown, options?: {
14
- exceptIds?: string[];
15
- }): Promise<void>;
@@ -1,26 +0,0 @@
1
- import { roomToDurableId } from "./room-id.js";
2
- /**
3
- * Push to a room from a Worker HTTP handler / cron / queue consumer (server-
4
- * initiated emit). Resolves the room's Durable Object and calls its `broadcast`
5
- * RPC method — the same canonical room→DO mapping the upgrade route uses, so it
6
- * always reaches the DO holding those sockets.
7
- *
8
- * @example
9
- * ```ts
10
- * // In a controller — ns from DurableObjectService.namespace
11
- * await broadcastToRoom(ns, `org:${id}`, 'order.created', order);
12
- * ```
13
- */ export async function broadcastToRoom(ns, room, event, data, options) {
14
- const cmd = {
15
- rooms: [
16
- room
17
- ],
18
- exceptIds: options?.exceptIds,
19
- frame: JSON.stringify({
20
- event,
21
- data
22
- })
23
- };
24
- const stub = ns.get(roomToDurableId(ns, room));
25
- await stub.broadcast(cmd);
26
- }
@@ -1,23 +0,0 @@
1
- import type { BroadcastCommand, RoomRegistry, WsClient } from '@velajs/vela/websocket';
2
- import type { DoStateLike, WsLike } from './do-state';
3
- /**
4
- * Core `RoomRegistry` backed by a Durable Object's hibernatable sockets. Room
5
- * membership lives in tags (hub room, set at accept) + the attachment (dynamic
6
- * `join()`), never in DO instance fields — so it survives hibernation with zero
7
- * rehydration.
8
- */
9
- export declare class CfRoomRegistry implements RoomRegistry {
10
- private readonly ctx;
11
- constructor(ctx: DoStateLike);
12
- register(_client: WsClient): void;
13
- leaveAll(_client: WsClient): void;
14
- join(client: WsClient, room: string): void | Promise<void>;
15
- leave(client: WsClient, room: string): void | Promise<void>;
16
- localIdsInRoom(room: string): string[];
17
- deliverLocal(cmd: BroadcastCommand): void;
18
- private collectRooms;
19
- private socketsForRoom;
20
- private attachmentOf;
21
- /** Reconstruct a `WsClient` for a raw socket (e.g. inside gateway lifecycle scans). */
22
- clientFor(ws: WsLike): WsClient;
23
- }
@@ -1,65 +0,0 @@
1
- import { CfWsClient } from "./cf-ws-client.js";
2
- /**
3
- * Core `RoomRegistry` backed by a Durable Object's hibernatable sockets. Room
4
- * membership lives in tags (hub room, set at accept) + the attachment (dynamic
5
- * `join()`), never in DO instance fields — so it survives hibernation with zero
6
- * rehydration.
7
- */ export class CfRoomRegistry {
8
- ctx;
9
- constructor(ctx){
10
- this.ctx = ctx;
11
- }
12
- // CF sockets are registered with the DO by `acceptWebSocket`; nothing to track.
13
- register(_client) {}
14
- leaveAll(_client) {}
15
- join(client, room) {
16
- return client.join(room);
17
- }
18
- leave(client, room) {
19
- return client.leave(room);
20
- }
21
- localIdsInRoom(room) {
22
- return this.socketsForRoom(room).map((ws)=>this.attachmentOf(ws).connId);
23
- }
24
- deliverLocal(cmd) {
25
- const excludeIds = new Set(cmd.exceptIds ?? []);
26
- const excludeRooms = cmd.exceptRooms ?? [];
27
- // Empty `rooms` => GLOBAL (every socket in this DO). Otherwise the union of
28
- // targeted rooms, deduped per connection.
29
- const targets = cmd.rooms.length === 0 ? this.ctx.getWebSockets() : this.collectRooms(cmd.rooms);
30
- const seen = new Set();
31
- for (const ws of targets){
32
- const att = this.attachmentOf(ws);
33
- if (seen.has(att.connId)) continue;
34
- seen.add(att.connId);
35
- if (excludeIds.has(att.connId)) continue;
36
- if (excludeRooms.some((r)=>att.rooms.includes(r))) continue;
37
- ws.send(cmd.frame);
38
- }
39
- }
40
- collectRooms(rooms) {
41
- const set = new Set();
42
- for (const room of rooms)for (const ws of this.socketsForRoom(room))set.add(ws);
43
- return [
44
- ...set
45
- ];
46
- }
47
- socketsForRoom(room) {
48
- // Membership is the attachment's `rooms`, NOT the hibernation tag: tags are
49
- // immutable after acceptWebSocket, so a socket that left its hub room still
50
- // carries the tag. Since one DO ≈ one room, scanning all sockets in the DO
51
- // and filtering by attachment is both correct and cheap.
52
- return this.ctx.getWebSockets().filter((ws)=>this.attachmentOf(ws).rooms.includes(room));
53
- }
54
- attachmentOf(ws) {
55
- return ws.deserializeAttachment() ?? {
56
- connId: '',
57
- path: '',
58
- rooms: [],
59
- data: {}
60
- };
61
- }
62
- /** Reconstruct a `WsClient` for a raw socket (e.g. inside gateway lifecycle scans). */ clientFor(ws) {
63
- return new CfWsClient(this.ctx, ws);
64
- }
65
- }
@@ -1,28 +0,0 @@
1
- import type { WsClient } from '@velajs/vela/websocket';
2
- import type { DoStateLike, WsLike } from './do-state';
3
- /**
4
- * Core `WsClient` over a native Cloudflare `WebSocket` inside a Durable Object.
5
- * Per-connection state lives in the hibernation attachment (survives eviction),
6
- * so a fresh `CfWsClient` is reconstructed per message with no in-memory state.
7
- */
8
- export declare class CfWsClient<TData extends Record<string, unknown> = Record<string, unknown>> implements WsClient<TData> {
9
- private readonly ctx;
10
- private readonly ws;
11
- private readonly attachment;
12
- constructor(ctx: DoStateLike, ws: WsLike);
13
- get id(): string;
14
- /** The gateway route path this socket connected on (used to route messages). */
15
- get path(): string;
16
- get rooms(): ReadonlySet<string>;
17
- get data(): TData;
18
- set data(value: TData);
19
- get raw(): unknown;
20
- send(event: string, data?: unknown, id?: string): void;
21
- sendRaw(payload: string): void;
22
- join(room: string): void;
23
- leave(room: string): void;
24
- /** Persist `data`/room mutations to the hibernation attachment. */
25
- commit(): void;
26
- close(code?: number, reason?: string): void;
27
- private persist;
28
- }
@@ -1,83 +0,0 @@
1
- const MAX_ATTACHMENT_BYTES = 16_384; // Cloudflare hibernation attachment limit (16 KiB).
2
- const encoder = new TextEncoder();
3
- const EMPTY = {
4
- connId: '',
5
- path: '',
6
- rooms: [],
7
- data: {}
8
- };
9
- /**
10
- * Core `WsClient` over a native Cloudflare `WebSocket` inside a Durable Object.
11
- * Per-connection state lives in the hibernation attachment (survives eviction),
12
- * so a fresh `CfWsClient` is reconstructed per message with no in-memory state.
13
- */ export class CfWsClient {
14
- ctx;
15
- ws;
16
- attachment;
17
- constructor(ctx, ws){
18
- this.ctx = ctx;
19
- this.ws = ws;
20
- const raw = ws.deserializeAttachment();
21
- this.attachment = raw ?? {
22
- ...EMPTY,
23
- data: {}
24
- };
25
- }
26
- get id() {
27
- return this.attachment.connId;
28
- }
29
- /** The gateway route path this socket connected on (used to route messages). */ get path() {
30
- return this.attachment.path;
31
- }
32
- get rooms() {
33
- return new Set(this.attachment.rooms);
34
- }
35
- get data() {
36
- return this.attachment.data;
37
- }
38
- set data(value) {
39
- this.attachment.data = value;
40
- }
41
- get raw() {
42
- return this.ws;
43
- }
44
- send(event, data, id) {
45
- this.ws.send(JSON.stringify(id !== undefined ? {
46
- id,
47
- event,
48
- data
49
- } : {
50
- event,
51
- data
52
- }));
53
- }
54
- sendRaw(payload) {
55
- this.ws.send(payload);
56
- }
57
- join(room) {
58
- if (!this.attachment.rooms.includes(room)) {
59
- this.attachment.rooms.push(room);
60
- this.persist();
61
- }
62
- }
63
- leave(room) {
64
- const next = this.attachment.rooms.filter((r)=>r !== room);
65
- if (next.length !== this.attachment.rooms.length) {
66
- this.attachment.rooms = next;
67
- this.persist();
68
- }
69
- }
70
- /** Persist `data`/room mutations to the hibernation attachment. */ commit() {
71
- this.persist();
72
- }
73
- close(code, reason) {
74
- this.ws.close(code, reason);
75
- }
76
- persist() {
77
- const serialized = JSON.stringify(this.attachment);
78
- if (encoder.encode(serialized).length > MAX_ATTACHMENT_BYTES) {
79
- throw new Error(`WebSocket attachment exceeds the 16 KiB Cloudflare limit. Store large ` + `per-connection state in Durable Object storage keyed by connId instead.`);
80
- }
81
- this.ws.serializeAttachment(this.attachment);
82
- }
83
- }
@@ -1,11 +0,0 @@
1
- import type { DynamicModule } from '@velajs/vela';
2
- /**
3
- * Cloudflare counterpart to the core `WebSocketModule.forRoot()`. Import this in
4
- * your `AppModule` instead: it provides the gateway dispatcher plus a late-bound
5
- * `WS_SERVER` (`WsServerHolder`) that the WebSocket Durable Object wires to a
6
- * ctx-backed server per instance. `useClass` ensures a fresh holder per DI
7
- * container so colocated DO instances never share a server.
8
- */
9
- export declare class CloudflareWebSocketModule {
10
- static forRoot(): DynamicModule;
11
- }
@@ -1,27 +0,0 @@
1
- import { WsDispatcher, WS_SERVER } from "@velajs/vela/websocket";
2
- import { WsServerHolder } from "./ws-server-holder.js";
3
- /**
4
- * Cloudflare counterpart to the core `WebSocketModule.forRoot()`. Import this in
5
- * your `AppModule` instead: it provides the gateway dispatcher plus a late-bound
6
- * `WS_SERVER` (`WsServerHolder`) that the WebSocket Durable Object wires to a
7
- * ctx-backed server per instance. `useClass` ensures a fresh holder per DI
8
- * container so colocated DO instances never share a server.
9
- */ export class CloudflareWebSocketModule {
10
- static forRoot() {
11
- const providers = [
12
- {
13
- provide: WS_SERVER,
14
- useClass: WsServerHolder
15
- },
16
- WsDispatcher
17
- ];
18
- return {
19
- module: CloudflareWebSocketModule,
20
- providers,
21
- exports: [
22
- WS_SERVER,
23
- WsDispatcher
24
- ]
25
- };
26
- }
27
- }
@@ -1,24 +0,0 @@
1
- import type { Type } from '@velajs/vela';
2
- import { WsDispatcher } from '@velajs/vela/websocket';
3
- import type { WsServer } from '@velajs/vela/websocket';
4
- import type { LiveEngine } from '@velajs/vela/live';
5
- import { CfRoomRegistry } from './cf-room-registry';
6
- import type { DoStateLike } from './do-state';
7
- export interface DoRuntime {
8
- dispatcher: WsDispatcher;
9
- registry: CfRoomRegistry;
10
- server: WsServer;
11
- /** Gateway paths from `app.entrypoints.ofKind('websocket')` (discovery order). */
12
- gatewayPaths: string[];
13
- /** The live-query engine (undefined when the app doesn't import LiveModule). */
14
- live?: LiveEngine;
15
- close(signal?: string): Promise<void>;
16
- }
17
- /**
18
- * Slim DI bootstrap for the Durable Object isolate: wires the container and runs
19
- * `OnModuleInit`/`OnApplicationBootstrap` (so `WsDispatcher` discovers gateways)
20
- * WITHOUT building the Hono app/routes the DO never serves. Cloudflare binding
21
- * refs are initialized straight from the DO's `env`, and the ctx-backed server
22
- * is bound before bootstrap lifecycle so gateway `afterInit`/handlers see it.
23
- */
24
- export declare function buildDoRuntime(rootModule: Type, ctx: DoStateLike, env: Record<string, unknown>): Promise<DoRuntime>;
@@ -1,55 +0,0 @@
1
- import { bootstrap, VelaApplication } from "@velajs/vela";
2
- import { local, WsDispatcher, WsServerImpl, WS_SERVER } from "@velajs/vela/websocket";
3
- import { BindingRef } from "../binding-ref.js";
4
- import { EnvRef } from "../env-ref.js";
5
- import { CfRoomRegistry } from "./cf-room-registry.js";
6
- import { initDoLive } from "./do-live.js";
7
- import { WsServerHolder } from "./ws-server-holder.js";
8
- /**
9
- * Slim DI bootstrap for the Durable Object isolate: wires the container and runs
10
- * `OnModuleInit`/`OnApplicationBootstrap` (so `WsDispatcher` discovers gateways)
11
- * WITHOUT building the Hono app/routes the DO never serves. Cloudflare binding
12
- * refs are initialized straight from the DO's `env`, and the ctx-backed server
13
- * is bound before bootstrap lifecycle so gateway `afterInit`/handlers see it.
14
- */ export async function buildDoRuntime(rootModule, ctx, env) {
15
- const { container, routeManager, loader } = await bootstrap(rootModule);
16
- // Init binding refs from env directly (no request middleware inside a DO).
17
- // Enumerate per-instance useValue providers across ALL module buckets — two
18
- // same-type binding modules share one token but live in distinct buckets, so
19
- // resolving the token would return only the first and leave the rest uninitialized.
20
- for (const value of container.getUseValues()){
21
- if (value instanceof EnvRef) value._initialize(env);
22
- else if (value instanceof BindingRef) value._initialize(env[value.bindingName]);
23
- }
24
- const registry = new CfRoomRegistry(ctx);
25
- const driver = local();
26
- driver.bind(registry);
27
- const server = new WsServerImpl(driver);
28
- try {
29
- const holder = container.resolve(WS_SERVER);
30
- if (holder instanceof WsServerHolder) holder.setTarget(server);
31
- } catch {
32
- // CloudflareWebSocketModule not imported — gateways won't have a server.
33
- }
34
- const app = new VelaApplication(container, routeManager);
35
- app.setInstances(await loader.resolveAllInstances());
36
- await app.callOnModuleInit();
37
- await app.callOnApplicationBootstrap();
38
- // The entrypoint registry is the transport contract: one 'websocket' entry
39
- // per discovered gateway ({ meta: { path, dispatcher } }). Built by
40
- // callOnApplicationBootstrap(), so this slim no-routes path has it too.
41
- const wsEntrypoints = app.entrypoints.ofKind('websocket');
42
- // Live queries: wire the SQLite cursor log + local driver mode and replay
43
- // hibernation-persisted subscriptions into the fresh engine.
44
- const live = initDoLive(app, container, ctx);
45
- return {
46
- // Zero gateways still yields a live dispatcher (module imported, nothing
47
- // decorated) — fall back to resolving it directly.
48
- dispatcher: wsEntrypoints[0]?.meta.dispatcher ?? app.get(WsDispatcher),
49
- registry,
50
- server,
51
- gatewayPaths: wsEntrypoints.map((ep)=>ep.meta.path),
52
- live,
53
- close: (signal)=>app.close(signal)
54
- };
55
- }
@@ -1,81 +0,0 @@
1
- import type { Container } from '@velajs/vela';
2
- import type { CommitStamp, CursorLog, LiveDriver, LiveEngine, ResumeVerdict } from '@velajs/vela/live';
3
- import type { DoStateLike, SqlStorageLike } from './do-state';
4
- /**
5
- * The durable `CursorLog`: an append-only tag-invalidation log in the DO's
6
- * SQLite (`__vela_live_log`, AUTOINCREMENT seq = cursor) plus an epoch UUID in
7
- * `__vela_live_meta`. Because the cursor survives hibernation AND trims (it is
8
- * read from `sqlite_sequence`, lunora's `ctx-db-cdc.ts` trick), a reconnecting
9
- * client whose gap the log still covers gets a tiny `resume` instead of a
10
- * re-run — the real-resume half of the live protocol.
11
- *
12
- * Constructed un-initialized at module-composition time (the same app module
13
- * bootstraps in the Worker AND in each DO); `initDoLive` wires the SQLite
14
- * handle inside the DO. In the Worker isolate it stays un-initialized — and is
15
- * never consulted there, because `durableObjectLive()` routes every
16
- * invalidation to the room DO's log (one log scope per room, exactly the
17
- * protocol's model).
18
- */
19
- export declare class DoCursorLog implements CursorLog {
20
- private readonly maxRows;
21
- private sql?;
22
- private epoch?;
23
- constructor(maxRows?: number);
24
- /** @internal — called by `initDoLive` with the DO's `ctx.storage.sql`. */
25
- _initialize(sql: SqlStorageLike): void;
26
- append(tags: string[]): CommitStamp;
27
- current(): CommitStamp;
28
- evaluateResume(sinceCursor: number, sinceEpoch: string, subscriptionTags: string[]): ResumeVerdict;
29
- private assertReady;
30
- }
31
- export interface DurableObjectLiveOptions {
32
- /** The wrangler binding name of the WebSocket DO namespace (e.g. `'CHAT_ROOM'`). */
33
- binding: string;
34
- /** Room used when an invalidation names none. Matches the client default. */
35
- defaultRoom?: string;
36
- }
37
- export interface CfLiveDriver extends LiveDriver {
38
- /** @internal — Worker isolate: capture `env` so the namespace binding resolves per dispatch. */
39
- _initializeEnv(env: Record<string, unknown>): void;
40
- /** @internal — DO isolate: deliver invalidations straight to this DO's engine. */
41
- _setLocalMode(): void;
42
- }
43
- /**
44
- * The Cloudflare `LiveDriver`. Dual-mode, because the SAME app module
45
- * bootstraps in both isolates:
46
- *
47
- * - **Worker** (HTTP mutations, queue consumers, crons): route the command to
48
- * the room's Durable Object over the `invalidate` RPC — the same canonical
49
- * `roomToDurableId` mapping the upgrade route and `broadcastToRoom` use —
50
- * and return THAT log scope's commit stamp (what `Vela-Commit-Cursor`
51
- * must carry).
52
- * - **DO** (writes issued from inside the object): apply to the local engine.
53
- */
54
- export declare function durableObjectLive(options: DurableObjectLiveOptions): CfLiveDriver;
55
- /** Worker-side wiring, called from `cloudflareAdapter`'s first-request middleware. */
56
- export declare function initializeWorkerLive(container: Container, env: Record<string, unknown>): void;
57
- /** The app-facing surface of the engine reached through `app.entrypoints.ofKind('live')`. */
58
- interface EntrypointsApp {
59
- entrypoints: {
60
- ofKind<M>(kind: string): Array<{
61
- meta: M;
62
- }>;
63
- };
64
- }
65
- /**
66
- * DO-side wiring, called from `buildDoRuntime`: initialize the SQLite cursor
67
- * log, flip the driver to local mode, and replay every hibernation-persisted
68
- * subscription into the (fresh) engine so an eviction is invisible to
69
- * subscribers. Returns the engine for the `invalidate` RPC, or undefined when
70
- * the app doesn't use LiveModule.
71
- */
72
- export declare function initDoLive(app: EntrypointsApp, container: Container, ctx: DoStateLike): LiveEngine | undefined;
73
- /** Ergonomic alias: the log option for `LiveModule.forRoot` on Cloudflare. */
74
- export declare function durableObjectCursorLog(maxRows?: number): DoCursorLog;
75
- /**
76
- * Invalidate live tags in a room from a Worker (controller / cron / queue
77
- * consumer) — the live sibling of `broadcastToRoom`. Returns the room log
78
- * scope's commit stamp for `Vela-Commit-Cursor` stamping.
79
- */
80
- export declare function liveInvalidateToRoom(ns: DurableObjectNamespace, room: string, tags: string[]): Promise<CommitStamp | undefined>;
81
- export {};