@concavejs/runtime-cf-base 0.0.1-alpha.4

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.

Potentially problematic release.


This version of @concavejs/runtime-cf-base might be problematic. Click here for more details.

Files changed (45) hide show
  1. package/dist/adapters/cf-websocket-adapter.d.ts +38 -0
  2. package/dist/adapters/cf-websocket-adapter.js +83 -0
  3. package/dist/durable-objects/concave-do-base.d.ts +158 -0
  4. package/dist/durable-objects/concave-do-base.js +412 -0
  5. package/dist/http/dx-http.d.ts +28 -0
  6. package/dist/http/dx-http.js +306 -0
  7. package/dist/http/http-api.d.ts +1 -0
  8. package/dist/http/http-api.js +262 -0
  9. package/dist/http/index.d.ts +7 -0
  10. package/dist/http/index.js +7 -0
  11. package/dist/index.d.ts +15 -0
  12. package/dist/index.js +25 -0
  13. package/dist/internal.d.ts +4 -0
  14. package/dist/internal.js +4 -0
  15. package/dist/routing/instance.d.ts +25 -0
  16. package/dist/routing/instance.js +101 -0
  17. package/dist/rpc/blobstore-proxy.d.ts +11 -0
  18. package/dist/rpc/blobstore-proxy.js +28 -0
  19. package/dist/rpc/docstore-proxy.d.ts +11 -0
  20. package/dist/rpc/docstore-proxy.js +72 -0
  21. package/dist/rpc/index.d.ts +2 -0
  22. package/dist/rpc/index.js +2 -0
  23. package/dist/sync/cf-websocket-adapter.d.ts +15 -0
  24. package/dist/sync/cf-websocket-adapter.js +22 -0
  25. package/dist/sync/concave-do-udf-executor.d.ts +37 -0
  26. package/dist/sync/concave-do-udf-executor.js +67 -0
  27. package/dist/sync/index.d.ts +2 -0
  28. package/dist/sync/index.js +2 -0
  29. package/dist/udf/executor/do-client-executor.d.ts +14 -0
  30. package/dist/udf/executor/do-client-executor.js +42 -0
  31. package/dist/udf/executor/index.d.ts +9 -0
  32. package/dist/udf/executor/index.js +9 -0
  33. package/dist/udf/executor/inline-executor.d.ts +13 -0
  34. package/dist/udf/executor/inline-executor.js +25 -0
  35. package/dist/udf/executor/isolated-executor.d.ts +24 -0
  36. package/dist/udf/executor/isolated-executor.js +31 -0
  37. package/dist/udf/executor/shim-content.d.ts +1 -0
  38. package/dist/udf/executor/shim-content.js +3 -0
  39. package/dist/worker/create-concave-worker.d.ts +34 -0
  40. package/dist/worker/create-concave-worker.js +162 -0
  41. package/dist/worker/index.d.ts +6 -0
  42. package/dist/worker/index.js +6 -0
  43. package/dist/worker/udf-worker.d.ts +14 -0
  44. package/dist/worker/udf-worker.js +63 -0
  45. package/package.json +45 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cloudflare WebSocket Adapter
3
+ *
4
+ * Adapts Cloudflare Workers WebSocket to the platform-agnostic WebSocketConnection interface
5
+ */
6
+ import { type WebSocketConnection, WebSocketReadyState, type WebSocketMessageHandler, type WebSocketCloseHandler, type WebSocketErrorHandler } from "@concavejs/core/abstractions";
7
+ export declare class CFWebSocketAdapter implements WebSocketConnection {
8
+ private ws;
9
+ private messageHandlers;
10
+ private closeHandlers;
11
+ private errorHandlers;
12
+ constructor(ws: WebSocket);
13
+ get readyState(): WebSocketReadyState;
14
+ send(data: string | ArrayBuffer): void;
15
+ close(code?: number, reason?: string): void;
16
+ onMessage(handler: WebSocketMessageHandler): void;
17
+ onClose(handler: WebSocketCloseHandler): void;
18
+ onError(handler: WebSocketErrorHandler): void;
19
+ /**
20
+ * Internal method to trigger message handlers
21
+ * Call this from your Durable Object's webSocketMessage handler
22
+ */
23
+ triggerMessage(data: string | ArrayBuffer): void;
24
+ /**
25
+ * Internal method to trigger close handlers
26
+ * Call this from your Durable Object's webSocketClose handler
27
+ */
28
+ triggerClose(code?: number, reason?: string): void;
29
+ /**
30
+ * Internal method to trigger error handlers
31
+ * Call this from your Durable Object's webSocketError handler
32
+ */
33
+ triggerError(error: Error): void;
34
+ /**
35
+ * Get the underlying Cloudflare WebSocket
36
+ */
37
+ get underlying(): WebSocket;
38
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Cloudflare WebSocket Adapter
3
+ *
4
+ * Adapts Cloudflare Workers WebSocket to the platform-agnostic WebSocketConnection interface
5
+ */
6
+ import { WebSocketReadyState, } from "@concavejs/core/abstractions";
7
+ export class CFWebSocketAdapter {
8
+ ws;
9
+ messageHandlers = [];
10
+ closeHandlers = [];
11
+ errorHandlers = [];
12
+ constructor(ws) {
13
+ this.ws = ws;
14
+ }
15
+ get readyState() {
16
+ // Map CF WebSocket ready states to our enum
17
+ switch (this.ws.readyState) {
18
+ case WebSocket.READY_STATE_CONNECTING:
19
+ return WebSocketReadyState.CONNECTING;
20
+ case WebSocket.READY_STATE_OPEN:
21
+ return WebSocketReadyState.OPEN;
22
+ case WebSocket.READY_STATE_CLOSING:
23
+ return WebSocketReadyState.CLOSING;
24
+ case WebSocket.READY_STATE_CLOSED:
25
+ return WebSocketReadyState.CLOSED;
26
+ default:
27
+ return WebSocketReadyState.CLOSED;
28
+ }
29
+ }
30
+ send(data) {
31
+ this.ws.send(data);
32
+ }
33
+ close(code, reason) {
34
+ if (code !== undefined) {
35
+ this.ws.close(code, reason);
36
+ }
37
+ else {
38
+ this.ws.close();
39
+ }
40
+ }
41
+ onMessage(handler) {
42
+ this.messageHandlers.push(handler);
43
+ }
44
+ onClose(handler) {
45
+ this.closeHandlers.push(handler);
46
+ }
47
+ onError(handler) {
48
+ this.errorHandlers.push(handler);
49
+ }
50
+ /**
51
+ * Internal method to trigger message handlers
52
+ * Call this from your Durable Object's webSocketMessage handler
53
+ */
54
+ triggerMessage(data) {
55
+ for (const handler of this.messageHandlers) {
56
+ handler(data);
57
+ }
58
+ }
59
+ /**
60
+ * Internal method to trigger close handlers
61
+ * Call this from your Durable Object's webSocketClose handler
62
+ */
63
+ triggerClose(code, reason) {
64
+ for (const handler of this.closeHandlers) {
65
+ handler(code, reason);
66
+ }
67
+ }
68
+ /**
69
+ * Internal method to trigger error handlers
70
+ * Call this from your Durable Object's webSocketError handler
71
+ */
72
+ triggerError(error) {
73
+ for (const handler of this.errorHandlers) {
74
+ handler(error);
75
+ }
76
+ }
77
+ /**
78
+ * Get the underlying Cloudflare WebSocket
79
+ */
80
+ get underlying() {
81
+ return this.ws;
82
+ }
83
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Base class for ConcaveDO implementations
3
+ *
4
+ * This provides the common functionality shared between runtime-cf and runtime-cloud.
5
+ * Subclasses provide platform-specific wiring through constructor config.
6
+ */
7
+ import { DurableObject } from "cloudflare:workers";
8
+ import type { UdfExec, UdfResult } from "@concavejs/core/udf";
9
+ import type { DocStore, DocumentLogEntry, DatabaseIndexUpdate, Interval, Order, IndexKeyBytes, LatestDocument, TimestampRange, InternalDocumentId, GlobalKey, DocumentPrevTsQuery, SearchIndexDefinition, VectorIndexDefinition } from "@concavejs/core/docstore";
10
+ import type { BlobStore, StorageOptions, StorageMetadata } from "@concavejs/core/abstractions";
11
+ import { ScheduledFunctionExecutor, CronExecutor } from "@concavejs/core";
12
+ import type { SerializedKeyRange } from "@concavejs/core/queryengine";
13
+ import type { JSONValue } from "convex/values";
14
+ /**
15
+ * Configuration for ConcaveDOBase
16
+ */
17
+ export interface ConcaveDOAdapterContext<Env = any> {
18
+ state: DurableObjectState;
19
+ env: Env;
20
+ instance: string;
21
+ }
22
+ export interface ConcaveDOExecutorContext<Env = any> extends ConcaveDOAdapterContext<Env> {
23
+ docstore: DocStore;
24
+ blobstore?: BlobStore;
25
+ }
26
+ export interface ConcaveDOConfig {
27
+ /** Override the DocStore implementation (default: DODocStore) */
28
+ createDocstore?: (context: ConcaveDOAdapterContext) => DocStore;
29
+ /** Override the BlobStore implementation */
30
+ createBlobstore?: (context: ConcaveDOAdapterContext) => BlobStore | undefined;
31
+ /** Create the UDF executor from resolved runtime services */
32
+ createUdfExecutor: (context: ConcaveDOExecutorContext) => UdfExec;
33
+ /**
34
+ * Pre-computed cron specs to sync on initialization.
35
+ * When provided, these are used directly instead of auto-discovering from modules.
36
+ * Useful for deploy pipelines that extract specs at build time.
37
+ */
38
+ cronSpecs?: Record<string, any>;
39
+ /**
40
+ * Whether to auto-discover cron specs from registered modules (default: true).
41
+ * Set to false to disable auto-discovery (e.g. if no modules are registered globally).
42
+ * Ignored when `cronSpecs` is explicitly provided.
43
+ */
44
+ discoverCrons?: boolean;
45
+ }
46
+ /**
47
+ * Base class for Concave Durable Objects
48
+ *
49
+ * Provides:
50
+ * - Request handling (fetch, syscall, HTTP)
51
+ * - Scheduled function execution
52
+ * - Cron job execution
53
+ * - SyncDO notification
54
+ *
55
+ * Subclasses/configurers provide:
56
+ * - createUdfExecutor(): Platform-specific UDF executor creation
57
+ */
58
+ export declare class ConcaveDOBase extends DurableObject {
59
+ protected _docstore: DocStore;
60
+ protected _blobstore?: BlobStore;
61
+ protected udfExecutor: UdfExec;
62
+ protected doState: DurableObjectState;
63
+ env: any;
64
+ protected scheduler: ScheduledFunctionExecutor;
65
+ protected cronExecutor: CronExecutor;
66
+ constructor(state: DurableObjectState, env: any, config: ConcaveDOConfig);
67
+ /**
68
+ * Initialize scheduler and cron executor
69
+ */
70
+ private initializeSchedulers;
71
+ /**
72
+ * Discover and sync cron specs during DO initialization.
73
+ * Supports both pre-computed specs (from deploy pipelines) and
74
+ * runtime auto-discovery from the global module registry.
75
+ */
76
+ private initializeCronSpecs;
77
+ /**
78
+ * Main request handler
79
+ */
80
+ fetch(request: Request): Promise<Response>;
81
+ /**
82
+ * Handle UDF execution request
83
+ */
84
+ protected handleUdfRequest(request: Request): Promise<Response>;
85
+ /**
86
+ * Handle HTTP action requests
87
+ */
88
+ protected handleHttp(request: Request): Promise<Response>;
89
+ /**
90
+ * Execute a UDF
91
+ */
92
+ protected execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string): Promise<UdfResult>;
93
+ /**
94
+ * Handle scheduled function alarms
95
+ */
96
+ alarm(): Promise<void>;
97
+ /**
98
+ * Reschedule alarms
99
+ */
100
+ protected reschedule(): Promise<void>;
101
+ /**
102
+ * Sync cron specs
103
+ */
104
+ syncCronSpecs(cronSpecs: Record<string, any>): Promise<void>;
105
+ setupSchema(options?: {
106
+ searchIndexes?: SearchIndexDefinition[];
107
+ vectorIndexes?: VectorIndexDefinition[];
108
+ }): Promise<void>;
109
+ write(documents: DocumentLogEntry[], indexes: Set<{
110
+ ts: bigint;
111
+ update: DatabaseIndexUpdate;
112
+ }>, conflictStrategy: "Error" | "Overwrite"): Promise<void>;
113
+ get(id: InternalDocumentId, readTimestamp?: bigint): Promise<LatestDocument | null>;
114
+ scan(table: string, readTimestamp?: bigint): Promise<LatestDocument[]>;
115
+ scanPaginated(table: string, cursor: string | null, limit: number, order: Order, readTimestamp?: bigint): Promise<{
116
+ documents: LatestDocument[];
117
+ nextCursor: string | null;
118
+ hasMore: boolean;
119
+ }>;
120
+ /**
121
+ * Generators return arrays over RPC (cannot stream async generators)
122
+ */
123
+ index_scan(indexId: string, tabletId: string, readTimestamp: bigint, interval: Interval, order: Order): Promise<[IndexKeyBytes, LatestDocument][]>;
124
+ /**
125
+ * Generators return arrays over RPC (cannot stream async generators)
126
+ */
127
+ load_documents(range: TimestampRange, order: Order): Promise<DocumentLogEntry[]>;
128
+ count(table: string): Promise<number>;
129
+ search(indexId: string, searchQuery: string, filters: Map<string, unknown>, options?: {
130
+ limit?: number;
131
+ }): Promise<{
132
+ doc: LatestDocument;
133
+ score: number;
134
+ }[]>;
135
+ vectorSearch(indexId: string, vector: number[], limit: number, filters: Map<string, string>): Promise<{
136
+ doc: LatestDocument;
137
+ score: number;
138
+ }[]>;
139
+ getGlobal(key: GlobalKey): Promise<JSONValue | null>;
140
+ writeGlobal(key: GlobalKey, value: JSONValue): Promise<void>;
141
+ previous_revisions(queries: Set<{
142
+ id: InternalDocumentId;
143
+ ts: bigint;
144
+ }>): Promise<[string, DocumentLogEntry][]>;
145
+ previous_revisions_of_documents(queries: Set<DocumentPrevTsQuery>): Promise<[string, DocumentLogEntry][]>;
146
+ blobstoreStore(buffer: ArrayBuffer, options?: StorageOptions): Promise<StorageMetadata>;
147
+ blobstoreGet(storageId: string): Promise<ArrayBuffer | null>;
148
+ blobstoreDelete(storageId: string): Promise<void>;
149
+ blobstoreGetUrl(storageId: string): Promise<string | null>;
150
+ /**
151
+ * Notify SyncDO of writes for subscription invalidation
152
+ */
153
+ protected notifySyncDo(writtenRanges?: SerializedKeyRange[], writtenTables?: string[] | undefined, commitTimestamp?: bigint): Promise<void>;
154
+ /**
155
+ * Get CORS headers for responses
156
+ */
157
+ protected corsHeaders(request?: Request): Record<string, string>;
158
+ }
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Base class for ConcaveDO implementations
3
+ *
4
+ * This provides the common functionality shared between runtime-cf and runtime-cloud.
5
+ * Subclasses provide platform-specific wiring through constructor config.
6
+ */
7
+ import { DurableObject } from "cloudflare:workers";
8
+ import { DODocStore } from "@concavejs/docstore-cf-do";
9
+ import { writtenTablesFromRanges } from "@concavejs/core/utils";
10
+ import { jsonToConvex } from "convex/values";
11
+ import { getSearchIndexesFromSchema, getVectorIndexesFromSchema } from "@concavejs/core/docstore";
12
+ import { SchemaService } from "@concavejs/core/kernel";
13
+ import { runAsClientCall, runAsServerCall } from "@concavejs/core/udf";
14
+ import { ScheduledFunctionExecutor, CronExecutor } from "@concavejs/core";
15
+ import { resolveAdminAuthConfigFromEnv, resolveJwtValidationConfigFromEnv, resolveSystemAuthConfigFromEnv, setAdminAuthConfig, setJwtValidationConfig, setSystemAuthConfig, } from "@concavejs/core/auth";
16
+ const VERSIONED_API_PREFIX = /^\/api\/\d+\.\d+(?:\.\d+)?(?=\/|$)/;
17
+ function stripApiVersionPrefix(pathname) {
18
+ return pathname.replace(VERSIONED_API_PREFIX, "/api");
19
+ }
20
+ function isReservedApiPath(pathname) {
21
+ const normalizedPath = stripApiVersionPrefix(pathname);
22
+ if (normalizedPath === "/api/execute" ||
23
+ normalizedPath === "/api/sync" ||
24
+ normalizedPath === "/api/reset-test-state" ||
25
+ normalizedPath === "/api/query" ||
26
+ normalizedPath === "/api/mutation" ||
27
+ normalizedPath === "/api/action") {
28
+ return true;
29
+ }
30
+ if (normalizedPath === "/api/storage" || normalizedPath.startsWith("/api/storage/")) {
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+ function shouldHandleAsHttpRoute(pathname) {
36
+ if (pathname.startsWith("/api/http")) {
37
+ return true;
38
+ }
39
+ if (!pathname.startsWith("/api/")) {
40
+ return false;
41
+ }
42
+ return !isReservedApiPath(pathname);
43
+ }
44
+ /**
45
+ * Base class for Concave Durable Objects
46
+ *
47
+ * Provides:
48
+ * - Request handling (fetch, syscall, HTTP)
49
+ * - Scheduled function execution
50
+ * - Cron job execution
51
+ * - SyncDO notification
52
+ *
53
+ * Subclasses/configurers provide:
54
+ * - createUdfExecutor(): Platform-specific UDF executor creation
55
+ */
56
+ export class ConcaveDOBase extends DurableObject {
57
+ _docstore;
58
+ _blobstore;
59
+ udfExecutor;
60
+ doState;
61
+ env;
62
+ scheduler;
63
+ cronExecutor;
64
+ constructor(state, env, config) {
65
+ super(state, env);
66
+ this.doState = state;
67
+ this.env = env;
68
+ const jwtConfig = resolveJwtValidationConfigFromEnv(env);
69
+ if (jwtConfig) {
70
+ setJwtValidationConfig(jwtConfig);
71
+ }
72
+ const adminConfig = resolveAdminAuthConfigFromEnv(env);
73
+ const systemConfig = resolveSystemAuthConfigFromEnv(env);
74
+ setAdminAuthConfig(adminConfig);
75
+ setSystemAuthConfig(systemConfig);
76
+ const instanceId = state.id.name ?? state.id.toString();
77
+ console.log(`[ConcaveDO.constructor] instanceId=${instanceId}`);
78
+ const adapterContext = {
79
+ state,
80
+ env,
81
+ instance: instanceId,
82
+ };
83
+ // Create DocStore (allow override for testing or alternative implementations)
84
+ this._docstore = config.createDocstore ? config.createDocstore(adapterContext) : new DODocStore(state);
85
+ // Create BlobStore (allow override for testing or alternative implementations)
86
+ this._blobstore = config.createBlobstore?.(adapterContext);
87
+ // Create UDF executor from resolved runtime services
88
+ this.udfExecutor = config.createUdfExecutor({
89
+ ...adapterContext,
90
+ docstore: this._docstore,
91
+ blobstore: this._blobstore,
92
+ });
93
+ // Set up scheduler and cron executor
94
+ this.initializeSchedulers();
95
+ // Initialize schema and cron specs
96
+ this.doState.blockConcurrencyWhile(async () => {
97
+ const schemaService = new SchemaService();
98
+ const searchIndexes = await getSearchIndexesFromSchema(schemaService);
99
+ const vectorIndexes = await getVectorIndexesFromSchema(schemaService);
100
+ await this._docstore.setupSchema({ searchIndexes, vectorIndexes });
101
+ // Sync cron specs: use pre-computed if provided, otherwise auto-discover
102
+ await this.initializeCronSpecs(config);
103
+ });
104
+ }
105
+ /**
106
+ * Initialize scheduler and cron executor
107
+ */
108
+ initializeSchedulers() {
109
+ const notifyWrites = async (ranges, tables, commitTimestamp) => {
110
+ await this.notifySyncDo(ranges, tables, commitTimestamp);
111
+ };
112
+ const allocateTimestamp = () => {
113
+ const oracle = this._docstore?.timestampOracle;
114
+ return oracle?.allocateTimestamp ? oracle.allocateTimestamp() : BigInt(Date.now());
115
+ };
116
+ this.scheduler = new ScheduledFunctionExecutor({
117
+ docstore: this._docstore,
118
+ udfExecutor: this.udfExecutor,
119
+ logger: console,
120
+ notifyWrites,
121
+ allocateTimestamp,
122
+ runMutationInTransaction: async (operation) => this.doState.storage.transaction(async () => operation()),
123
+ });
124
+ this.cronExecutor = new CronExecutor({
125
+ docstore: this._docstore,
126
+ udfExecutor: this.udfExecutor,
127
+ logger: console,
128
+ notifyWrites,
129
+ allocateTimestamp,
130
+ });
131
+ }
132
+ /**
133
+ * Discover and sync cron specs during DO initialization.
134
+ * Supports both pre-computed specs (from deploy pipelines) and
135
+ * runtime auto-discovery from the global module registry.
136
+ */
137
+ async initializeCronSpecs(config) {
138
+ try {
139
+ let cronSpecs = null;
140
+ if (config.cronSpecs) {
141
+ // Pre-computed specs provided (e.g. extracted at build/deploy time)
142
+ cronSpecs = config.cronSpecs;
143
+ }
144
+ else if (config.discoverCrons !== false) {
145
+ // Auto-discover from registered modules
146
+ const { discoverCronSpecs } = await import("@concavejs/core/system");
147
+ cronSpecs = await discoverCronSpecs();
148
+ }
149
+ if (cronSpecs && Object.keys(cronSpecs).length > 0) {
150
+ await this.cronExecutor.syncCronSpecs(cronSpecs);
151
+ await this.reschedule();
152
+ }
153
+ }
154
+ catch (error) {
155
+ console.warn("[ConcaveDO] Failed to initialize cron specs:", error?.message ?? error);
156
+ }
157
+ }
158
+ /**
159
+ * Main request handler
160
+ */
161
+ async fetch(request) {
162
+ const url = new URL(request.url);
163
+ if (shouldHandleAsHttpRoute(url.pathname)) {
164
+ return this.handleHttp(request);
165
+ }
166
+ if (url.pathname === "/health") {
167
+ return new Response("OK", { status: 200 });
168
+ }
169
+ return this.handleUdfRequest(request);
170
+ }
171
+ /**
172
+ * Handle UDF execution request
173
+ */
174
+ async handleUdfRequest(request) {
175
+ try {
176
+ const { path, args, type, auth, componentPath, caller } = await request.json();
177
+ const convexArgs = jsonToConvex(args);
178
+ const requestId = crypto.randomUUID();
179
+ const exec = () => this.execute(path, convexArgs, type, auth, componentPath, requestId);
180
+ const result = caller === "server" ? await runAsServerCall(exec, path) : await runAsClientCall(exec);
181
+ if (type === "mutation" || type === "action") {
182
+ this.doState.waitUntil(this.reschedule());
183
+ }
184
+ const writtenTables = writtenTablesFromRanges(result.writtenRanges) ?? [];
185
+ const responseBody = {
186
+ result: result.result,
187
+ readRanges: result.readRanges,
188
+ writtenRanges: result.writtenRanges,
189
+ writtenTables,
190
+ logLines: result.logLines,
191
+ commitTimestamp: result.commitTimestamp ? result.commitTimestamp.toString() : undefined,
192
+ };
193
+ return new Response(JSON.stringify(responseBody), {
194
+ headers: this.corsHeaders(request),
195
+ });
196
+ }
197
+ catch (e) {
198
+ console.error(e);
199
+ return new Response(`Error in Durable Object: ${e.message}`, {
200
+ headers: this.corsHeaders(request),
201
+ status: 500,
202
+ });
203
+ }
204
+ }
205
+ /**
206
+ * Handle HTTP action requests
207
+ */
208
+ async handleHttp(request) {
209
+ try {
210
+ const url = new URL(request.url);
211
+ url.pathname = url.pathname.replace(/^\/api\/http/, "");
212
+ const req = new Request(url.toString(), request);
213
+ const auth = undefined;
214
+ const requestId = crypto.randomUUID();
215
+ return this.udfExecutor.executeHttp(req, auth, requestId);
216
+ }
217
+ catch (e) {
218
+ console.error(e);
219
+ return new Response(`Error in Durable Object: ${e.message}`, { status: 500 });
220
+ }
221
+ }
222
+ /**
223
+ * Execute a UDF
224
+ */
225
+ async execute(path, args, type, auth, componentPath, requestId) {
226
+ return this.udfExecutor.execute(path, args, type, auth, componentPath, requestId);
227
+ }
228
+ /**
229
+ * Handle scheduled function alarms
230
+ */
231
+ async alarm() {
232
+ const scheduledResult = await this.scheduler.runDueJobs();
233
+ const cronResult = await this.cronExecutor.runDueJobs();
234
+ const nextTimes = [scheduledResult.nextScheduledTime, cronResult.nextScheduledTime].filter((t) => t !== null);
235
+ if (nextTimes.length === 0) {
236
+ await this.doState.storage.deleteAlarm();
237
+ }
238
+ else {
239
+ await this.doState.storage.setAlarm(Math.min(...nextTimes));
240
+ }
241
+ }
242
+ /**
243
+ * Reschedule alarms
244
+ */
245
+ async reschedule() {
246
+ const scheduledTime = await this.scheduler.getNextScheduledTime();
247
+ const cronTime = await this.cronExecutor.getNextScheduledTime();
248
+ const nextTimes = [scheduledTime, cronTime].filter((t) => t !== null);
249
+ if (nextTimes.length === 0) {
250
+ await this.doState.storage.deleteAlarm();
251
+ }
252
+ else {
253
+ await this.doState.storage.setAlarm(Math.min(...nextTimes));
254
+ }
255
+ }
256
+ /**
257
+ * Sync cron specs
258
+ */
259
+ async syncCronSpecs(cronSpecs) {
260
+ await this.cronExecutor.syncCronSpecs(cronSpecs);
261
+ await this.reschedule();
262
+ }
263
+ // =============================================================================
264
+ // DocStore RPC Methods - Direct delegation to _docstore
265
+ // =============================================================================
266
+ async setupSchema(options) {
267
+ return this._docstore.setupSchema(options);
268
+ }
269
+ async write(documents, indexes, conflictStrategy) {
270
+ return this._docstore.write(documents, indexes, conflictStrategy);
271
+ }
272
+ async get(id, readTimestamp) {
273
+ return this._docstore.get(id, readTimestamp);
274
+ }
275
+ async scan(table, readTimestamp) {
276
+ return this._docstore.scan(table, readTimestamp);
277
+ }
278
+ async scanPaginated(table, cursor, limit, order, readTimestamp) {
279
+ return this._docstore.scanPaginated(table, cursor, limit, order, readTimestamp);
280
+ }
281
+ /**
282
+ * Generators return arrays over RPC (cannot stream async generators)
283
+ */
284
+ async index_scan(indexId, tabletId, readTimestamp, interval, order) {
285
+ const results = [];
286
+ for await (const item of this._docstore.index_scan(indexId, tabletId, readTimestamp, interval, order)) {
287
+ results.push(item);
288
+ }
289
+ return results;
290
+ }
291
+ /**
292
+ * Generators return arrays over RPC (cannot stream async generators)
293
+ */
294
+ async load_documents(range, order) {
295
+ const results = [];
296
+ for await (const item of this._docstore.load_documents(range, order)) {
297
+ results.push(item);
298
+ }
299
+ return results;
300
+ }
301
+ async count(table) {
302
+ return this._docstore.count(table);
303
+ }
304
+ async search(indexId, searchQuery, filters, options) {
305
+ return this._docstore.search(indexId, searchQuery, filters, options);
306
+ }
307
+ async vectorSearch(indexId, vector, limit, filters) {
308
+ return this._docstore.vectorSearch(indexId, vector, limit, filters);
309
+ }
310
+ async getGlobal(key) {
311
+ return this._docstore.getGlobal(key);
312
+ }
313
+ async writeGlobal(key, value) {
314
+ return this._docstore.writeGlobal(key, value);
315
+ }
316
+ async previous_revisions(queries) {
317
+ const result = await this._docstore.previous_revisions(queries);
318
+ return Array.from(result.entries());
319
+ }
320
+ async previous_revisions_of_documents(queries) {
321
+ const result = await this._docstore.previous_revisions_of_documents(queries);
322
+ return Array.from(result.entries());
323
+ }
324
+ // =============================================================================
325
+ // Blobstore RPC Methods - Prefixed to avoid collision with other methods
326
+ // =============================================================================
327
+ async blobstoreStore(buffer, options) {
328
+ if (!this._blobstore) {
329
+ throw new Error("Blobstore not configured");
330
+ }
331
+ return this._blobstore.store(buffer, options);
332
+ }
333
+ async blobstoreGet(storageId) {
334
+ if (!this._blobstore) {
335
+ throw new Error("Blobstore not configured");
336
+ }
337
+ const result = await this._blobstore.get(storageId);
338
+ if (result === null)
339
+ return null;
340
+ if (result instanceof Blob)
341
+ return result.arrayBuffer();
342
+ return result;
343
+ }
344
+ async blobstoreDelete(storageId) {
345
+ if (!this._blobstore) {
346
+ throw new Error("Blobstore not configured");
347
+ }
348
+ return this._blobstore.delete(storageId);
349
+ }
350
+ async blobstoreGetUrl(storageId) {
351
+ if (!this._blobstore) {
352
+ throw new Error("Blobstore not configured");
353
+ }
354
+ return this._blobstore.getUrl(storageId);
355
+ }
356
+ /**
357
+ * Notify SyncDO of writes for subscription invalidation
358
+ */
359
+ async notifySyncDo(writtenRanges, writtenTables, commitTimestamp) {
360
+ if (!writtenRanges?.length && !writtenTables?.length) {
361
+ return;
362
+ }
363
+ try {
364
+ const instanceName = this.doState.id.name ?? "singleton";
365
+ const syncNamespace = this.env?.SYNC_DO;
366
+ if (!syncNamespace) {
367
+ return;
368
+ }
369
+ const syncId = syncNamespace.idFromName(instanceName);
370
+ const syncStub = syncNamespace.get(syncId);
371
+ await syncStub.fetch("http://do/notify", {
372
+ method: "POST",
373
+ headers: { "Content-Type": "application/json" },
374
+ body: JSON.stringify({
375
+ writtenRanges,
376
+ writtenTables,
377
+ commitTimestamp: commitTimestamp ? commitTimestamp.toString() : undefined,
378
+ }),
379
+ });
380
+ }
381
+ catch (error) {
382
+ console.warn("Failed to notify SyncDO", error?.message ?? error);
383
+ }
384
+ }
385
+ /**
386
+ * Get CORS headers for responses
387
+ */
388
+ corsHeaders(request) {
389
+ const origin = request?.headers.get("Origin") ?? null;
390
+ const requestedHeaders = request?.headers.get("Access-Control-Request-Headers") ?? null;
391
+ const headers = {
392
+ "Content-Type": "application/json",
393
+ "Access-Control-Allow-Origin": origin ?? "*",
394
+ "Access-Control-Allow-Headers": requestedHeaders ?? "*",
395
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
396
+ "Access-Control-Max-Age": "86400",
397
+ };
398
+ if (origin) {
399
+ headers["Access-Control-Allow-Credentials"] = "true";
400
+ }
401
+ if (origin && requestedHeaders) {
402
+ headers.Vary = "Origin, Access-Control-Request-Headers";
403
+ }
404
+ else if (origin) {
405
+ headers.Vary = "Origin";
406
+ }
407
+ else if (requestedHeaders) {
408
+ headers.Vary = "Access-Control-Request-Headers";
409
+ }
410
+ return headers;
411
+ }
412
+ }