@concavejs/runtime-cf-base 0.0.1-alpha.7 → 0.0.1-alpha.9
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/blobstore-rpc.d.ts +13 -0
- package/dist/durable-objects/blobstore-rpc.js +27 -0
- package/dist/durable-objects/concave-do-base.d.ts +4 -0
- package/dist/durable-objects/concave-do-base.js +44 -96
- package/dist/durable-objects/docstore-rpc.d.ts +46 -0
- package/dist/durable-objects/docstore-rpc.js +63 -0
- package/dist/durable-objects/scheduler-manager.d.ts +19 -0
- package/dist/durable-objects/scheduler-manager.js +52 -0
- package/dist/durable-objects/sync-notifier.d.ts +16 -0
- package/dist/durable-objects/sync-notifier.js +38 -0
- package/dist/http/http-api.d.ts +32 -1
- package/dist/http/http-api.js +95 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/routing/sync-topology.d.ts +40 -0
- package/dist/routing/sync-topology.js +669 -0
- package/dist/udf/executor/do-client-executor.js +17 -1
- package/dist/worker/create-concave-worker.d.ts +45 -0
- package/dist/worker/create-concave-worker.js +36 -5
- package/package.json +11 -7
package/dist/http/http-api.js
CHANGED
|
@@ -60,7 +60,7 @@ function base64ToArrayBuffer(base64) {
|
|
|
60
60
|
* Create a storage adapter that routes through the ConcaveDO's storage syscall handler.
|
|
61
61
|
* This ensures storage operations are properly isolated within the DO.
|
|
62
62
|
*/
|
|
63
|
-
function createStorageAdapter(concaveDO
|
|
63
|
+
function createStorageAdapter(concaveDO) {
|
|
64
64
|
return {
|
|
65
65
|
store: async (blob) => {
|
|
66
66
|
const buffer = await blob.arrayBuffer();
|
|
@@ -106,26 +106,66 @@ function createStorageAdapter(concaveDO, _instance) {
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
|
-
function createNotifyWrites(env,
|
|
109
|
+
function createNotifyWrites(env, syncDoNames, ctx, logicalInstance, projectId, dispatchSyncWrites) {
|
|
110
110
|
return async (writtenRanges, writtenTables, commitTimestamp) => {
|
|
111
111
|
if (!writtenRanges?.length && !writtenTables?.length) {
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
|
-
const syncDoId = env.SYNC_DO.idFromName(instance);
|
|
115
|
-
const syncDo = env.SYNC_DO.get(syncDoId);
|
|
116
114
|
const payload = {
|
|
115
|
+
logicalInstance,
|
|
116
|
+
projectId,
|
|
117
|
+
syncTargets: syncDoNames,
|
|
117
118
|
writtenRanges,
|
|
118
119
|
writtenTables: writtenTables ?? writtenTablesFromRanges(writtenRanges),
|
|
119
120
|
commitTimestamp: commitTimestamp ? commitTimestamp.toString() : undefined,
|
|
120
121
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
if (dispatchSyncWrites) {
|
|
123
|
+
ctx.waitUntil(dispatchSyncWrites(payload).catch((error) => {
|
|
124
|
+
console.warn("[notifyWrites] Custom sync write dispatch failed", error);
|
|
125
|
+
}));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (isQueueLike(env.SYNC_NOTIFY_QUEUE)) {
|
|
129
|
+
ctx.waitUntil(env.SYNC_NOTIFY_QUEUE.send(payload).catch((error) => {
|
|
130
|
+
console.warn("[notifyWrites] Queue-based sync write dispatch failed", error);
|
|
131
|
+
}));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const doPayload = {
|
|
135
|
+
writtenRanges,
|
|
136
|
+
writtenTables: writtenTables ?? writtenTablesFromRanges(writtenRanges),
|
|
137
|
+
commitTimestamp: commitTimestamp ? commitTimestamp.toString() : undefined,
|
|
138
|
+
};
|
|
139
|
+
for (const syncDoName of syncDoNames) {
|
|
140
|
+
const syncDoId = env.SYNC_DO.idFromName(syncDoName);
|
|
141
|
+
const syncDo = env.SYNC_DO.get(syncDoId);
|
|
142
|
+
const headers = {
|
|
143
|
+
"Content-Type": "application/json",
|
|
144
|
+
"X-Concave-Instance": logicalInstance,
|
|
145
|
+
};
|
|
146
|
+
if (projectId) {
|
|
147
|
+
headers["X-Concave-Project-Id"] = projectId;
|
|
148
|
+
}
|
|
149
|
+
ctx.waitUntil(syncDo
|
|
150
|
+
.fetch("http://do/notify", {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers,
|
|
153
|
+
body: JSON.stringify(doPayload),
|
|
154
|
+
})
|
|
155
|
+
.catch((error) => {
|
|
156
|
+
console.warn("[notifyWrites] Direct SyncDO notify failed", error);
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
126
159
|
};
|
|
127
160
|
}
|
|
128
|
-
|
|
161
|
+
function isQueueLike(value) {
|
|
162
|
+
if (!value || typeof value !== "object") {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
const candidate = value;
|
|
166
|
+
return typeof candidate.send === "function";
|
|
167
|
+
}
|
|
168
|
+
export async function handleHttpApiRequest(request, env, ctx, instance = "singleton", routingTargets) {
|
|
129
169
|
const corsHeaders = computeCorsHeaders(request);
|
|
130
170
|
const apply = (response) => applyCors(response, corsHeaders);
|
|
131
171
|
const url = new URL(request.url);
|
|
@@ -133,14 +173,19 @@ export async function handleHttpApiRequest(request, env, ctx, instance = "single
|
|
|
133
173
|
if (pathParts[0] !== "api") {
|
|
134
174
|
return apply(new Response("Not found", { status: 404 }));
|
|
135
175
|
}
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
const
|
|
176
|
+
const logicalInstance = instance;
|
|
177
|
+
const concaveDoName = normalizeDoName(routingTargets?.concaveDoName ?? routingTargets?.concaveTargetName, logicalInstance);
|
|
178
|
+
const syncDoNames = normalizeSyncDoNames(routingTargets?.syncDoNames ?? routingTargets?.syncTargetNames, logicalInstance);
|
|
179
|
+
const projectId = request.headers.get("X-Concave-Project-Id") ?? undefined;
|
|
180
|
+
console.log(`[handleHttpApiRequest] logicalInstance=${logicalInstance} concaveDo=${concaveDoName} syncTargets=${syncDoNames.join(",")}`);
|
|
181
|
+
const concaveId = env.CONCAVE_DO.idFromName(concaveDoName);
|
|
182
|
+
const concaveRaw = env.CONCAVE_DO.get(concaveId);
|
|
183
|
+
const concave = wrapConcaveStub(concaveRaw, logicalInstance, projectId);
|
|
139
184
|
const executor = new ConcaveStubExecutor(concave);
|
|
140
185
|
const adapter = createClientAdapter(executor);
|
|
141
|
-
const notifyWrites = createNotifyWrites(env,
|
|
186
|
+
const notifyWrites = createNotifyWrites(env, syncDoNames, ctx, logicalInstance, projectId, routingTargets?.dispatchSyncWrites);
|
|
142
187
|
// Route storage operations through the DO's storage syscall handler
|
|
143
|
-
const storageAdapter = createStorageAdapter(concave
|
|
188
|
+
const storageAdapter = createStorageAdapter(concave);
|
|
144
189
|
const authHeader = request.headers.get("Authorization");
|
|
145
190
|
const headerToken = authHeader?.replace(/^Bearer\s+/i, "").trim() || undefined;
|
|
146
191
|
let headerIdentity;
|
|
@@ -294,3 +339,38 @@ export async function handleHttpApiRequest(request, env, ctx, instance = "single
|
|
|
294
339
|
}
|
|
295
340
|
return apply(new Response("Not found", { status: 404 }));
|
|
296
341
|
}
|
|
342
|
+
function wrapConcaveStub(stub, logicalInstance, projectId) {
|
|
343
|
+
return {
|
|
344
|
+
fetch: async (input, init) => {
|
|
345
|
+
const headers = new Headers(init?.headers);
|
|
346
|
+
headers.set("X-Concave-Instance", logicalInstance);
|
|
347
|
+
if (projectId) {
|
|
348
|
+
headers.set("X-Concave-Project-Id", projectId);
|
|
349
|
+
}
|
|
350
|
+
return stub.fetch(input, {
|
|
351
|
+
...init,
|
|
352
|
+
headers,
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function normalizeDoName(target, fallback) {
|
|
358
|
+
const trimmed = target?.trim();
|
|
359
|
+
return trimmed && trimmed.length > 0 ? trimmed : fallback;
|
|
360
|
+
}
|
|
361
|
+
function normalizeSyncDoNames(targets, fallback) {
|
|
362
|
+
if (targets === undefined) {
|
|
363
|
+
return [fallback];
|
|
364
|
+
}
|
|
365
|
+
if (targets.length === 0) {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
const deduped = new Set();
|
|
369
|
+
for (const target of targets) {
|
|
370
|
+
const trimmed = target.trim();
|
|
371
|
+
if (trimmed.length > 0) {
|
|
372
|
+
deduped.add(trimmed);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return Array.from(deduped);
|
|
376
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from "@concavejs/core";
|
|
2
2
|
export type { ConcaveEnv, ConcaveStorageEnv, ConcaveD1Env } from "./env";
|
|
3
3
|
export { UdfExecutorRpc } from "./worker/udf-worker";
|
|
4
|
-
export { createConcaveWorker, resolveNamespaceBinding, createScopedNamespace, type ConcaveWorkerOptions, type ConcaveWorkerBindings, type ConcaveWorker, } from "./worker/create-concave-worker";
|
|
4
|
+
export { createConcaveWorker, resolveNamespaceBinding, createScopedNamespace, type ConcaveWorkerOptions, type ConcaveWorkerBindings, type ConcaveWorker, type ConcaveWorkerRouteContext, type ConcaveWorkerRouteTargets, } from "./worker/create-concave-worker";
|
|
5
5
|
export { DEFAULT_INSTANCE_KEY, DEFAULT_INSTANCE_VALUE, DEFAULT_INSTANCE_COOKIE_PATH, resolveInstanceFromRequest, maybeAttachInstanceCookie, readCookieValue, buildInstanceCookie, type InstanceResolution, type InstanceResolutionOptions, type InstanceCookieOptions, } from "./routing/instance";
|
|
6
|
+
export { createCfSyncTopologyRuntime, buildPooledInstanceName as buildSyncPooledInstanceName, parseNodeIds as parseSyncNodeIds, parseNodeIdsByRegion as parseSyncNodeIdsByRegion, normalizeTargetList as normalizeSyncTargetList, type CfSyncTopologyConfig, type BuildShardMapFromReportsOptions as SyncBuildShardMapFromReportsOptions, type ResolveSyncFanoutDoNamesAsyncOptions, type CreateCfSyncTopologyRuntimeOptions, type CfSyncTopologyRuntime, } from "./routing/sync-topology";
|
|
6
7
|
export { UdfExecInline } from "./udf/executor/inline-executor";
|
|
7
8
|
export { UdfExecIsolated } from "./udf/executor/isolated-executor";
|
|
8
9
|
export { ConcaveDOBase, type ConcaveDOConfig, type ConcaveDOAdapterContext, type ConcaveDOExecutorContext, } from "./durable-objects/concave-do-base";
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { UdfExecutorRpc } from "./worker/udf-worker";
|
|
|
5
5
|
export { createConcaveWorker, resolveNamespaceBinding, createScopedNamespace, } from "./worker/create-concave-worker";
|
|
6
6
|
// Export instance routing helpers
|
|
7
7
|
export { DEFAULT_INSTANCE_KEY, DEFAULT_INSTANCE_VALUE, DEFAULT_INSTANCE_COOKIE_PATH, resolveInstanceFromRequest, maybeAttachInstanceCookie, readCookieValue, buildInstanceCookie, } from "./routing/instance";
|
|
8
|
+
export { createCfSyncTopologyRuntime, buildPooledInstanceName as buildSyncPooledInstanceName, parseNodeIds as parseSyncNodeIds, parseNodeIdsByRegion as parseSyncNodeIdsByRegion, normalizeTargetList as normalizeSyncTargetList, } from "./routing/sync-topology";
|
|
8
9
|
// Export UDF executors
|
|
9
10
|
export { UdfExecInline } from "./udf/executor/inline-executor";
|
|
10
11
|
export { UdfExecIsolated } from "./udf/executor/isolated-executor";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ConcaveWorkerRouteContext, ConcaveWorkerRouteTargets } from "../worker/create-concave-worker";
|
|
2
|
+
import { type BuildShardMapFromReportsOptions as SharedBuildShardMapFromReportsOptions, type SyncShardMap, type SyncTopologyConfig } from "@concavejs/runtime-base";
|
|
3
|
+
export interface CfSyncTopologyConfig extends SyncTopologyConfig {
|
|
4
|
+
syncAffinityKey: string;
|
|
5
|
+
regionKey: string;
|
|
6
|
+
regionByColo: Record<string, string>;
|
|
7
|
+
useColoAsRegion: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface BuildShardMapFromReportsOptions extends Omit<SharedBuildShardMapFromReportsOptions, "config"> {
|
|
10
|
+
logicalInstance: string;
|
|
11
|
+
env: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface ResolveSyncFanoutDoNamesAsyncOptions {
|
|
14
|
+
request?: Request;
|
|
15
|
+
projectId?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateCfSyncTopologyRuntimeOptions {
|
|
18
|
+
logPrefix?: string;
|
|
19
|
+
projectHeaderName?: string;
|
|
20
|
+
coordinatorBindingName?: string;
|
|
21
|
+
shardMapKvBindingName?: string;
|
|
22
|
+
resolveProjectId?: (request: Request, env: Record<string, unknown>) => string | undefined;
|
|
23
|
+
buildCoordinatorDoName: (logicalInstance: string, projectId?: string) => string;
|
|
24
|
+
buildShardMapKvKey: (logicalInstance: string, projectId?: string) => string;
|
|
25
|
+
buildShardMapCacheKey?: (logicalInstance: string, projectId?: string) => string;
|
|
26
|
+
}
|
|
27
|
+
export interface CfSyncTopologyRuntime {
|
|
28
|
+
resolveRouteTargets(context: ConcaveWorkerRouteContext): Promise<ConcaveWorkerRouteTargets>;
|
|
29
|
+
resolveSyncFanoutDoNames(logicalInstance: string, env: Record<string, unknown>): string[];
|
|
30
|
+
resolveSyncFanoutDoNamesAsync(logicalInstance: string, env: Record<string, unknown>, options?: ResolveSyncFanoutDoNamesAsyncOptions): Promise<string[]>;
|
|
31
|
+
resolveStaticSyncShardMap(logicalInstance: string, env: Record<string, unknown>, nowMs?: number): SyncShardMap;
|
|
32
|
+
buildShardMapFromReports(options: BuildShardMapFromReportsOptions): SyncShardMap;
|
|
33
|
+
resolveTopologyConfig(env: Record<string, unknown>): CfSyncTopologyConfig;
|
|
34
|
+
resolveRequestRegion(request: Request, env: Record<string, unknown>): string;
|
|
35
|
+
}
|
|
36
|
+
export declare function createCfSyncTopologyRuntime(options: CreateCfSyncTopologyRuntimeOptions): CfSyncTopologyRuntime;
|
|
37
|
+
export declare function buildPooledInstanceName(logicalInstance: string, poolKind: "sync" | "udf", nodeId: string): string;
|
|
38
|
+
export declare function parseNodeIds(raw: unknown): string[];
|
|
39
|
+
export declare function parseNodeIdsByRegion(raw: unknown): Record<string, string[]>;
|
|
40
|
+
export declare function normalizeTargetList(targets: string[] | undefined): string[];
|