@concavejs/runtime-cf-base 0.0.1-alpha.6 → 0.0.1-alpha.7
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/dist/durable-objects/concave-do-base.d.ts +2 -1
- package/dist/durable-objects/concave-do-base.js +87 -16
- package/dist/http/http-api.js +42 -8
- package/dist/rpc/docstore-proxy.js +13 -12
- package/dist/udf/executor/do-client-executor.d.ts +1 -1
- package/dist/udf/executor/do-client-executor.js +2 -1
- package/dist/udf/executor/isolated-executor.d.ts +1 -1
- package/dist/udf/executor/isolated-executor.js +2 -2
- package/dist/worker/udf-worker.d.ts +1 -1
- package/dist/worker/udf-worker.js +2 -2
- package/package.json +1 -1
|
@@ -74,6 +74,7 @@ export declare class ConcaveDOBase extends DurableObject {
|
|
|
74
74
|
* runtime auto-discovery from the global module registry.
|
|
75
75
|
*/
|
|
76
76
|
private initializeCronSpecs;
|
|
77
|
+
private currentSnapshotTimestamp;
|
|
77
78
|
/**
|
|
78
79
|
* Main request handler
|
|
79
80
|
*/
|
|
@@ -89,7 +90,7 @@ export declare class ConcaveDOBase extends DurableObject {
|
|
|
89
90
|
/**
|
|
90
91
|
* Execute a UDF
|
|
91
92
|
*/
|
|
92
|
-
protected execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string): Promise<UdfResult>;
|
|
93
|
+
protected execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string, snapshotTimestamp?: bigint): Promise<UdfResult>;
|
|
93
94
|
/**
|
|
94
95
|
* Handle scheduled function alarms
|
|
95
96
|
*/
|
|
@@ -12,7 +12,8 @@ import { getSearchIndexesFromSchema, getVectorIndexesFromSchema } from "@concave
|
|
|
12
12
|
import { SchemaService } from "@concavejs/core/kernel";
|
|
13
13
|
import { runAsClientCall, runAsServerCall } from "@concavejs/core/udf";
|
|
14
14
|
import { ScheduledFunctionExecutor, CronExecutor } from "@concavejs/core";
|
|
15
|
-
import {
|
|
15
|
+
import { resolveAuthContext } from "@concavejs/core/http";
|
|
16
|
+
import { AdminAuthError, identityFromToken, isAdminToken, isSystemToken, JWTValidationError, resolveAdminAuthConfigFromEnv, resolveJwtValidationConfigFromEnv, resolveSystemAuthConfigFromEnv, setAdminAuthConfig, setJwtValidationConfig, setSystemAuthConfig, SystemAuthError, } from "@concavejs/core/auth";
|
|
16
17
|
const VERSIONED_API_PREFIX = /^\/api\/\d+\.\d+(?:\.\d+)?(?=\/|$)/;
|
|
17
18
|
function stripApiVersionPrefix(pathname) {
|
|
18
19
|
return pathname.replace(VERSIONED_API_PREFIX, "/api");
|
|
@@ -23,6 +24,8 @@ function isReservedApiPath(pathname) {
|
|
|
23
24
|
normalizedPath === "/api/sync" ||
|
|
24
25
|
normalizedPath === "/api/reset-test-state" ||
|
|
25
26
|
normalizedPath === "/api/query" ||
|
|
27
|
+
normalizedPath === "/api/query_ts" ||
|
|
28
|
+
normalizedPath === "/api/query_at_ts" ||
|
|
26
29
|
normalizedPath === "/api/mutation" ||
|
|
27
30
|
normalizedPath === "/api/action") {
|
|
28
31
|
return true;
|
|
@@ -41,6 +44,28 @@ function shouldHandleAsHttpRoute(pathname) {
|
|
|
41
44
|
}
|
|
42
45
|
return !isReservedApiPath(pathname);
|
|
43
46
|
}
|
|
47
|
+
function parseSnapshotTimestamp(value) {
|
|
48
|
+
if (value === undefined || value === null) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "bigint") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === "number") {
|
|
55
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
56
|
+
throw new Error("Invalid snapshotTimestamp");
|
|
57
|
+
}
|
|
58
|
+
return BigInt(value);
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === "string") {
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
63
|
+
throw new Error("Invalid snapshotTimestamp");
|
|
64
|
+
}
|
|
65
|
+
return BigInt(trimmed);
|
|
66
|
+
}
|
|
67
|
+
throw new Error("Invalid snapshotTimestamp");
|
|
68
|
+
}
|
|
44
69
|
/**
|
|
45
70
|
* Base class for Concave Durable Objects
|
|
46
71
|
*
|
|
@@ -74,7 +99,6 @@ export class ConcaveDOBase extends DurableObject {
|
|
|
74
99
|
setAdminAuthConfig(adminConfig);
|
|
75
100
|
setSystemAuthConfig(systemConfig);
|
|
76
101
|
const instanceId = state.id.name ?? state.id.toString();
|
|
77
|
-
console.log(`[ConcaveDO.constructor] instanceId=${instanceId}`);
|
|
78
102
|
const adapterContext = {
|
|
79
103
|
state,
|
|
80
104
|
env,
|
|
@@ -155,11 +179,30 @@ export class ConcaveDOBase extends DurableObject {
|
|
|
155
179
|
console.warn("[ConcaveDO] Failed to initialize cron specs:", error?.message ?? error);
|
|
156
180
|
}
|
|
157
181
|
}
|
|
182
|
+
currentSnapshotTimestamp() {
|
|
183
|
+
const oracle = this._docstore?.timestampOracle;
|
|
184
|
+
const oracleTimestamp = typeof oracle?.beginSnapshot === "function"
|
|
185
|
+
? oracle.beginSnapshot()
|
|
186
|
+
: typeof oracle?.getCurrentTimestamp === "function"
|
|
187
|
+
? oracle.getCurrentTimestamp()
|
|
188
|
+
: undefined;
|
|
189
|
+
const wallClock = BigInt(Date.now());
|
|
190
|
+
if (typeof oracleTimestamp === "bigint" && oracleTimestamp > wallClock) {
|
|
191
|
+
return oracleTimestamp;
|
|
192
|
+
}
|
|
193
|
+
return wallClock;
|
|
194
|
+
}
|
|
158
195
|
/**
|
|
159
196
|
* Main request handler
|
|
160
197
|
*/
|
|
161
198
|
async fetch(request) {
|
|
162
199
|
const url = new URL(request.url);
|
|
200
|
+
if (url.pathname === "/query_ts") {
|
|
201
|
+
if (request.method !== "POST") {
|
|
202
|
+
return new Response("Method not allowed", { status: 405 });
|
|
203
|
+
}
|
|
204
|
+
return Response.json({ ts: this.currentSnapshotTimestamp().toString() }, { headers: this.corsHeaders(request) });
|
|
205
|
+
}
|
|
163
206
|
if (shouldHandleAsHttpRoute(url.pathname)) {
|
|
164
207
|
return this.handleHttp(request);
|
|
165
208
|
}
|
|
@@ -173,10 +216,11 @@ export class ConcaveDOBase extends DurableObject {
|
|
|
173
216
|
*/
|
|
174
217
|
async handleUdfRequest(request) {
|
|
175
218
|
try {
|
|
176
|
-
const { path, args, type, auth, componentPath, caller } = await request.json();
|
|
219
|
+
const { path, args, type, auth, componentPath, caller, snapshotTimestamp } = await request.json();
|
|
177
220
|
const convexArgs = jsonToConvex(args);
|
|
221
|
+
const parsedSnapshotTimestamp = parseSnapshotTimestamp(snapshotTimestamp);
|
|
178
222
|
const requestId = crypto.randomUUID();
|
|
179
|
-
const exec = () => this.execute(path, convexArgs, type, auth, componentPath, requestId);
|
|
223
|
+
const exec = () => this.execute(path, convexArgs, type, auth, componentPath, requestId, parsedSnapshotTimestamp);
|
|
180
224
|
const result = caller === "server" ? await runAsServerCall(exec, path) : await runAsClientCall(exec);
|
|
181
225
|
if (type === "mutation" || type === "action") {
|
|
182
226
|
this.doState.waitUntil(this.reschedule());
|
|
@@ -196,7 +240,7 @@ export class ConcaveDOBase extends DurableObject {
|
|
|
196
240
|
}
|
|
197
241
|
catch (e) {
|
|
198
242
|
console.error(e);
|
|
199
|
-
return new Response(
|
|
243
|
+
return new Response("Internal Server Error", {
|
|
200
244
|
headers: this.corsHeaders(request),
|
|
201
245
|
status: 500,
|
|
202
246
|
});
|
|
@@ -210,33 +254,60 @@ export class ConcaveDOBase extends DurableObject {
|
|
|
210
254
|
const url = new URL(request.url);
|
|
211
255
|
url.pathname = url.pathname.replace(/^\/api\/http/, "");
|
|
212
256
|
const req = new Request(url.toString(), request);
|
|
213
|
-
const
|
|
257
|
+
const authHeader = req.headers.get("Authorization");
|
|
258
|
+
const headerToken = authHeader?.replace(/^Bearer\s+/i, "").trim() || undefined;
|
|
259
|
+
let headerIdentity;
|
|
260
|
+
try {
|
|
261
|
+
headerIdentity =
|
|
262
|
+
headerToken && !isAdminToken(headerToken) && !isSystemToken(headerToken)
|
|
263
|
+
? await identityFromToken(headerToken)
|
|
264
|
+
: undefined;
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (error instanceof JWTValidationError || error instanceof AdminAuthError || error instanceof SystemAuthError) {
|
|
268
|
+
return Response.json({ error: "Unauthorized" }, { status: 401, headers: this.corsHeaders(request) });
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
const auth = (await resolveAuthContext(undefined, headerToken, headerIdentity));
|
|
214
273
|
const requestId = crypto.randomUUID();
|
|
215
274
|
return this.udfExecutor.executeHttp(req, auth, requestId);
|
|
216
275
|
}
|
|
217
276
|
catch (e) {
|
|
218
277
|
console.error(e);
|
|
219
|
-
return new Response(
|
|
278
|
+
return new Response("Internal Server Error", { status: 500, headers: this.corsHeaders(request) });
|
|
220
279
|
}
|
|
221
280
|
}
|
|
222
281
|
/**
|
|
223
282
|
* Execute a UDF
|
|
224
283
|
*/
|
|
225
|
-
async execute(path, args, type, auth, componentPath, requestId) {
|
|
226
|
-
return this.udfExecutor.execute(path, args, type, auth, componentPath, requestId);
|
|
284
|
+
async execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp) {
|
|
285
|
+
return this.udfExecutor.execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp);
|
|
227
286
|
}
|
|
228
287
|
/**
|
|
229
288
|
* Handle scheduled function alarms
|
|
230
289
|
*/
|
|
231
290
|
async alarm() {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
291
|
+
try {
|
|
292
|
+
const scheduledResult = await this.scheduler.runDueJobs();
|
|
293
|
+
const cronResult = await this.cronExecutor.runDueJobs();
|
|
294
|
+
const nextTimes = [scheduledResult.nextScheduledTime, cronResult.nextScheduledTime].filter((t) => t !== null);
|
|
295
|
+
if (nextTimes.length === 0) {
|
|
296
|
+
await this.doState.storage.deleteAlarm();
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
await this.doState.storage.setAlarm(Math.min(...nextTimes));
|
|
300
|
+
}
|
|
237
301
|
}
|
|
238
|
-
|
|
239
|
-
|
|
302
|
+
catch (error) {
|
|
303
|
+
console.error("[ConcaveDO] Alarm handler failed:", error?.message ?? error);
|
|
304
|
+
// Re-check for pending jobs so we don't lose scheduled work
|
|
305
|
+
try {
|
|
306
|
+
await this.reschedule();
|
|
307
|
+
}
|
|
308
|
+
catch (rescheduleError) {
|
|
309
|
+
console.error("[ConcaveDO] Reschedule after alarm failure also failed:", rescheduleError?.message);
|
|
310
|
+
}
|
|
240
311
|
}
|
|
241
312
|
}
|
|
242
313
|
/**
|
package/dist/http/http-api.js
CHANGED
|
@@ -15,6 +15,8 @@ function isReservedApiPath(pathname) {
|
|
|
15
15
|
normalizedPath === "/api/sync" ||
|
|
16
16
|
normalizedPath === "/api/reset-test-state" ||
|
|
17
17
|
normalizedPath === "/api/query" ||
|
|
18
|
+
normalizedPath === "/api/query_ts" ||
|
|
19
|
+
normalizedPath === "/api/query_at_ts" ||
|
|
18
20
|
normalizedPath === "/api/mutation" ||
|
|
19
21
|
normalizedPath === "/api/action") {
|
|
20
22
|
return true;
|
|
@@ -36,6 +38,24 @@ function shouldForwardApiPath(pathname) {
|
|
|
36
38
|
}
|
|
37
39
|
return !isReservedApiPath(pathname);
|
|
38
40
|
}
|
|
41
|
+
function arrayBufferToBase64(buffer) {
|
|
42
|
+
const bytes = new Uint8Array(buffer);
|
|
43
|
+
const chunkSize = 0x8000;
|
|
44
|
+
let binary = "";
|
|
45
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
46
|
+
const chunk = bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length));
|
|
47
|
+
binary += String.fromCharCode(...chunk);
|
|
48
|
+
}
|
|
49
|
+
return btoa(binary);
|
|
50
|
+
}
|
|
51
|
+
function base64ToArrayBuffer(base64) {
|
|
52
|
+
const binary = atob(base64);
|
|
53
|
+
const bytes = new Uint8Array(binary.length);
|
|
54
|
+
for (let i = 0; i < binary.length; i++) {
|
|
55
|
+
bytes[i] = binary.charCodeAt(i);
|
|
56
|
+
}
|
|
57
|
+
return bytes.buffer;
|
|
58
|
+
}
|
|
39
59
|
/**
|
|
40
60
|
* Create a storage adapter that routes through the ConcaveDO's storage syscall handler.
|
|
41
61
|
* This ensures storage operations are properly isolated within the DO.
|
|
@@ -44,7 +64,7 @@ function createStorageAdapter(concaveDO, _instance) {
|
|
|
44
64
|
return {
|
|
45
65
|
store: async (blob) => {
|
|
46
66
|
const buffer = await blob.arrayBuffer();
|
|
47
|
-
const base64 =
|
|
67
|
+
const base64 = arrayBufferToBase64(buffer);
|
|
48
68
|
const response = await concaveDO.fetch("http://do/storage", {
|
|
49
69
|
method: "POST",
|
|
50
70
|
headers: { "Content-Type": "application/json" },
|
|
@@ -81,12 +101,8 @@ function createStorageAdapter(concaveDO, _instance) {
|
|
|
81
101
|
if (!result.result || !result.result.__arrayBuffer) {
|
|
82
102
|
return { blob: null };
|
|
83
103
|
}
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < binary.length; i++) {
|
|
87
|
-
bytes[i] = binary.charCodeAt(i);
|
|
88
|
-
}
|
|
89
|
-
return { blob: new Blob([bytes.buffer]) };
|
|
104
|
+
const buffer = base64ToArrayBuffer(result.result.__arrayBuffer);
|
|
105
|
+
return { blob: new Blob([buffer]) };
|
|
90
106
|
},
|
|
91
107
|
};
|
|
92
108
|
}
|
|
@@ -142,10 +158,28 @@ export async function handleHttpApiRequest(request, env, ctx, instance = "single
|
|
|
142
158
|
}
|
|
143
159
|
// Note: Internal function access control is now handled by core executor (fail-closed)
|
|
144
160
|
const coreResult = await handleCoreHttpApiRequest(request, {
|
|
145
|
-
executeFunction: async ({ type, path, args, auth, componentPath }) => adapter.executeUdf(path, args, type, auth, componentPath),
|
|
161
|
+
executeFunction: async ({ type, path, args, auth, componentPath, snapshotTimestamp }) => adapter.executeUdf(path, args, type, auth, componentPath, undefined, snapshotTimestamp),
|
|
146
162
|
notifyWrites,
|
|
147
163
|
storage: storageAdapter,
|
|
148
164
|
corsHeaders,
|
|
165
|
+
getSnapshotTimestamp: async () => {
|
|
166
|
+
try {
|
|
167
|
+
const response = await concave.fetch("http://do/query_ts", {
|
|
168
|
+
method: "POST",
|
|
169
|
+
});
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error(`query_ts failed with status ${response.status}`);
|
|
172
|
+
}
|
|
173
|
+
const body = (await response.json());
|
|
174
|
+
if (typeof body.ts !== "string" || !/^\d+$/.test(body.ts)) {
|
|
175
|
+
throw new Error("Invalid query_ts response");
|
|
176
|
+
}
|
|
177
|
+
return BigInt(body.ts);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return BigInt(Date.now());
|
|
181
|
+
}
|
|
182
|
+
},
|
|
149
183
|
});
|
|
150
184
|
if (coreResult?.handled) {
|
|
151
185
|
return coreResult.response;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TimestampOracle } from "@concavejs/core/utils";
|
|
1
2
|
/**
|
|
2
3
|
* Wraps a DO stub as a DocStore.
|
|
3
4
|
* Generator methods are converted from arrays back to async generators.
|
|
@@ -16,6 +17,9 @@ export function createGatewayDocStoreProxy(gateway, projectId, instance) {
|
|
|
16
17
|
* Internal helper that creates a DocStore proxy with configurable argument transformation.
|
|
17
18
|
*/
|
|
18
19
|
function createDocStoreProxyInternal(target, transformArgs) {
|
|
20
|
+
// Use a real TimestampOracle to guarantee monotonic, unique timestamps.
|
|
21
|
+
// The DO-side oracle cannot be accessed via RPC, so each proxy gets its own.
|
|
22
|
+
const oracle = new TimestampOracle();
|
|
19
23
|
return new Proxy({}, {
|
|
20
24
|
get(_, prop) {
|
|
21
25
|
// Convert array results back to async generators
|
|
@@ -48,25 +52,22 @@ function createDocStoreProxyInternal(target, transformArgs) {
|
|
|
48
52
|
return new Map(result);
|
|
49
53
|
};
|
|
50
54
|
}
|
|
51
|
-
// Provide a local
|
|
55
|
+
// Provide a local TimestampOracle (DO-side oracle cannot be accessed via RPC)
|
|
52
56
|
if (prop === "timestampOracle") {
|
|
53
|
-
return
|
|
54
|
-
observeTimestamp: () => { },
|
|
55
|
-
allocateTimestamp: () => BigInt(Date.now()),
|
|
56
|
-
getCurrentTimestamp: () => BigInt(Date.now()),
|
|
57
|
-
beginSnapshot: () => BigInt(Date.now()),
|
|
58
|
-
};
|
|
57
|
+
return oracle;
|
|
59
58
|
}
|
|
60
59
|
// No-op close
|
|
61
60
|
if (prop === "close") {
|
|
62
61
|
return async () => { };
|
|
63
62
|
}
|
|
64
|
-
// Bind methods to target with transformed args
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
// Bind methods to target with transformed args.
|
|
64
|
+
// Use direct invocation (target[prop](...)) instead of .call() because
|
|
65
|
+
// Cloudflare RPC stubs intercept property access - .call() would be
|
|
66
|
+
// interpreted as an RPC method name rather than Function.prototype.call.
|
|
67
|
+
if (typeof target[prop] === "function") {
|
|
68
|
+
return (...args) => target[prop](...transformArgs(...args));
|
|
68
69
|
}
|
|
69
|
-
return
|
|
70
|
+
return target[prop];
|
|
70
71
|
},
|
|
71
72
|
});
|
|
72
73
|
}
|
|
@@ -9,6 +9,6 @@ import type { AuthContext } from "@concavejs/core/sync/protocol-handler";
|
|
|
9
9
|
export declare class ConcaveStubExecutor implements UdfExec {
|
|
10
10
|
private readonly stub;
|
|
11
11
|
constructor(stub: DurableObjectStub);
|
|
12
|
-
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: AuthContext | UserIdentityAttributes, componentPath?: string, requestId?: string): Promise<UdfResult>;
|
|
12
|
+
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: AuthContext | UserIdentityAttributes, componentPath?: string, requestId?: string, snapshotTimestamp?: bigint): Promise<UdfResult>;
|
|
13
13
|
executeHttp(request: Request): Promise<Response>;
|
|
14
14
|
}
|
|
@@ -8,7 +8,7 @@ export class ConcaveStubExecutor {
|
|
|
8
8
|
constructor(stub) {
|
|
9
9
|
this.stub = stub;
|
|
10
10
|
}
|
|
11
|
-
async execute(path, args, type, auth, componentPath, requestId) {
|
|
11
|
+
async execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp) {
|
|
12
12
|
const payload = {
|
|
13
13
|
path,
|
|
14
14
|
args: convexToJson(args),
|
|
@@ -17,6 +17,7 @@ export class ConcaveStubExecutor {
|
|
|
17
17
|
componentPath,
|
|
18
18
|
caller: "client",
|
|
19
19
|
requestId,
|
|
20
|
+
snapshotTimestamp: snapshotTimestamp?.toString(),
|
|
20
21
|
};
|
|
21
22
|
const response = await this.stub.fetch("http://do/execute", {
|
|
22
23
|
method: "POST",
|
|
@@ -19,6 +19,6 @@ export declare class UdfExecIsolated implements UdfExec {
|
|
|
19
19
|
private instance;
|
|
20
20
|
private projectId;
|
|
21
21
|
constructor(stubOrOptions: UdfExecutorRpc | UdfExecIsolatedOptions);
|
|
22
|
-
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string): Promise<UdfResult>;
|
|
22
|
+
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string, snapshotTimestamp?: bigint): Promise<UdfResult>;
|
|
23
23
|
executeHttp(request: Request, auth?: any, requestId?: string): Promise<Response>;
|
|
24
24
|
}
|
|
@@ -21,9 +21,9 @@ export class UdfExecIsolated {
|
|
|
21
21
|
this.projectId = "default";
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
async execute(path, args, type, auth, componentPath, requestId) {
|
|
24
|
+
async execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp) {
|
|
25
25
|
// Pass instance context for syscall routing
|
|
26
|
-
return this.rpc.execute(path, args, type, auth, componentPath, requestId, this.instance, this.projectId);
|
|
26
|
+
return this.rpc.execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp, this.instance, this.projectId);
|
|
27
27
|
}
|
|
28
28
|
async executeHttp(request, auth, requestId) {
|
|
29
29
|
return this.rpc.executeHttp(request, auth, requestId, this.instance, this.projectId);
|
|
@@ -19,7 +19,7 @@ interface Env {
|
|
|
19
19
|
export declare class UdfExecutorRpc extends WorkerEntrypoint {
|
|
20
20
|
private udfExecutor;
|
|
21
21
|
constructor(ctx: ExecutionContext, env: Env);
|
|
22
|
-
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string, _instance?: string, _projectId?: string): Promise<import("@concavejs/core").UdfResult>;
|
|
22
|
+
execute(path: string, args: Record<string, any>, type: "query" | "mutation" | "action", auth?: any, componentPath?: string, requestId?: string, snapshotTimestamp?: bigint, _instance?: string, _projectId?: string): Promise<import("@concavejs/core").UdfResult>;
|
|
23
23
|
executeHttp(request: Request, auth?: any, requestId?: string, _instance?: string, _projectId?: string): Promise<Response>;
|
|
24
24
|
}
|
|
25
25
|
export {};
|
|
@@ -51,11 +51,11 @@ export class UdfExecutorRpc extends WorkerEntrypoint {
|
|
|
51
51
|
// Pass blobstore if available, otherwise fall back to R2Bucket for direct mode
|
|
52
52
|
this.udfExecutor = new UdfExecInline(docstore, blobstore ?? env.STORAGE_BUCKET, env.R2_PUBLIC_URL);
|
|
53
53
|
}
|
|
54
|
-
async execute(path, args, type, auth, componentPath, requestId, _instance, _projectId) {
|
|
54
|
+
async execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp, _instance, _projectId) {
|
|
55
55
|
// Note: instance and projectId are passed for per-request context but
|
|
56
56
|
// currently we rely on the environment settings for syscall routing.
|
|
57
57
|
// This can be enhanced to create per-request syscall clients if needed.
|
|
58
|
-
return this.udfExecutor.execute(path, args, type, auth, componentPath, requestId);
|
|
58
|
+
return this.udfExecutor.execute(path, args, type, auth, componentPath, requestId, snapshotTimestamp);
|
|
59
59
|
}
|
|
60
60
|
async executeHttp(request, auth, requestId, _instance, _projectId) {
|
|
61
61
|
return this.udfExecutor.executeHttp(request, auth, requestId);
|