@lunora/codegen 1.0.0-alpha.319 → 1.0.0-alpha.320
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/index.d.mts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{NOTIFY_FILENAME-CjbDYC2a.mjs → NOTIFY_FILENAME-bAt7J3cs.mjs} +1 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-Bo67h-3B.mjs +11 -0
- package/dist/packem_shared/chain-Cm6BMT8g.mjs +1 -0
- package/dist/packem_shared/classify-procedure-call-DVxdUhZt.mjs +1 -0
- package/dist/packem_shared/{context-property-calls-Bw8u2Pz4.mjs → context-property-calls-Cn3upNgZ.mjs} +1 -1
- package/dist/packem_shared/{context-root-YkWLB6M2.mjs → context-root-BdxINX-T.mjs} +1 -1
- package/dist/packem_shared/{database-calls-BnVUlTx_.mjs → database-calls-BKozZUEW.mjs} +1 -1
- package/dist/packem_shared/discoverFunctions-CZgLhsMn.mjs +1 -0
- package/dist/packem_shared/discoverHyperdriveCalls-DV4IglLE.mjs +1 -0
- package/dist/packem_shared/{discoverInserts-DUbjhNzk.mjs → discoverInserts-BjxXCACZ.mjs} +1 -1
- package/dist/packem_shared/discoverMaskProcedures-DE-8CDen.mjs +1 -0
- package/dist/packem_shared/{discoverNondeterministicCalls-D7Ovq8ae.mjs → discoverNondeterministicCalls-DvhyKM-f.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-BNqjOBY_.mjs → discoverQueries-C8E7TeRT.mjs} +1 -1
- package/dist/packem_shared/discoverR2sqlCalls-DVcfMO6-.mjs +1 -0
- package/dist/packem_shared/{discoverRlsMetadata-CbdfaVep.mjs → discoverRlsMetadata-QRsWRQbM.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsProcedures-BeLdV5RT.mjs → discoverRlsProcedures-Du7ITNTQ.mjs} +1 -1
- package/dist/packem_shared/{discoverStorageRulesMetadata-BBMy9JVZ.mjs → discoverStorageRulesMetadata-ClzKLdCV.mjs} +1 -1
- package/dist/packem_shared/{emitAgents-DuSp0uSJ.mjs → emitAgents-CR2_X_ov.mjs} +1 -1
- package/dist/packem_shared/emitFunctions-rjyb7h7k.mjs +422 -0
- package/dist/packem_shared/{emitShard-Ddgmq6De.mjs → emitShard-D3Po46nf.mjs} +1 -1
- package/dist/packem_shared/{exported-procedure-chains-DAl7xBBx.mjs → exported-procedure-chains-BNBUkXui.mjs} +1 -1
- package/dist/packem_shared/{index-DvmKcoFV.mjs → index-JnqBWRYO.mjs} +1 -1
- package/dist/packem_shared/{shard-DYYhXxEG.mjs → shard-CsOnD3Na.mjs} +226 -117
- package/dist/packem_shared/{shard-runtime-BQ6ZNigW.mjs → shard-runtime-BpoCV-aM.mjs} +13 -8
- package/package.json +1 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-DwqkR6hn.mjs +0 -11
- package/dist/packem_shared/chain-CubC5VoS.mjs +0 -1
- package/dist/packem_shared/classify-procedure-call-CcgP-9I0.mjs +0 -1
- package/dist/packem_shared/discoverFunctions-5Zy6K4uA.mjs +0 -1
- package/dist/packem_shared/discoverHyperdriveCalls-DFbp-ILH.mjs +0 -1
- package/dist/packem_shared/discoverMaskProcedures-BfVFPpAJ.mjs +0 -1
- package/dist/packem_shared/discoverR2sqlCalls-TNwsmYje.mjs +0 -1
- package/dist/packem_shared/emitFunctions-amc10WSc.mjs +0 -291
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{LunoraError as
|
|
1
|
+
import{LunoraError as It}from"@lunora/errors";import{serializeSchemaSnapshot as Pt,hashSchemaSnapshot as Nt}from"./SCHEMA_SNAPSHOT_VERSION-C0oHwiJZ.mjs";import{isHyperdriveGlobalTable as Mt,isD1GlobalTable as $t}from"./isD1GlobalTable-udvHAAzD.mjs";import{r as qt}from"./json-data-Chfs2Cz5.mjs";import{r as _,e as Ft,a as Ut,b as Bt,c as jt,d as Wt,f as Ht,g as Vt,h as Kt,i as Gt,j as Qt,k as Jt,l as zt,m as Yt,n as Xt,o as Zt,p as er,q as tr}from"./shard-runtime-BpoCV-aM.mjs";import{u as J,i as rr,G as nr,d as ar}from"./shared-DnCokNcE.mjs";import{p as sr}from"./platform-8oj7iXQy.mjs";import{i as or}from"./schema-BgM8A3XV.mjs";const h=(n,e)=>`((): (() => ${e}) => { let cached: ${e} | undefined; return () => (cached ??= JSON.parse(${JSON.stringify(JSON.stringify(n))}) as ${e}); })()`,oe=["categories","description","facing","level","remediation","title"],ir=n=>{const e={};return{advisoryRows:n.map(a=>{let r=e[a.name];return r||(r=Object.fromEntries(oe.filter(s=>s in a).map(s=>[s,a[s]])),e[a.name]=r),Object.fromEntries(Object.entries(a).filter(([s,d])=>!oe.includes(s)||JSON.stringify(d)!==JSON.stringify(r[s])))}),advisoryRules:e}},dr=` /**
|
|
2
2
|
* The bare writer every admin/maintenance entry point writes through.
|
|
3
3
|
*
|
|
4
4
|
* Admin and maintenance writes go through the SAME reactive-cache hooks as a
|
|
@@ -30,7 +30,7 @@ import{LunoraError as At}from"@lunora/errors";import{serializeSchemaSnapshot as
|
|
|
30
30
|
schema: schema as unknown as SchemaLike,
|
|
31
31
|
sql: this.sql as SqlExec,
|
|
32
32
|
});
|
|
33
|
-
}`,
|
|
33
|
+
}`,g=(n="")=>` const writer = this.adminWriter(${n});`,cr=`${dr}
|
|
34
34
|
|
|
35
35
|
protected override async runShardDataMigration(args: RunShardMigrationArgs): Promise<MigrationRunResult> {
|
|
36
36
|
this.ensureMigrated();
|
|
@@ -47,7 +47,7 @@ import{LunoraError as At}from"@lunora/errors";import{serializeSchemaSnapshot as
|
|
|
47
47
|
throw new LunoraError("MIGRATION_NOT_FOUND", \`data migration "\${args.id}" is not registered\`, { status: 404 });
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
${
|
|
50
|
+
${g()}
|
|
51
51
|
|
|
52
52
|
return runDataMigration({
|
|
53
53
|
...(args.batchSize === undefined ? {} : { batchSize: args.batchSize }),
|
|
@@ -80,7 +80,7 @@ ${m()}
|
|
|
80
80
|
|
|
81
81
|
this.ensureMigrated();
|
|
82
82
|
|
|
83
|
-
${
|
|
83
|
+
${g("headroom ?? this.transactionHeadroom()")}
|
|
84
84
|
|
|
85
85
|
if (args.op === "insert") {
|
|
86
86
|
const id = await writer.insert(args.table, args.doc ?? {});
|
|
@@ -135,7 +135,7 @@ ${m("headroom ?? this.transactionHeadroom()")}
|
|
|
135
135
|
protected override async runShardExport(args: RunShardExportArgs): Promise<ExportRow[]> {
|
|
136
136
|
this.ensureMigrated();
|
|
137
137
|
|
|
138
|
-
${
|
|
138
|
+
${g()}
|
|
139
139
|
|
|
140
140
|
const rows: ExportRow[] = [];
|
|
141
141
|
|
|
@@ -152,7 +152,7 @@ ${m()}
|
|
|
152
152
|
protected override async runShardImport(args: RunShardImportArgs): Promise<ImportShardResult> {
|
|
153
153
|
this.ensureMigrated();
|
|
154
154
|
|
|
155
|
-
${
|
|
155
|
+
${g()}
|
|
156
156
|
|
|
157
157
|
// \`importShardRows\` inserts with \`allowExplicitId\`, so a source
|
|
158
158
|
// database's \`_id\`s carry across verbatim and every foreign key
|
|
@@ -163,7 +163,7 @@ ${m()}
|
|
|
163
163
|
protected override async runShardRankBefore(args: RunShardRankBeforeArgs): Promise<{ before: number; total: number }> {
|
|
164
164
|
this.ensureMigrated();
|
|
165
165
|
|
|
166
|
-
${
|
|
166
|
+
${g()}
|
|
167
167
|
|
|
168
168
|
// \`rankBefore\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
169
169
|
// but the shard writer from \`createShardCtxDb\` always defines it.
|
|
@@ -181,7 +181,7 @@ ${m()}
|
|
|
181
181
|
protected override async runShardFindRelated(args: RunShardFindRelatedArgs): Promise<RelatedPage> {
|
|
182
182
|
this.ensureMigrated();
|
|
183
183
|
|
|
184
|
-
${
|
|
184
|
+
${g()}
|
|
185
185
|
|
|
186
186
|
// \`related\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
187
187
|
// but the shard writer from \`createShardCtxDb\` always defines it — it
|
|
@@ -199,7 +199,7 @@ ${m()}
|
|
|
199
199
|
protected override async runShardRankPage(args: RunShardRankPageArgs): Promise<ShardRankPageResult> {
|
|
200
200
|
this.ensureMigrated();
|
|
201
201
|
|
|
202
|
-
${
|
|
202
|
+
${g()}
|
|
203
203
|
|
|
204
204
|
// \`rankPageRows\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
205
205
|
// but the shard writer from \`createShardCtxDb\` always defines it. The sort
|
|
@@ -220,16 +220,18 @@ ${m()}
|
|
|
220
220
|
protected override async runShardApplyCdc(args: RunShardApplyCdcArgs): Promise<{ applied: number }> {
|
|
221
221
|
this.ensureMigrated();
|
|
222
222
|
|
|
223
|
-
${
|
|
223
|
+
${g()}
|
|
224
224
|
|
|
225
225
|
await applyCdcChanges(writer, args.changes);
|
|
226
226
|
|
|
227
227
|
return { applied: args.changes.length };
|
|
228
|
-
}`,
|
|
228
|
+
}`,lr=({actionOnlyBuild:n,actionOnlyFields:e,agentsBuild:t,agentsContextField:a,containersBuild:r,containersContextField:s,databaseOptions:d,everyContextBuild:f,everyContextField:v,facadeBlock:c,globalDatabaseLine:l,lazyFunctions:p=!1,notifyBuild:L,ormContextField:x,paymentsBuild:k,paymentsContextField:I,queuesBuild:i,queuesContextField:b,topicsBuild:P,topicsContextField:N,vectorsBuild:M,vectorsContextField:O,workflowsBuild:$,workflowsContextField:q})=>{const S=e.length>0,u=x===""?"{ db }":"{ db, orm: bindOrm(facade) }",D=x===""?"":`
|
|
229
|
+
orm: databaseContext.orm,`,w=x===""?"":`
|
|
230
|
+
orm: { configurable: true, enumerable: true, value: mutationDatabase.orm, writable: true },`,F=S?`
|
|
229
231
|
// ActionCtx-only helpers (external, non-deterministic I/O): constructed
|
|
230
232
|
// and attached only for an \`action\` so query/mutation ctx never carry them.
|
|
231
233
|
if (isAction) {
|
|
232
|
-
${n.replaceAll(/^(?=.)/gmu," ")}${e.map(
|
|
234
|
+
${n.replaceAll(/^(?=.)/gmu," ")}${e.map(R=>` ctx.${R} = ${R};`).join(`
|
|
233
235
|
`)}
|
|
234
236
|
}
|
|
235
237
|
`:"";return` private buildCtx(options: { bookmarks?: DispatchBookmark | undefined; functionPath?: string | undefined; headroom?: TransactionHeadroomTracker | undefined; identity?: SubscriptionIdentity | undefined; kind?: "query" | undefined; onRead?: ((table: string, idOrScan?: string) => void) | undefined; onReadRange?: ((range: KeyRange) => void) | undefined; scope?: QueryReadScope | undefined; trusted?: boolean | undefined } = {}): unknown {
|
|
@@ -259,7 +261,7 @@ ${n.replaceAll(/^(?=.)/gmu," ")}${e.map(u=>` ctx.${u} = ${u};`
|
|
|
259
261
|
};
|
|
260
262
|
})();
|
|
261
263
|
const { identity, ip, userId } = caller;
|
|
262
|
-
${
|
|
264
|
+
${M}${f}${r}${$}${i}${P}${t}
|
|
263
265
|
// Which dispatch this ctx belongs to. Drives the two deferral facades
|
|
264
266
|
// below and the \`ctx.run*\` caller guard; a ctx built for an
|
|
265
267
|
// admin/lifecycle path has no registered function, and so no kind. An
|
|
@@ -275,11 +277,9 @@ ${I}${g}${r}${M}${i}${L}${t}
|
|
|
275
277
|
// equivalent of an \`afterCommit\` hook, and the SchedulerDO persists the
|
|
276
278
|
// job the moment it is called — so inside a mutation the call is BUFFERED
|
|
277
279
|
// and replayed after the transaction commits (a rollback drops it).
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
// a
|
|
281
|
-
// An action's own schedules stay immediate — the window is only open
|
|
282
|
-
// while a transaction is (see \`runMutationTransaction\`).
|
|
280
|
+
// Composed mutations get their own facade when the caller is outside
|
|
281
|
+
// a transaction. True nested calls share the enclosing facade and open
|
|
282
|
+
// a nested window. The action's own schedules stay immediate.
|
|
283
283
|
//
|
|
284
284
|
// Every kind but \`query\` is wrapped, because \`runMutationTransaction\`
|
|
285
285
|
// is installed on \`ctx.runMutation\` for every kind but \`query\`: a
|
|
@@ -332,12 +332,11 @@ ${I}${g}${r}${M}${i}${L}${t}
|
|
|
332
332
|
]);
|
|
333
333
|
const storage = makeStorage(requestOrigin);
|
|
334
334
|
// \`ctx.storage.deleteAfterCommit(key)\`, on every dispatch that can host
|
|
335
|
-
// a MUTATION handler — which is not only a mutation dispatch
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
// one, and their ctx is built on the hot subscription path.
|
|
335
|
+
// a MUTATION handler — which is not only a mutation dispatch. An
|
|
336
|
+
// action's composed mutation gets a child ctx with its own deferred
|
|
337
|
+
// delete queue, while mutations reuse the admitted ctx. Queries stay
|
|
338
|
+
// unwrapped: they cannot host one, and their ctx is built on the hot
|
|
339
|
+
// subscription path.
|
|
341
340
|
//
|
|
342
341
|
// Every dispatch wrapped here also flushes (see \`handleRpc\` and
|
|
343
342
|
// \`runReactor\`) — a queue nothing drains leaks silently, which is worse
|
|
@@ -368,13 +367,20 @@ ${I}${g}${r}${M}${i}${L}${t}
|
|
|
368
367
|
// so both write to the same trace.
|
|
369
368
|
const span = this.makeDispatchSpan(traceAnchor, observability);
|
|
370
369
|
|
|
371
|
-
${
|
|
370
|
+
${l} // \`ctx.db\`, wrapped in automatic instrumentation: by default this
|
|
372
371
|
// adds aggregate counters (call count, total time, per-operation
|
|
373
372
|
// breakdown) to the wide event rather than a span per call, so a
|
|
374
373
|
// handler making hundreds of queries stays readable. See
|
|
375
374
|
// \`instrumentDatabase\` for the \`"spans"\` / \`"off"\` levels.
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
// Schema triggers close over this DB's scheduler, so a composed
|
|
376
|
+
// mutation must rebuild its DB and table/ORM views with its own queue.
|
|
377
|
+
const makeDatabaseContext = (scheduler: SchedulerLike) => {
|
|
378
|
+
const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${d}), logFunctionPath, traceAnchor, observability);
|
|
379
|
+
${c} return ${u};
|
|
380
|
+
};
|
|
381
|
+
const databaseContext = makeDatabaseContext(scheduler);
|
|
382
|
+
const db = databaseContext.db;
|
|
383
|
+
${k}
|
|
378
384
|
|
|
379
385
|
// \`ctx.trace\` / \`ctx.metrics\`: spans and measurements to the same sink.
|
|
380
386
|
// The trace anchor is threaded explicitly for the same reason \`identity\`
|
|
@@ -382,7 +388,7 @@ ${c}${v}
|
|
|
382
388
|
// writing mutation's trace from the shared per-request field.
|
|
383
389
|
const trace = this.makeTracer(logFunctionPath, observability, traceAnchor);
|
|
384
390
|
const metrics = this.makeMetrics(logFunctionPath, observability);
|
|
385
|
-
${
|
|
391
|
+
${L}
|
|
386
392
|
// \`ctx.now\`: the wall-clock instant (epoch ms) this function began,
|
|
387
393
|
// captured ONCE so the whole handler body sees a single stable value.
|
|
388
394
|
// A \`query\` handler is re-run by every live subscription that reads it,
|
|
@@ -433,21 +439,48 @@ ${S}
|
|
|
433
439
|
// to the handler so code that signs its own URLs (a copy-in
|
|
434
440
|
// \`createStorage\`) can fall back to it too. \`undefined\` for a
|
|
435
441
|
// query and for a deferred dispatch, for the reasons given there.
|
|
436
|
-
origin: requestOrigin,${
|
|
442
|
+
origin: requestOrigin,${D}
|
|
437
443
|
scheduler,
|
|
438
444
|
span,
|
|
439
445
|
storage: contextStorage,
|
|
440
|
-
trace,${
|
|
446
|
+
trace,${O}${v}${I}${s}${q}${b}${N}${a}
|
|
447
|
+
};
|
|
448
|
+
${S?` const isAction = contextKind === "action";
|
|
449
|
+
`:""}${F}
|
|
450
|
+
${p?` const captureScope = (): (() => void) => {
|
|
451
|
+
const requestScope = this.captureRequestScope();
|
|
452
|
+
return () => this.restoreRequestScope(requestScope);
|
|
441
453
|
};
|
|
442
|
-
|
|
443
|
-
`:""}
|
|
444
|
-
|
|
445
|
-
target.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, target, kind);
|
|
454
|
+
|
|
455
|
+
`:""} const installRun = (target: Record<string, unknown>, kind: typeof contextKind): void => {
|
|
456
|
+
target.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, target, kind${p?", undefined, captureScope":""});
|
|
446
457
|
// The composed mutation runs under the SAME wrapper the top-level RPC
|
|
447
458
|
// uses, so "do the transactional work in a mutation and call it from the
|
|
448
459
|
// action" — the recipe the docs give for atomicity — actually is atomic.
|
|
449
|
-
target.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) =>
|
|
450
|
-
|
|
460
|
+
target.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) => {
|
|
461
|
+
// An action can start sibling mutations concurrently. Each owns its
|
|
462
|
+
// context and deferral queues; only calls from an admitted mutation
|
|
463
|
+
// inherit that mutation's transaction.
|
|
464
|
+
const mutationCtx: Record<string, unknown> = this.mutationContexts.has(target)
|
|
465
|
+
? target
|
|
466
|
+
: (() => {
|
|
467
|
+
const mutationScheduler = withDeferredSchedules(schedulerBase, this.scheduleOutbox());
|
|
468
|
+
const mutationDatabase = makeDatabaseContext(mutationScheduler);
|
|
469
|
+
|
|
470
|
+
return Object.defineProperties({}, {
|
|
471
|
+
...Object.getOwnPropertyDescriptors(target),
|
|
472
|
+
db: { configurable: true, enumerable: true, value: mutationDatabase.db, writable: true },${w}
|
|
473
|
+
scheduler: { configurable: true, enumerable: true, value: mutationScheduler, writable: true },
|
|
474
|
+
storage: { configurable: true, enumerable: true, value: withDeferredDeletes(storage), writable: true },
|
|
475
|
+
});
|
|
476
|
+
})();
|
|
477
|
+
|
|
478
|
+
if (mutationCtx !== target) {
|
|
479
|
+
installRun(mutationCtx, kind);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return dispatchRun("mutation", reference.__lunoraRef, fnArgs, mutationCtx, kind, async (work) => this.runMutationTransaction(mutationCtx, work)${p?", captureScope":""});
|
|
483
|
+
};
|
|
451
484
|
// \`ctx.runQuery(ref, args, { untracked: true })\` runs the sub-query on
|
|
452
485
|
// its OWN query context, built without the subscription's read-footprint
|
|
453
486
|
// hooks — so its reads never enter this subscription's footprint and a
|
|
@@ -469,7 +502,9 @@ ${k?` const isAction = contextKind === "action";
|
|
|
469
502
|
runOptions?.untracked === true
|
|
470
503
|
? this.buildCtx({ bookmarks: options.bookmarks, functionPath: options.functionPath, headroom: options.headroom, identity: caller, kind: "query", scope: options.scope })
|
|
471
504
|
: queryContext(),
|
|
472
|
-
kind
|
|
505
|
+
kind,${p?`
|
|
506
|
+
undefined,
|
|
507
|
+
captureScope,`:""}
|
|
473
508
|
);
|
|
474
509
|
};
|
|
475
510
|
|
|
@@ -491,7 +526,7 @@ ${k?` const isAction = contextKind === "action";
|
|
|
491
526
|
|
|
492
527
|
if (queryView === undefined) {
|
|
493
528
|
const descriptors: PropertyDescriptorMap = Object.getOwnPropertyDescriptors(ctx);
|
|
494
|
-
${e.map(
|
|
529
|
+
${e.map(R=>` delete descriptors.${R};
|
|
495
530
|
`).join("")} // Writable and configurable, like the plain fields they replace.
|
|
496
531
|
const field = (value: unknown): PropertyDescriptor => ({ configurable: true, enumerable: true, value, writable: true });
|
|
497
532
|
|
|
@@ -510,7 +545,7 @@ ${e.map(u=>` delete descriptors.${u};
|
|
|
510
545
|
installRun(ctx, contextKind);
|
|
511
546
|
|
|
512
547
|
return ctx;
|
|
513
|
-
}`},
|
|
548
|
+
}`},ie=`// Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
|
|
514
549
|
// referencing call fails loudly with a clear error instead of overflowing the
|
|
515
550
|
// stack. Tracked across the awaited handler chain (one DO invocation is
|
|
516
551
|
// single-threaded), so the counter reflects true nesting depth.
|
|
@@ -576,8 +611,29 @@ const dispatchRun = async (
|
|
|
576
611
|
} finally {
|
|
577
612
|
runDepth -= 1;
|
|
578
613
|
}
|
|
579
|
-
};`,
|
|
580
|
-
|
|
614
|
+
};`,ur=n=>n?ie.replace(" runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,",` runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,
|
|
615
|
+
captureScope?: () => () => void,`).replace(` try {
|
|
616
|
+
// A mutation composed from an action`,` try {
|
|
617
|
+
let loaded = getLoadedFunction(functionPath);
|
|
618
|
+
|
|
619
|
+
if (loaded === undefined) {
|
|
620
|
+
const restoreScope = captureScope?.();
|
|
621
|
+
|
|
622
|
+
try {
|
|
623
|
+
loaded = await loadFunction(functionPath);
|
|
624
|
+
} finally {
|
|
625
|
+
restoreScope?.();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (!loaded) {
|
|
630
|
+
throw new Error(\`unknown function: \${functionPath}\`);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const loadedFunction = loaded;
|
|
634
|
+
|
|
635
|
+
// A mutation composed from an action`).replaceAll("registered.handler","loadedFunction.handler"):ie,hr=` public override async handleRpc(functionPath: string, args: Record<string, unknown>, headroom?: TransactionHeadroomTracker, scope?: QueryReadScope, bookmarks?: DispatchBookmark): Promise<unknown> {
|
|
636
|
+
__PREPARE_FUNCTION__ const registered = LUNORA_FUNCTIONS[functionPath];
|
|
581
637
|
|
|
582
638
|
// Internal functions are reachable server-side only: via \`ctx.run*\`
|
|
583
639
|
// composition, or a trusted system dispatch (scheduler/cron, marked by
|
|
@@ -631,14 +687,15 @@ const dispatchRun = async (
|
|
|
631
687
|
|
|
632
688
|
const result = await registered.handler(ctx, args);
|
|
633
689
|
|
|
634
|
-
//
|
|
635
|
-
//
|
|
636
|
-
// transaction settled lands here. A dispatch with nothing queued no-ops.
|
|
690
|
+
// Composed mutations settle their own delete queues. An action can
|
|
691
|
+
// also queue deletes outside those mutations; drain its own queue here.
|
|
637
692
|
await this.deferPastResponse(flushDeferredDeletes(ctx));
|
|
638
693
|
|
|
639
694
|
return result;
|
|
640
695
|
}
|
|
641
696
|
|
|
697
|
+
private readonly mutationContexts = new WeakSet<object>();
|
|
698
|
+
|
|
642
699
|
/**
|
|
643
700
|
* Run one mutation handler with the atomicity every caller of a mutation is
|
|
644
701
|
* promised: a BEGIN/COMMIT span (with the single-writer gate
|
|
@@ -659,11 +716,9 @@ const dispatchRun = async (
|
|
|
659
716
|
* scheduler flush.
|
|
660
717
|
*
|
|
661
718
|
* Both deferral windows are opened here and settled against the SAME
|
|
662
|
-
* outcome.
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
* action's own and the action's flush destroys objects whose rows the
|
|
666
|
-
* rollback put back.
|
|
719
|
+
* outcome. True nested calls share their caller's context, so each window
|
|
720
|
+
* must discard its own failed delete and schedule intents even when the
|
|
721
|
+
* outer handler catches the error and commits.
|
|
667
722
|
*/
|
|
668
723
|
private async runMutationTransaction<T>(ctx: unknown, work: () => Promise<T>): Promise<T> {
|
|
669
724
|
const settleSchedules = beginDeferredSchedules(ctx as { scheduler?: unknown });
|
|
@@ -671,7 +726,7 @@ const dispatchRun = async (
|
|
|
671
726
|
// settle first on every path and cannot mask the outcome.
|
|
672
727
|
const settleDeletes = beginDeferredDeletes(ctx);
|
|
673
728
|
|
|
674
|
-
if (this.
|
|
729
|
+
if (this.mutationContexts.has(ctx as object)) {
|
|
675
730
|
try {
|
|
676
731
|
const nested = await work();
|
|
677
732
|
|
|
@@ -690,7 +745,15 @@ const dispatchRun = async (
|
|
|
690
745
|
let result: T;
|
|
691
746
|
|
|
692
747
|
try {
|
|
693
|
-
result = await this.runInTransaction(
|
|
748
|
+
result = await this.runInTransaction(async () => {
|
|
749
|
+
this.mutationContexts.add(ctx as object);
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
return await work();
|
|
753
|
+
} finally {
|
|
754
|
+
this.mutationContexts.delete(ctx as object);
|
|
755
|
+
}
|
|
756
|
+
}, { independent: true });
|
|
694
757
|
} catch (error) {
|
|
695
758
|
// The span rolled back, so the rows are gone and the jobs that were
|
|
696
759
|
// to run "after I commit" must go with them. Dropping them is the
|
|
@@ -753,14 +816,14 @@ __COMMITTED_CATCH__ } finally {
|
|
|
753
816
|
// \`ctx.scheduler\` call that reached the buffer in the first place.
|
|
754
817
|
protected override scheduleOutboxScheduler(): SchedulerLike | undefined {
|
|
755
818
|
return config.scheduler?.((this.env ?? {}) as Record<string, unknown>) as SchedulerLike | undefined;
|
|
756
|
-
}`,
|
|
819
|
+
}`,pr=` protected override async executeSubscription(functionPath: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): Promise<{ ranges?: Map<string, KeyRange[]>; result: unknown; tables: Set<string> } | null> {
|
|
757
820
|
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
758
821
|
|
|
759
822
|
if (!registered || registered.kind !== "query" || registered.visibility === "internal") {
|
|
760
823
|
return null;
|
|
761
824
|
}
|
|
762
825
|
|
|
763
|
-
this.ensureMigrated();
|
|
826
|
+
__PREPARE_FUNCTION__ this.ensureMigrated();
|
|
764
827
|
|
|
765
828
|
const footprint = createReadFootprint();
|
|
766
829
|
// Identity is threaded EXPLICITLY from the (deferred/interleaved)
|
|
@@ -794,7 +857,7 @@ __COMMITTED_CATCH__ } finally {
|
|
|
794
857
|
// \`ctx.auth\` stream as nobody — or as a concurrent RPC's caller.
|
|
795
858
|
iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath, identity }), args, signal),
|
|
796
859
|
};
|
|
797
|
-
}`,
|
|
860
|
+
}`,mr=` } catch (error) {
|
|
798
861
|
// The write committed before this failed. Mark the error so the caller can tell
|
|
799
862
|
// a durable write from a rollback: the rethrow still reaches the caller unchanged.
|
|
800
863
|
if (typeof error === "object" && error !== null) {
|
|
@@ -802,7 +865,24 @@ __COMMITTED_CATCH__ } finally {
|
|
|
802
865
|
}
|
|
803
866
|
|
|
804
867
|
throw error;
|
|
805
|
-
`,
|
|
868
|
+
`,gr=(n,e=!1)=>{const t=e?` const registeredBeforeLoad = LUNORA_FUNCTIONS[functionPath];
|
|
869
|
+
|
|
870
|
+
if (registeredBeforeLoad?.visibility === "internal" && !this.isSystemDispatch()) {
|
|
871
|
+
throw new LunoraError("FUNCTION_NOT_FOUND", "function not registered: " + functionPath);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
`:"",a=e?` const requestScope = this.captureRequestScope();
|
|
875
|
+
const preparation = this.prepareFunction(functionPath, "rpc");
|
|
876
|
+
|
|
877
|
+
if (preparation !== undefined) {
|
|
878
|
+
try {
|
|
879
|
+
await preparation;
|
|
880
|
+
} finally {
|
|
881
|
+
this.restoreRequestScope(requestScope);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
`:"";return hr.replace("__COMMITTED_CATCH__",n?mr:"").replace("__PREPARE_FUNCTION__",`${t}${a}`)},fr=n=>{const e={};for(const t of n.tables){const a={};for(const[r,s]of Object.entries(t.shape)){const d=J(s);d.kind==="id"&&d.tableName!==void 0&&(a[r]=d.tableName)}Object.keys(a).length>0&&(e[t.name]=a)}return e},br=n=>{const e={};for(const t of n.tables){const a=[];for(const[r,s]of Object.entries(t.shape))J(s).kind==="storage"&&a.push(r);a.length>0&&(e[t.name]=a)}return e},wr=n=>{const e={};for(const t of n.tables){const a=[...t.indexes.map(r=>({fields:[...r.fields],name:r.name,type:"index",...r.unique===!0?{unique:!0}:{}})),...t.searchIndexes.map(r=>({fields:[r.field,...r.filterFields??[]],name:r.name,type:"search"})),...(t.geoIndexes??[]).map(r=>({fields:[r.field],name:r.name,type:"geo"})),...t.rankIndexes.map(r=>({fields:r.sortBy.map(s=>s.field),name:r.name,type:"rank"})),...t.vectorIndexes.map(r=>({fields:r.field===void 0?[]:[r.field],name:r.name,type:"vector"}))];a.length>0&&(e[t.name]=a)}return e},yr=n=>{const e=[];for(const t of n.tables)!t.ttl||t.shardMode==="global"||e.push({...t.ttl.after===void 0?{}:{after:t.ttl.after},field:t.ttl.field,...t.softDelete?{softDeleteField:t.softDelete.field}:{},table:t.name});return e},vr=n=>{if(n.kind!=="union"||n.members===void 0||n.members.length===0)return;const e=[];for(const t of n.members){if(t.kind!=="literal"||t.literalValue===void 0)return;try{const a=JSON.parse(t.literalValue);if(typeof a!="string")return;e.push(a)}catch{return}}return e},kr=(n,e,t)=>{const a=J(e),r={name:n,optional:rr(e),type:a.kind};if(a.kind==="id"&&a.tableName!==void 0){r.ref=a.tableName;const d=t.get(n);d!==void 0&&(r.onDelete=d)}a.kind==="storage"&&(r.isStorage=!0,a.bucket!==void 0&&(r.bucket=a.bucket));const s=vr(a);return s!==void 0&&(r.enumValues=s),a.column?.notNull===!1&&(r.nullable=!0),r},Sr=n=>{const e={};for(const t of n.tables){const a=[{name:"_id",optional:!1,pk:!0,type:"id"},{name:"_creationTime",optional:!1,type:"number"},...t.commitOrdered===!0?[{name:"_commitSeq",optional:!1,type:"number"}]:[]],r=new Map(t.relations.flatMap(s=>s.kind==="one"?[[s.field,s.onDelete]]:[]));for(const[s,d]of Object.entries(t.shape))a.push(kr(s,d,r));e[t.name]=a}return e},Rr=({hasMutators:n,hasShapes:e,rlsData:t})=>{const a=e?`
|
|
806
886
|
protected override resolveShape(name: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string } | undefined {
|
|
807
887
|
const shape = LUNORA_SHAPES[name];
|
|
808
888
|
|
|
@@ -865,14 +945,14 @@ __COMMITTED_CATCH__ } finally {
|
|
|
865
945
|
|
|
866
946
|
return { ...(shape.columns === undefined ? {} : { columns: shape.columns }), effectiveWhere, global: isGlobal, table: shape.table };
|
|
867
947
|
}
|
|
868
|
-
`:"",r=e?[...new Set(t.policies.filter(c=>c.on==="read"&&c.table!=="").map(c=>c.table))].toSorted((c,
|
|
948
|
+
`:"",r=e?[...new Set(t.policies.filter(c=>c.on==="read"&&c.table!=="").map(c=>c.table))].toSorted((c,l)=>c.localeCompare(l)):[],s=r.length>0?`
|
|
869
949
|
/** Refuses to boot a shape that would replicate around the read policies its table is governed by (\`defineShape({ use })\` is how a shape opts in; \`use: []\` acknowledges an ungoverned one). */
|
|
870
950
|
assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as unknown as { rlsMode?: string }).rlsMode === "required");
|
|
871
951
|
`:"";return{customMutatorOverride:n?`
|
|
872
952
|
protected override isCustomMutator(functionPath: string): boolean {
|
|
873
953
|
return LUNORA_MUTATOR_PATHS.has(functionPath);
|
|
874
954
|
}
|
|
875
|
-
`:"",shapeGuardImport:e?"assertShapeShardable, ":"",shapeReadPolicyAssertion:s,shapeReadPolicyImport:s===""?"":"assertShapesDeclareReadPolicies, ",shapeResolveOverride:a}},
|
|
955
|
+
`:"",shapeGuardImport:e?"assertShapeShardable, ":"",shapeReadPolicyAssertion:s,shapeReadPolicyImport:s===""?"":"assertShapesDeclareReadPolicies, ",shapeResolveOverride:a}},xr=(n,e)=>n?`
|
|
876
956
|
protected override async readGlobalShapeRows(resolved: { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string }, identity?: SubscriptionIdentity): Promise<Array<{ doc: Record<string, unknown>; id: string }>> {
|
|
877
957
|
const env = this.env as Record<string, unknown>;
|
|
878
958
|
// A shape read performs no writes, so the widened request only needs
|
|
@@ -938,7 +1018,7 @@ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as
|
|
|
938
1018
|
|
|
939
1019
|
return globalDb.cdcChangedTables?.(sinceSeq, cursorOnly === undefined ? {} : { cursorOnly });
|
|
940
1020
|
}
|
|
941
|
-
`:"",
|
|
1021
|
+
`:"",Ar=n=>({externalSourceOverride:n?`
|
|
942
1022
|
protected override async pollExternalSources(trace?: TraceRefLike): Promise<number | undefined> {
|
|
943
1023
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
944
1024
|
const sourced = Object.entries((schema as unknown as SchemaLike).tables)
|
|
@@ -1091,8 +1171,8 @@ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as
|
|
|
1091
1171
|
`:"",sourceClientCacheConst:n?`
|
|
1092
1172
|
const sourceClientCache = new WeakMap<object, Map<string, SourceClientLike>>();
|
|
1093
1173
|
const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
1094
|
-
`:""}),
|
|
1095
|
-
`:"",d=t?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",
|
|
1174
|
+
`:""}),Or=(n,e)=>{const t=e.length>0,r=n?_("vectorsStub: VectorSearchLike","ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().",["deleteByIds","getByIds","query","upsert","upsertMany","upsertNow"]):"",s=t?` const vectorShardKey = this.currentShardKey();
|
|
1175
|
+
`:"",d=t?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",v=`, { ${["deferAfterCommit: (work) => this.deferAfterCommit(work)",...t?["namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey",`shardedIndexNames: [${e.map(p=>JSON.stringify(p)).join(", ")}]`]:[]].join(", ")} }`,c=n?`
|
|
1096
1176
|
const vectorSync = this.vectorSync(env);
|
|
1097
1177
|
// Vectorize lives outside this shard's SQLite, so a query whose result
|
|
1098
1178
|
// depends on a similarity search cannot be proven current on reconnect —
|
|
@@ -1109,7 +1189,7 @@ const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
|
1109
1189
|
}
|
|
1110
1190
|
|
|
1111
1191
|
const lunora = createVectors({ indexes: config.vectors(env) });
|
|
1112
|
-
${s} const vectors = createContextVectors(lunora${
|
|
1192
|
+
${s} const vectors = createContextVectors(lunora${v});
|
|
1113
1193
|
|
|
1114
1194
|
return {
|
|
1115
1195
|
backfill: createVectorBackfillSync({ ${d}schema: schema as unknown as VectorSchemaLike, upsertMany: lunora.upsertMany, vectors }),
|
|
@@ -1135,14 +1215,14 @@ ${s} const vectors = createContextVectors(lunora${y});
|
|
|
1135
1215
|
ordered: async (read, work) => this.runOrderedAfterWrites(read, work),
|
|
1136
1216
|
});
|
|
1137
1217
|
}
|
|
1138
|
-
`:"",vectorsBuild:c,vectorsStub:r}},
|
|
1218
|
+
`:"",vectorsBuild:c,vectorsStub:r}},Fr=({advisories:n=[],advisorProcedures:e=[],agents:t=[],capabilities:a=new Set,containers:r=[],env:s,flagKeys:d=[],hasFlags:f=!1,hasNotify:v=!1,hasRooms:c=!1,lazyFunctions:l=!1,hasVectors:p=!0,maskMetadata:L,mutators:x=[],queues:k=[],rlsMetadata:I,schema:i,schemaSnapshot:b,shapes:P=[],storageRules:N,studioFeatures:M,services:O=[],topics:$=[],useUmbrella:q=!1,workflows:S=[]})=>{const u=ar(q),D=x.length>0,w=P.length>0,F=b===void 0?"":Pt(b),z=b===void 0?"":`
|
|
1139
1219
|
/** Structural schema snapshot + its content hash, recorded in the shard's \`__lunora_schema_history\` ledger on cold start so the studio can show a schema-version timeline and diff any two versions. */
|
|
1140
|
-
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(
|
|
1141
|
-
`,
|
|
1142
|
-
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",
|
|
1143
|
-
d1?: (env: Record<string, unknown>, request?: { bookmark?: string; cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; onBookmark?: (bookmark: string | undefined) => void; userId?: string }) => DatabaseWriterLike | undefined;`:"",
|
|
1144
|
-
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",
|
|
1145
|
-
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",
|
|
1220
|
+
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(Nt(b))}, json: ${JSON.stringify(F)} };
|
|
1221
|
+
`,R=b===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",y=Ft(a),T=Ut(f,u.flags),U=Bt(d,f,u.flags),B=tr(v),j=jt(s),{build:de,contextField:ce,importLines:le,specs:ue}=Wt(sr(k)),{build:he,contextField:pe,importLines:me,specs:ge}=Ht($,k),W=Vt(O,u.server),{build:fe,contextField:be,importLines:we,specs:ye}=Kt(r,i.jurisdiction),{build:ve,contextField:ke,importLines:Se,specs:Re}=Gt(S),{build:xe,contextField:Ae,importLines:Oe,specs:De}=Qt(t),{build:Te,configField:Ce,contextField:Ee,imports:_e,stub:Le}=Jt(a.has("payments")),Ie=n,{advisoryRules:Pe,advisoryRows:Ne}=ir(Ie),Me=e,Y=I??{policies:[],roles:[]},$e=L??{columns:[]},qe=N??{rules:[]},Fe=M??{analytics:!1,auth:!1,containers:!1,flags:!1,kv:!1,mail:!1,notifications:!1,payments:!1,privacy:!1,queues:!1,scheduler:!1,storage:!1,vectors:!1,workflows:!1},{constant:Ue,override:Be}=zt(S),{constant:je,override:We}=Yt(k),m=p&&i.vectorIndexes.length>0,H=new Set(i.tables.filter(o=>or(o)).map(o=>o.name)),X=m&&i.vectorIndexes.some(o=>H.has(o.table)),A=i.tables.some(o=>o.shardMode==="global"),V=Xt(H),K=i.tables.some(o=>Mt(o)),Z=i.tables.some(o=>$t(o)),C=i.tables.some(o=>o.externalSource!==void 0),ee=i.tables.some(o=>o.memory===!0),G=i.tables.some(o=>o.shardMode!=="global"&&o.searchIndexes.length>0);if(Z&&K)throw new It("INTERNAL",'lunora codegen: mixing `.global()` (D1) and `.global({ backend: "hyperdrive" })` tables in one app is not supported yet — use a single global backend.');const Q=i.tables.length>0,He=fr(i),Ve=wr(i),Ke=Sr(i),Ge=br(i),te=yr(i),re=Zt(m,S.length>0,k.length>0,f,G);w&&re.push("WhereInput");const ne=er(A),{customMutatorOverride:Qe,shapeGuardImport:Je,shapeReadPolicyAssertion:ze,shapeReadPolicyImport:Ye,shapeResolveOverride:Xe}=Rr({hasMutators:D,hasShapes:w,rlsData:Y}),E=[`import type { ${re.join(", ")} } from "${u.do}";`,`import { applyCdcChanges, createStableIdFactory, ${G?"backfillSearchIndexes, ":""}${m?"backfillVectorIndexes, ":""}buildReprojectionMigration, ${ee?"clearMemoryTables, ":""}${Je}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${C?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}markUnvouchableReads, ${X?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${ne.importFragment}ShardDO as ShardDOBase } from "${u.do}";`,...C?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${u.do}";`]:[],w?`import { asBucketStorage, ${Ye}beginDeferredDeletes, beginDeferredSchedules, composeShapeReadWhere, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${u.server}";`:`import { asBucketStorage, beginDeferredDeletes, beginDeferredSchedules, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${u.server}";`];Q&&E.push(`import { bindOrm, bindTableFacade } from "${u.server}";`),m&&E.push('import type { SchemaLike as VectorSchemaLike, VectorBackfillSync, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectorBackfillSync, createVectors, createVectorSyncHook, vectorBackfillTargets } from "@lunora/bindings/vectors";'),E.push(...y.importLines,...T.importLines,...B.importLines,...j.importLines,...we,...Se,...le,...me,...W.importLines,...Oe,..._e,"",'import schema from "../schema.js";',`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...l?["getLoadedFunction","loadFunction"]:[],...D?["LUNORA_MUTATOR_PATHS"]:[],...c?["LUNORA_ROOMS"]:[],...w?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const Ze=m?`
|
|
1222
|
+
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",et=Z?`
|
|
1223
|
+
d1?: (env: Record<string, unknown>, request?: { bookmark?: string; cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; onBookmark?: (bookmark: string | undefined) => void; userId?: string }) => DatabaseWriterLike | undefined;`:"",tt=K?`
|
|
1224
|
+
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",rt=C?`
|
|
1225
|
+
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",nt="ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().",at="ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().",st="ctx.storage: no storage configured. Pass `storage` to createShardDO().",ot=A?_("globalDbStub: DatabaseWriterLike",nt,["aggregate","count","delete","explain","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",{vectorSyncMethod:it,vectorsBuild:dt,vectorsStub:ct}=Or(m,X?i.vectorIndexes.filter(o=>H.has(o.table)).map(o=>o.name):[]),lt=`{
|
|
1146
1226
|
...this.ctxDbTuning(),
|
|
1147
1227
|
auth: { identity: identity ?? null, userId: userId ?? null },
|
|
1148
1228
|
broadcast: (delta) => {
|
|
@@ -1182,7 +1262,7 @@ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.st
|
|
|
1182
1262
|
// fallback rather than reporting into a no-op.
|
|
1183
1263
|
...(options.onReadRange !== undefined
|
|
1184
1264
|
? { onReadRange: options.onReadRange }
|
|
1185
|
-
: options.scope === undefined ? {} : { onReadRange: this.getCtxDbReadRangeHook(options.scope) }),${
|
|
1265
|
+
: options.scope === undefined ? {} : { onReadRange: this.getCtxDbReadRangeHook(options.scope) }),${m?`
|
|
1186
1266
|
// Vectorize is outside this shard's SQLite and cannot roll back, so
|
|
1187
1267
|
// the sync is held until the mutation's transaction has COMMITTED
|
|
1188
1268
|
// (\`deferAfterCommit\`) instead of running inline. Inline, a write
|
|
@@ -1194,11 +1274,11 @@ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.st
|
|
|
1194
1274
|
scheduler,
|
|
1195
1275
|
schema: schema as unknown as SchemaLike,
|
|
1196
1276
|
sql: this.sql as SqlExec,
|
|
1197
|
-
storage,${
|
|
1277
|
+
storage,${A?`
|
|
1198
1278
|
globalDb,`:""}
|
|
1199
|
-
}`,
|
|
1200
|
-
vectors,`:"",
|
|
1201
|
-
orm: bindOrm(facade),`:"",
|
|
1279
|
+
}`,ut=m?`
|
|
1280
|
+
vectors,`:"",ht=Q?`
|
|
1281
|
+
orm: bindOrm(facade),`:"",ae=K?"config.hyperdriveGlobal":"config.d1",pt=A?` const bookmark = this.getInboundBookmark();
|
|
1202
1282
|
const globalRequest = {
|
|
1203
1283
|
...this.globalCdcOptions(config.cdc ?? false),
|
|
1204
1284
|
...(bookmark === undefined ? {} : { bookmark }),
|
|
@@ -1206,23 +1286,23 @@ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.st
|
|
|
1206
1286
|
onBookmark: (bookmarkValue: string | undefined) => { if (options.bookmarks !== undefined) { options.bookmarks.value = bookmarkValue; } },
|
|
1207
1287
|
...(userId === undefined ? {} : { userId }),
|
|
1208
1288
|
};
|
|
1209
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1210
|
-
`:"",
|
|
1289
|
+
const globalDb: DatabaseWriterLike = ${ae}?.(env, globalRequest) ?? globalDbStub;
|
|
1290
|
+
`:"",mt=xr(w&&A,ae),gt=c?` protected override roomHandlerPath(topic: string): string | undefined {
|
|
1211
1291
|
// Own properties only: a plain object would resolve \`toString\` and the like to inherited functions.
|
|
1212
1292
|
return Object.hasOwn(LUNORA_ROOMS, topic) ? LUNORA_ROOMS[topic] : undefined;
|
|
1213
1293
|
}
|
|
1214
|
-
`:"",{externalSourceOverride:
|
|
1294
|
+
`:"",{externalSourceOverride:ft,sourceBootstrap:bt,sourceClientCacheConst:wt}=Ar(C),yt=`
|
|
1215
1295
|
protected override async runShardInit(): Promise<void> {
|
|
1216
1296
|
this.ensureMigrated();
|
|
1217
|
-
${
|
|
1297
|
+
${ee?` clearMemoryTables(this.sql as SqlExec, schema as unknown as SchemaLike);
|
|
1218
1298
|
`:""}
|
|
1219
1299
|
await this.dispatchShardInit();
|
|
1220
1300
|
}
|
|
1221
|
-
`,
|
|
1301
|
+
`,vt=te.length>0?`
|
|
1222
1302
|
if (LUNORA_TTL_SWEEPS.length > 0) {
|
|
1223
1303
|
void this.scheduleTtlSweep();
|
|
1224
1304
|
}
|
|
1225
|
-
`:"",
|
|
1305
|
+
`:"",kt=`
|
|
1226
1306
|
public constructor(state: ShardDOState, env: unknown) {
|
|
1227
1307
|
super(state, env, {
|
|
1228
1308
|
// Every writer this file builds spreads the ctxDbTuning() slice —
|
|
@@ -1239,7 +1319,7 @@ ${J?` clearMemoryTables(this.sql as SqlExec, schema as unknown as Sch
|
|
|
1239
1319
|
// exists — a trigger that fails first still flushes and fuses.
|
|
1240
1320
|
...(config.observability === undefined ? {} : { telemetrySink: () => config.observability?.((env ?? {}) as Record<string, unknown>) }),
|
|
1241
1321
|
});
|
|
1242
|
-
${
|
|
1322
|
+
${bt}${vt} }
|
|
1243
1323
|
|
|
1244
1324
|
protected override isCacheableQuery(functionPath: string): boolean {
|
|
1245
1325
|
// NOT \`kind === "query"\` alone. A cache hit answers without running
|
|
@@ -1263,14 +1343,34 @@ ${mt}${bt} }
|
|
|
1263
1343
|
protected override isPaidFunction(functionPath: string): boolean {
|
|
1264
1344
|
return LUNORA_FUNCTIONS[functionPath]?.x402 !== undefined;
|
|
1265
1345
|
}
|
|
1266
|
-
`,
|
|
1346
|
+
`,se=l?`
|
|
1347
|
+
protected override prepareFunction(functionPath: string, reason: "rpc" | "subscription" | "stream" | "reactor"): Promise<void> | undefined {
|
|
1348
|
+
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
1349
|
+
const allowsInternal = reason === "reactor" || (reason === "rpc" && this.isSystemDispatch());
|
|
1350
|
+
|
|
1351
|
+
if (registered?.visibility === "internal" && !allowsInternal) {
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
if (getLoadedFunction(functionPath) !== undefined) {
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
return loadFunction(functionPath).then(() => undefined);
|
|
1360
|
+
}
|
|
1361
|
+
`:"",St=se===""?`
|
|
1362
|
+
|
|
1363
|
+
`:`${se}
|
|
1364
|
+
`,Rt=ur(l),xt=l?` const preparation = this.prepareFunction(functionPath, "subscription");
|
|
1365
|
+
if (preparation !== undefined) await preparation;
|
|
1366
|
+
`:"",At=pr.replace("__PREPARE_FUNCTION__",xt),Ot=gr(c,l),Dt=Q?`
|
|
1267
1367
|
const facade = db as unknown as Record<string, ReturnType<typeof bindTableFacade>>;
|
|
1268
1368
|
${i.tables.map(o=>` facade[${JSON.stringify(o.name)}] = bindTableFacade(db, ${JSON.stringify(o.name)});`).join(`
|
|
1269
1369
|
`)}
|
|
1270
|
-
`:"",
|
|
1370
|
+
`:"",Tt=`${y.everyBuild}${T.build}${j.build}
|
|
1271
1371
|
const secrets = createSecrets(env);
|
|
1272
|
-
`,
|
|
1273
|
-
secrets,`,
|
|
1372
|
+
`,Ct=`${y.everyFields}${T.contextField}${B.contextField}${j.contextField}
|
|
1373
|
+
secrets,`,Et=B.build,_t=[...y.actionFields,...O.length>0?["services"]:[]],Lt=`${y.actionBuild}${W.build}`;return`${nr}${E.join(`
|
|
1274
1374
|
`)}
|
|
1275
1375
|
|
|
1276
1376
|
type FunctionKind = "action" | "mutation" | "query";
|
|
@@ -1280,13 +1380,13 @@ interface FunctionReference {
|
|
|
1280
1380
|
}
|
|
1281
1381
|
|
|
1282
1382
|
/** Foreign-key columns per table (\`v.id("target")\` fields) for the data browser. */
|
|
1283
|
-
const LUNORA_TABLE_REFS = ${h(
|
|
1383
|
+
const LUNORA_TABLE_REFS = ${h(He,"Record<string, Record<string, string>>")};
|
|
1284
1384
|
|
|
1285
1385
|
/** Declared indexes per table (secondary, search, geo, rank, vector) for the schema viewer. */
|
|
1286
|
-
const LUNORA_TABLE_INDEXES = ${h(
|
|
1386
|
+
const LUNORA_TABLE_INDEXES = ${h(Ve,'Record<string, Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }>>')};
|
|
1287
1387
|
|
|
1288
1388
|
/** Columns per table (typed, with PK/FK markers) for the studio's schema diagram, served via \`__lunora_admin__:describeTable\`. */
|
|
1289
|
-
const LUNORA_TABLE_COLUMNS = ${h(
|
|
1389
|
+
const LUNORA_TABLE_COLUMNS = ${h(Ke,`Record<
|
|
1290
1390
|
string,
|
|
1291
1391
|
Array<{
|
|
1292
1392
|
bucket?: string;
|
|
@@ -1303,16 +1403,16 @@ const LUNORA_TABLE_COLUMNS = ${h(je,`Record<
|
|
|
1303
1403
|
>`)};
|
|
1304
1404
|
|
|
1305
1405
|
/** Storage-key columns per table (\`v.storage(...)\` fields) for the file browser's records↔files join. */
|
|
1306
|
-
const LUNORA_STORAGE_COLUMNS = ${h(
|
|
1406
|
+
const LUNORA_STORAGE_COLUMNS = ${h(Ge,"Record<string, string[]>")};
|
|
1307
1407
|
|
|
1308
1408
|
/** Declarative TTL policies (\`.ttl(field, { after? })\`) the DO alarm sweep auto-expires rows for. */
|
|
1309
|
-
const LUNORA_TTL_SWEEPS = ${
|
|
1409
|
+
const LUNORA_TTL_SWEEPS = ${qt(te,"Array<{ after?: number; field: string; softDeleteField?: string; table: string }>")};
|
|
1310
1410
|
|
|
1311
1411
|
/** Static schema advisories (computed by @lunora/advisor at codegen time) served via \`__lunora_admin__:getAdvisories\`. */
|
|
1312
|
-
const LUNORA_ADVISORY_RULES = ${h(
|
|
1412
|
+
const LUNORA_ADVISORY_RULES = ${h(Pe,"Record<string, Partial<AdvisoryFinding>>")};
|
|
1313
1413
|
|
|
1314
1414
|
/** Per-finding rows; rule-level text lives once in \`LUNORA_ADVISORY_RULES\` and is merged back on first read. */
|
|
1315
|
-
const LUNORA_ADVISORY_ROWS = ${h(
|
|
1415
|
+
const LUNORA_ADVISORY_ROWS = ${h(Ne,"Array<Partial<AdvisoryFinding> & { name: string }>")};
|
|
1316
1416
|
|
|
1317
1417
|
const LUNORA_ADVISORIES = ((): (() => AdvisoryFinding[]) => {
|
|
1318
1418
|
let cached: AdvisoryFinding[] | undefined;
|
|
@@ -1320,20 +1420,20 @@ const LUNORA_ADVISORIES = ((): (() => AdvisoryFinding[]) => {
|
|
|
1320
1420
|
})();
|
|
1321
1421
|
|
|
1322
1422
|
/** Every declared procedure (discovered by @lunora/codegen) served via \`__lunora_admin__:getAdvisorProcedures\` — the health map's denominator. */
|
|
1323
|
-
const LUNORA_ADVISOR_PROCEDURES = ${h(
|
|
1423
|
+
const LUNORA_ADVISOR_PROCEDURES = ${h(Me,"AdvisorProcedure[]")};
|
|
1324
1424
|
|
|
1325
1425
|
/** Read-only RLS metadata (policies + roles discovered from \`.use(rls(...))\` chains) served via \`__lunora_admin__:rlsPolicies\` for the studio's RLS inspector. */
|
|
1326
|
-
const LUNORA_RLS_METADATA = ${h(
|
|
1327
|
-
${
|
|
1426
|
+
const LUNORA_RLS_METADATA = ${h(Y,"RlsPoliciesResult")};
|
|
1427
|
+
${ze}
|
|
1328
1428
|
/** Read-only masking metadata (table + column + strategy discovered from \`.use(mask(...))\` chains) served via \`__lunora_admin__:maskPolicies\` for the studio's data-browser mask preview. */
|
|
1329
|
-
const LUNORA_MASK_METADATA = ${h(
|
|
1429
|
+
const LUNORA_MASK_METADATA = ${h($e,"MaskPoliciesResult")};
|
|
1330
1430
|
|
|
1331
1431
|
/** Read-only storage access-rule metadata (discovered from \`.use(storageRules(...))\` chains) served via \`__lunora_admin__:storageRules\` for the studio's access-rules view. */
|
|
1332
|
-
const LUNORA_STORAGE_RULES = ${h(
|
|
1432
|
+
const LUNORA_STORAGE_RULES = ${h(qe,"StorageRulesResult")};
|
|
1333
1433
|
|
|
1334
1434
|
/** Which optional package-backed features this app wires up (discovered from imports / \`ctx.*\` reads / schema signals) served via \`__lunora_admin__:studioFeatures\` so the studio hides nav pages whose package isn't enabled. */
|
|
1335
|
-
const LUNORA_STUDIO_FEATURES = ${h(
|
|
1336
|
-
${
|
|
1435
|
+
const LUNORA_STUDIO_FEATURES = ${h(Fe,"StudioFeaturesResult")};
|
|
1436
|
+
${z}${V.constant}${U.constant}${Ue}${je}${ye}${Re}${ue}${ge}${W.specs}${De}
|
|
1337
1437
|
export interface ShardDOConfig {
|
|
1338
1438
|
/** Current authority for RPC and ordinary subscription deliveries; other live delivery modes are refused. */
|
|
1339
1439
|
deliveryAuthorization?: (env: Record<string, unknown>) => DeliveryAuthorization;
|
|
@@ -1350,27 +1450,25 @@ export interface ShardDOConfig {
|
|
|
1350
1450
|
/** \`unknown\` because \`@lunora/scheduler\`'s \`Scheduler\` is not assignable to \`SchedulerLike\`; the shard casts it. */
|
|
1351
1451
|
scheduler?: (env: Record<string, unknown>) => unknown;
|
|
1352
1452
|
/** \`origin\` is the origin the current \`/rpc\` request reached the worker on — the fallback base for signed object URLs when no \`publicBaseUrl\` is configured. \`undefined\` off the synchronous dispatch path. */
|
|
1353
|
-
storage?: (env: Record<string, unknown>, origin?: string) => unknown;${
|
|
1453
|
+
storage?: (env: Record<string, unknown>, origin?: string) => unknown;${Ze}${y.configFields}${T.configField}${Ce}${et}${tt}${rt}${V.configField}
|
|
1354
1454
|
}
|
|
1355
|
-
${
|
|
1356
|
-
${
|
|
1455
|
+
${_("schedulerStub",at,["cancel","runAfter","runAt"])}${_("storageStub",st,["createMultipartUpload","delete","download","generateUploadUrl","getMetadata","getPresignedUrl","getSignedUrl","getUrl","head","list","resumeMultipartUpload","store","upload"],{sync:["getUrl","resumeMultipartUpload"]})}${ot}${wt}${ct}${y.stubs}${Le}
|
|
1456
|
+
${Rt}
|
|
1357
1457
|
|
|
1358
1458
|
/**
|
|
1359
1459
|
* Build the project's shard Durable Object. Export the result as \`ShardDO\`
|
|
1360
1460
|
* from the worker entry so wrangler binds it by name.
|
|
1361
1461
|
*/
|
|
1362
1462
|
export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOState, env: unknown) => ShardDOBase =>
|
|
1363
|
-
class extends ShardDOBase {${
|
|
1364
|
-
private migrated = false
|
|
1365
|
-
|
|
1366
|
-
${
|
|
1367
|
-
${
|
|
1368
|
-
${sr}
|
|
1369
|
-
${Ve}${Qe}${ht}${pt}
|
|
1463
|
+
class extends ShardDOBase {${kt}
|
|
1464
|
+
private migrated = false;${St}${Ot}
|
|
1465
|
+
${ne.override}
|
|
1466
|
+
${At}
|
|
1467
|
+
${Qe}${Xe}${mt}${ft}
|
|
1370
1468
|
protected override lifecycleHookPaths(event: "connect" | "disconnect" | "init" | "reactor" | "whisper"): readonly string[] {
|
|
1371
1469
|
return LUNORA_LIFECYCLE_HOOKS[event];
|
|
1372
1470
|
}
|
|
1373
|
-
${
|
|
1471
|
+
${gt}${V.override}${yt}
|
|
1374
1472
|
// One \`onQueryChange\` dispatch. Mirrors \`executeSubscription\` — the
|
|
1375
1473
|
// socket-terminated half of the same reactivity — and differs only in who
|
|
1376
1474
|
// consumes the result: the base's \`dispatchReactors\` stores the digest as
|
|
@@ -1391,7 +1489,18 @@ ${ut}${W.override}${ft}
|
|
|
1391
1489
|
// would instead run an app's reactor as whichever user happened to write
|
|
1392
1490
|
// last, which is worse than running it as nobody.
|
|
1393
1491
|
protected override async runReactor(functionPath: string, previousDigest?: string): Promise<{ digest: string; ran: boolean; tables: readonly string[] } | undefined> {
|
|
1394
|
-
const
|
|
1492
|
+
${l?` const requestScope = this.captureRequestScope();
|
|
1493
|
+
const preparation = this.prepareFunction(functionPath, "reactor");
|
|
1494
|
+
|
|
1495
|
+
if (preparation !== undefined) {
|
|
1496
|
+
try {
|
|
1497
|
+
await preparation;
|
|
1498
|
+
} finally {
|
|
1499
|
+
this.restoreRequestScope(requestScope);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
`:""} const registered = LUNORA_FUNCTIONS[functionPath];
|
|
1395
1504
|
|
|
1396
1505
|
if (!registered || registered.kind !== "mutation") {
|
|
1397
1506
|
return undefined;
|
|
@@ -1458,7 +1567,7 @@ ${ut}${W.override}${ft}
|
|
|
1458
1567
|
protected override studioFeatures(): StudioFeaturesResult {
|
|
1459
1568
|
return LUNORA_STUDIO_FEATURES();
|
|
1460
1569
|
}
|
|
1461
|
-
${
|
|
1570
|
+
${U.evaluateOverride}${U.subscriptionOverride}${Be}${We}
|
|
1462
1571
|
protected override advisories(): AdvisoryFinding[] {
|
|
1463
1572
|
return LUNORA_ADVISORIES();
|
|
1464
1573
|
}
|
|
@@ -1467,17 +1576,17 @@ ${$.evaluateOverride}${$.subscriptionOverride}${qe}${Ue}
|
|
|
1467
1576
|
return LUNORA_ADVISOR_PROCEDURES();
|
|
1468
1577
|
}
|
|
1469
1578
|
|
|
1470
|
-
${
|
|
1579
|
+
${cr}
|
|
1471
1580
|
|
|
1472
1581
|
protected override ensureMigrated(): void {
|
|
1473
1582
|
if (this.migrated) {
|
|
1474
1583
|
return;
|
|
1475
1584
|
}
|
|
1476
1585
|
|
|
1477
|
-
runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false${
|
|
1586
|
+
runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false${R} });
|
|
1478
1587
|
this.migrated = true;
|
|
1479
1588
|
}
|
|
1480
|
-
${
|
|
1589
|
+
${G?`
|
|
1481
1590
|
protected override runShardSearchBackfill(options: { maxPages?: number }): SearchBackfillProgress {
|
|
1482
1591
|
// Migrations create the fts5 companions; without them every backfill
|
|
1483
1592
|
// statement would raise "no such table" on a shard that has never
|
|
@@ -1486,7 +1595,7 @@ ${H?`
|
|
|
1486
1595
|
|
|
1487
1596
|
return backfillSearchIndexes(this.sql as SqlExec, schema as unknown as SchemaLike, options);
|
|
1488
1597
|
}
|
|
1489
|
-
`:""}${
|
|
1490
|
-
${
|
|
1598
|
+
`:""}${it}
|
|
1599
|
+
${lr({lazyFunctions:l,actionOnlyFields:_t,agentsBuild:xe,agentsContextField:Ae,containersBuild:fe,containersContextField:be,databaseOptions:lt,everyContextBuild:Tt,everyContextField:Ct,facadeBlock:Dt,globalDatabaseLine:pt,notifyBuild:Et,ormContextField:ht,paymentsBuild:Te,paymentsContextField:Ee,queuesBuild:de,queuesContextField:ce,topicsBuild:he,topicsContextField:pe,vectorsBuild:dt,vectorsContextField:ut,workflowsBuild:ve,workflowsContextField:ke,actionOnlyBuild:Lt})}
|
|
1491
1600
|
};
|
|
1492
|
-
`};export{
|
|
1601
|
+
`};export{br as b,Fr as e};
|