@lunora/codegen 1.0.0-alpha.335 → 1.0.0-alpha.337
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 +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{AGENTS_FILENAME-8zoeGNsB.mjs → AGENTS_FILENAME-isdHqPIt.mjs} +1 -1
- package/dist/packem_shared/CONTAINERS_FILENAME-mK4cZpcz.mjs +1 -0
- package/dist/packem_shared/{DEFAULT_TARGET-CDksdcYc.mjs → DEFAULT_TARGET-3OLdJhTj.mjs} +1 -1
- package/dist/packem_shared/{FLAGS_FILENAME-Cwq0B2Nl.mjs → FLAGS_FILENAME-BQk3SX8D.mjs} +1 -1
- package/dist/packem_shared/GENERATED_HEADER-BXK9A_wP.mjs +1 -0
- package/dist/packem_shared/LUNORA_ERROR_CODES-CIAJQeKB.mjs +1 -0
- package/dist/packem_shared/{MUTATORS_FILENAME-DZFH7a9G.mjs → MUTATORS_FILENAME-Cy9h3EVW.mjs} +1 -1
- package/dist/packem_shared/{NOTIFY_FILENAME-DZIt-rb0.mjs → NOTIFY_FILENAME-9to_l_Kh.mjs} +1 -1
- package/dist/packem_shared/{OPENRPC_VERSION-CeEGdAZ9.mjs → OPENRPC_VERSION-l4HD4osw.mjs} +1 -1
- package/dist/packem_shared/{QUEUES_FILENAME-EvfCmmv6.mjs → QUEUES_FILENAME-Civ-7SiI.mjs} +1 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-6cfIWExE.mjs +31 -0
- package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-C7Um0EnM.mjs +2 -0
- package/dist/packem_shared/{SHAPES_FILENAME-Ciu5tx7B.mjs → SHAPES_FILENAME-DVJbfDZ8.mjs} +1 -1
- package/dist/packem_shared/SchemaSnapshotParseError-H9eQX4ui.mjs +18 -0
- package/dist/packem_shared/{WORKFLOWS_FILENAME-C5tz9gMh.mjs → WORKFLOWS_FILENAME-kXq07aGl.mjs} +1 -1
- package/dist/packem_shared/{attribution-RI-H1KPm.mjs → attribution-CG8-kUcl.mjs} +1 -1
- package/dist/packem_shared/{buildOpenApiDocument-BBao-3vL.mjs → buildOpenApiDocument-DWdj2jvW.mjs} +1 -1
- package/dist/packem_shared/capabilitiesUsedBy-C-7D7vlH.mjs +1 -0
- package/dist/packem_shared/chain-74gjwZMP.mjs +1 -0
- package/dist/packem_shared/{classify-procedure-call-C5JY2w5V.mjs → classify-procedure-call-BkaeM9Ej.mjs} +1 -1
- package/dist/packem_shared/{context-property-calls-M6Oo1xQT.mjs → context-property-calls-dFjpeIlP.mjs} +1 -1
- package/dist/packem_shared/{context-root-DQd48e1F.mjs → context-root-CNR6TAsP.mjs} +1 -1
- package/dist/packem_shared/{createCodegenProject-C-fKC9h4.mjs → createCodegenProject-R4so-yTU.mjs} +1 -1
- package/dist/packem_shared/{database-calls-BNoSJRFy.mjs → database-calls-UPlhi3Ks.mjs} +1 -1
- package/dist/packem_shared/discoverAuthApiCalls-CScPrUD-.mjs +1 -0
- package/dist/packem_shared/discoverCrons-B1_NUt19.mjs +1 -0
- package/dist/packem_shared/{discoverFunctions-CJu5EH_G.mjs → discoverFunctions-DkPOc8b9.mjs} +1 -1
- package/dist/packem_shared/{discoverHttpRoutes-MrMxIY66.mjs → discoverHttpRoutes-DCDgDIYv.mjs} +1 -1
- package/dist/packem_shared/discoverHyperdriveCalls-CL-d0IVo.mjs +1 -0
- package/dist/packem_shared/{discoverInserts-Boh6WAYw.mjs → discoverInserts-BcoTjXqI.mjs} +1 -1
- package/dist/packem_shared/discoverMaskProcedures-Bw-BOIbA.mjs +1 -0
- package/dist/packem_shared/{discoverMigrations-BMdGxHeq.mjs → discoverMigrations-BWxx8Wv9.mjs} +1 -1
- package/dist/packem_shared/{discoverNondeterministicCalls-c9xenrIN.mjs → discoverNondeterministicCalls-3INW8oqs.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-o_00HnBB.mjs → discoverQueries-CxQRVuIV.mjs} +1 -1
- package/dist/packem_shared/discoverR2sqlCalls-B4oeTYEM.mjs +1 -0
- package/dist/packem_shared/{discoverRlsMetadata-C7J8dgVz.mjs → discoverRlsMetadata-D0B4nCmG.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsProcedures-Di3VEk-D.mjs → discoverRlsProcedures-Cl6UKT7L.mjs} +1 -1
- package/dist/packem_shared/{discoverSandboxUsage-CrhZZJZS.mjs → discoverSandboxUsage--Z1vx7GM.mjs} +1 -1
- package/dist/packem_shared/discoverSchema-Cwt-DQgh.mjs +1 -0
- package/dist/packem_shared/{discoverStorageRulesMetadata-BFCmbsIN.mjs → discoverStorageRulesMetadata-CitX6ziW.mjs} +1 -1
- package/dist/packem_shared/{emitAgents-CR2_X_ov.mjs → emitAgents--VDKoet4.mjs} +1 -1
- package/dist/packem_shared/{emitApi-CDgIPAG2.mjs → emitApi-c14__tie.mjs} +24 -24
- package/dist/packem_shared/{emitApp-DuJW6LKD.mjs → emitApp-BL16wvE9.mjs} +1 -1
- package/dist/packem_shared/{emitCrons-BQW2lviZ.mjs → emitCrons-lc-hnJYn.mjs} +1 -1
- package/dist/packem_shared/{emitDataModel-BskFt9Wv.mjs → emitDataModel-CuFqq97x.mjs} +1 -1
- package/dist/packem_shared/{emitDrizzleSchema-DitcRZ1a.mjs → emitDrizzleSchema-bct1OuH5.mjs} +1 -1
- package/dist/packem_shared/{emitFunctions-Cd0Tuw73.mjs → emitFunctions-CkBPtGDF.mjs} +71 -65
- package/dist/packem_shared/{emitServer-D1vA5_X1.mjs → emitServer-CGUhLmlG.mjs} +1 -1
- package/dist/packem_shared/emitShard-fpKA7Svs.mjs +1 -0
- package/dist/packem_shared/{exported-procedure-chains-Bol_VtzO.mjs → exported-procedure-chains-D1WlRfqJ.mjs} +1 -1
- package/dist/packem_shared/{fingerprintSchemaSources-BdFj8vPc.mjs → fingerprintSchemaSources-BKXkgkj4.mjs} +1 -1
- package/dist/packem_shared/{handler-reference-8w_VU4XY.mjs → handler-reference-CClSR4bi.mjs} +1 -1
- package/dist/packem_shared/index-CN8FVvr-.mjs +1 -0
- package/dist/packem_shared/{isTestPath-Cdv_LoVU.mjs → isTestPath-C3QumE88.mjs} +1 -1
- package/dist/packem_shared/loadProjectConfig-B7RmxIeD.mjs +1 -0
- package/dist/packem_shared/{modules-DDIIcM7P.mjs → modules-l-Db0C4S.mjs} +1 -1
- package/dist/packem_shared/parse-validator-iVTounLK.mjs +1 -0
- package/dist/packem_shared/property-name-7cBlgK_O.mjs +1 -0
- package/dist/packem_shared/{qualifiers-DEdXn5wP.mjs → qualifiers-Bl5fSmav.mjs} +1 -1
- package/dist/packem_shared/readServiceBindings-BA49C6aN.mjs +1 -0
- package/dist/packem_shared/{server-DAvi_bv_.mjs → server-B6VhYK7m.mjs} +1 -1
- package/dist/packem_shared/{shard-CsfiXxV0.mjs → shard-BCqz_05A.mjs} +267 -142
- package/dist/packem_shared/{shard-runtime-BpoCV-aM.mjs → shard-runtime-ByYXjCKY.mjs} +10 -8
- package/dist/packem_shared/shared-8vTRkL2-.mjs +13 -0
- package/dist/packem_shared/{unwrap-handler-return-Bfz7lH0C.mjs → unwrap-handler-return-CGdJGvGh.mjs} +1 -1
- package/dist/sdk/index.mjs +15 -15
- package/package.json +9 -9
- package/dist/packem_shared/CONTAINERS_FILENAME-QiNt84QA.mjs +0 -1
- package/dist/packem_shared/GENERATED_HEADER-Ck_WOHzE.mjs +0 -1
- package/dist/packem_shared/LUNORA_ERROR_CODES-C2s0xWQD.mjs +0 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-V7LYutDl.mjs +0 -31
- package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-C0oHwiJZ.mjs +0 -2
- package/dist/packem_shared/SchemaSnapshotParseError-BRJT7c2k.mjs +0 -18
- package/dist/packem_shared/capabilitiesUsedBy-BC7ldWJI.mjs +0 -1
- package/dist/packem_shared/chain-C9qZxjM_.mjs +0 -1
- package/dist/packem_shared/discoverAuthApiCalls-GPzUN_-_.mjs +0 -1
- package/dist/packem_shared/discoverCrons-C1fKEf4g.mjs +0 -1
- package/dist/packem_shared/discoverHyperdriveCalls-VVJGsgxj.mjs +0 -1
- package/dist/packem_shared/discoverMaskProcedures-D3Tu8Q8Y.mjs +0 -1
- package/dist/packem_shared/discoverR2sqlCalls-D_H1v-UD.mjs +0 -1
- package/dist/packem_shared/discoverSchema-Bdmw34_P.mjs +0 -1
- package/dist/packem_shared/emitShard-DtfjUgjj.mjs +0 -1
- package/dist/packem_shared/index-BMMBKqsT.mjs +0 -1
- package/dist/packem_shared/loadProjectConfig-B0uxUGF-.mjs +0 -1
- package/dist/packem_shared/parse-validator-BqDQLzDZ.mjs +0 -1
- package/dist/packem_shared/project-config-file-CBY6llht.mjs +0 -1
- package/dist/packem_shared/readServiceBindings-4xlUucKw.mjs +0 -1
- package/dist/packem_shared/shared-DnCokNcE.mjs +0 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{LunoraError as
|
|
1
|
+
import{LunoraError as he}from"@lunora/errors";import{serializeSchemaSnapshot as Jt,hashSchemaSnapshot as Yt}from"./SCHEMA_SNAPSHOT_VERSION-C7Um0EnM.mjs";import{isHyperdriveGlobalTable as zt,isD1GlobalTable as Xt}from"./isD1GlobalTable-udvHAAzD.mjs";import{r as Zt}from"./json-data-Chfs2Cz5.mjs";import{r as $,e as er,a as tr,b as rr,c as nr,d as ar,f as sr,g as or,h as ir,i as dr,j as cr,k as lr,l as ur,m as hr,n as pr,o as mr,p as gr,q as fr}from"./shard-runtime-ByYXjCKY.mjs";import{u as re,h as br,s as wr,a as yr,I as vr,c as kr,G as Sr,d as Rr}from"./shared-8vTRkL2-.mjs";import{p as xr}from"./platform-8oj7iXQy.mjs";import{i as Ar}from"./schema-BgM8A3XV.mjs";const u=(n,t)=>`((): (() => ${t}) => { let cached: ${t} | undefined; return () => (cached ??= JSON.parse(${JSON.stringify(JSON.stringify(n))}) as ${t}); })()`,pe=["categories","description","facing","level","remediation","title"],Or=n=>{const t={};return{advisoryRows:n.map(a=>{let r=t[a.name];return r||(r=Object.fromEntries(pe.filter(o=>o in a).map(o=>[o,a[o]])),t[a.name]=r),Object.fromEntries(Object.entries(a).filter(([o,d])=>!pe.includes(o)||JSON.stringify(d)!==JSON.stringify(r[o])))}),advisoryRules:t}},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 de}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
|
+
}`,w=(n="")=>` const writer = this.adminWriter(${n});`,Tr=`${Dr}
|
|
34
34
|
|
|
35
35
|
protected override async runShardDataMigration(args: RunShardMigrationArgs): Promise<MigrationRunResult> {
|
|
36
36
|
this.ensureMigrated();
|
|
@@ -47,21 +47,27 @@ import{LunoraError as de}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
|
-
${
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
${w()}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
return await runDataMigration({
|
|
54
|
+
...(args.batchSize === undefined ? {} : { batchSize: args.batchSize }),
|
|
55
|
+
...(args.direction === undefined ? {} : { direction: args.direction }),
|
|
56
|
+
...(args.dryRun === undefined ? {} : { dryRun: args.dryRun }),
|
|
57
|
+
...(args.maxBatches === undefined ? {} : { maxBatches: args.maxBatches }),
|
|
58
|
+
migration: migration as unknown as DataMigrationLike,
|
|
59
|
+
// Flush after each batch so live migrationStatus subscribers see
|
|
60
|
+
// progress mid-run (the base method records the reserved state
|
|
61
|
+
// table the raw-SQL progress write is invisible to the tracker).
|
|
62
|
+
onBatch: () => this.flushMigrationProgress(),
|
|
63
|
+
sql: this.sql as SqlExec,
|
|
64
|
+
writer,
|
|
65
|
+
});
|
|
66
|
+
} finally {
|
|
67
|
+
// The de-dup that unblocks a deferred UNIQUE index runs through here;
|
|
68
|
+
// let the next call rebuild it now instead of after the retry interval.
|
|
69
|
+
this.migrationRetryAt = 0;
|
|
70
|
+
}
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
protected override async runShardWrite(args: RunShardWriteArgs, headroom?: TransactionHeadroomTracker): Promise<RunShardWriteResult> {
|
|
@@ -80,7 +86,7 @@ ${g()}
|
|
|
80
86
|
|
|
81
87
|
this.ensureMigrated();
|
|
82
88
|
|
|
83
|
-
${
|
|
89
|
+
${w("headroom ?? this.transactionHeadroom()")}
|
|
84
90
|
|
|
85
91
|
if (args.op === "insert") {
|
|
86
92
|
const id = await writer.insert(args.table, args.doc ?? {});
|
|
@@ -135,7 +141,7 @@ ${g("headroom ?? this.transactionHeadroom()")}
|
|
|
135
141
|
protected override async runShardExport(args: RunShardExportArgs): Promise<ExportRow[]> {
|
|
136
142
|
this.ensureMigrated();
|
|
137
143
|
|
|
138
|
-
${
|
|
144
|
+
${w()}
|
|
139
145
|
|
|
140
146
|
const rows: ExportRow[] = [];
|
|
141
147
|
|
|
@@ -152,7 +158,7 @@ ${g()}
|
|
|
152
158
|
protected override async runShardImport(args: RunShardImportArgs): Promise<ImportShardResult> {
|
|
153
159
|
this.ensureMigrated();
|
|
154
160
|
|
|
155
|
-
${
|
|
161
|
+
${w()}
|
|
156
162
|
|
|
157
163
|
// \`importShardRows\` inserts with \`allowExplicitId\`, so a source
|
|
158
164
|
// database's \`_id\`s carry across verbatim and every foreign key
|
|
@@ -163,7 +169,7 @@ ${g()}
|
|
|
163
169
|
protected override async runShardRankBefore(args: RunShardRankBeforeArgs): Promise<{ before: number; total: number }> {
|
|
164
170
|
this.ensureMigrated();
|
|
165
171
|
|
|
166
|
-
${
|
|
172
|
+
${w()}
|
|
167
173
|
|
|
168
174
|
// \`rankBefore\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
169
175
|
// but the shard writer from \`createShardCtxDb\` always defines it.
|
|
@@ -181,7 +187,7 @@ ${g()}
|
|
|
181
187
|
protected override async runShardFindRelated(args: RunShardFindRelatedArgs): Promise<RelatedPage> {
|
|
182
188
|
this.ensureMigrated();
|
|
183
189
|
|
|
184
|
-
${
|
|
190
|
+
${w()}
|
|
185
191
|
|
|
186
192
|
// \`related\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
187
193
|
// but the shard writer from \`createShardCtxDb\` always defines it — it
|
|
@@ -199,7 +205,7 @@ ${g()}
|
|
|
199
205
|
protected override async runShardRankPage(args: RunShardRankPageArgs): Promise<ShardRankPageResult> {
|
|
200
206
|
this.ensureMigrated();
|
|
201
207
|
|
|
202
|
-
${
|
|
208
|
+
${w()}
|
|
203
209
|
|
|
204
210
|
// \`rankPageRows\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
|
|
205
211
|
// but the shard writer from \`createShardCtxDb\` always defines it. The sort
|
|
@@ -220,21 +226,21 @@ ${g()}
|
|
|
220
226
|
protected override async runShardApplyCdc(args: RunShardApplyCdcArgs): Promise<{ applied: number }> {
|
|
221
227
|
this.ensureMigrated();
|
|
222
228
|
|
|
223
|
-
${
|
|
229
|
+
${w()}
|
|
224
230
|
|
|
225
231
|
await applyCdcChanges(writer, args.changes);
|
|
226
232
|
|
|
227
233
|
return { applied: args.changes.length };
|
|
228
|
-
}`,
|
|
229
|
-
orm: databaseContext.orm,`,
|
|
230
|
-
orm: { configurable: true, enumerable: true, value: mutationDatabase.orm, writable: true },`,
|
|
234
|
+
}`,_r=({actionOnlyBuild:n,actionOnlyFields:t,agentsBuild:e,agentsContextField:a,containersBuild:r,containersContextField:o,databaseOptions:d,everyContextBuild:m,everyContextField:g,facadeBlock:h,globalDatabaseLine:x,hasSensitiveArgs:S=!1,lazyFunctions:f=!1,notifyBuild:c,ormContextField:y,paymentsBuild:M,paymentsContextField:A,queuesBuild:q,queuesContextField:F,topicsBuild:R,topicsContextField:U,vectorsBuild:i,vectorsContextField:v,workflowsBuild:E,workflowsContextField:B})=>{const I=t.length>0,C=y===""?"{ db }":"{ db, orm: bindOrm(facade) }",j=y===""?"":`
|
|
235
|
+
orm: databaseContext.orm,`,W=y===""?"":`
|
|
236
|
+
orm: { configurable: true, enumerable: true, value: mutationDatabase.orm, writable: true },`,O=I?`
|
|
231
237
|
// ActionCtx-only helpers (external, non-deterministic I/O): constructed
|
|
232
238
|
// and attached only for an \`action\` so query/mutation ctx never carry them.
|
|
233
239
|
if (isAction) {
|
|
234
|
-
${n.replaceAll(/^(?=.)/gmu," ")}${
|
|
240
|
+
${n.replaceAll(/^(?=.)/gmu," ")}${t.map(p=>` ctx.${p} = ${p};`).join(`
|
|
235
241
|
`)}
|
|
236
242
|
}
|
|
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 {
|
|
243
|
+
`:"";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; sensitive?: SensitiveScope | undefined; trusted?: boolean | undefined } = {}): unknown {
|
|
238
244
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
239
245
|
// The caller context this ctx runs under, resolved ONCE on one
|
|
240
246
|
// discriminant. When the caller threads an explicit identity
|
|
@@ -261,7 +267,7 @@ ${n.replaceAll(/^(?=.)/gmu," ")}${e.map(p=>` ctx.${p} = ${p};`
|
|
|
261
267
|
};
|
|
262
268
|
})();
|
|
263
269
|
const { identity, ip, userId } = caller;
|
|
264
|
-
${
|
|
270
|
+
${i}${m}${r}${E}${q}${R}${e}
|
|
265
271
|
// Which dispatch this ctx belongs to. Drives the two deferral facades
|
|
266
272
|
// below and the \`ctx.run*\` caller guard; a ctx built for an
|
|
267
273
|
// admin/lifecycle path has no registered function, and so no kind. An
|
|
@@ -357,7 +363,7 @@ ${w}${f}${r}${$}${P}${N}${t}
|
|
|
357
363
|
// dispatch's wide-event handle.
|
|
358
364
|
const observability = config.observability?.(env);
|
|
359
365
|
const logFunctionPath = options.functionPath ?? "";
|
|
360
|
-
const log = this.makeLogger(logFunctionPath, observability);
|
|
366
|
+
const log = this.makeLogger(logFunctionPath, observability, undefined, options.sensitive);
|
|
361
367
|
const traceAnchor = this.resolveDispatchAnchor(Boolean(options.identity));
|
|
362
368
|
|
|
363
369
|
// \`ctx.span\`: the handle onto the DISPATCH's own span — the wide-event
|
|
@@ -365,9 +371,9 @@ ${w}${f}${r}${$}${P}${N}${t}
|
|
|
365
371
|
// and are folded into the one span it already emits, instead of
|
|
366
372
|
// becoming N separate log lines. Shares \`traceAnchor\` with \`ctx.trace\`
|
|
367
373
|
// so both write to the same trace.
|
|
368
|
-
const span = this.makeDispatchSpan(traceAnchor, observability);
|
|
374
|
+
const span = this.makeDispatchSpan(traceAnchor, observability, options.sensitive);
|
|
369
375
|
|
|
370
|
-
${
|
|
376
|
+
${x} // \`ctx.db\`, wrapped in automatic instrumentation: by default this
|
|
371
377
|
// adds aggregate counters (call count, total time, per-operation
|
|
372
378
|
// breakdown) to the wide event rather than a span per call, so a
|
|
373
379
|
// handler making hundreds of queries stays readable. See
|
|
@@ -375,20 +381,20 @@ ${b} // \`ctx.db\`, wrapped in automatic instrumentation: by default
|
|
|
375
381
|
// Schema triggers close over this DB's scheduler, so a composed
|
|
376
382
|
// mutation must rebuild its DB and table/ORM views with its own queue.
|
|
377
383
|
const makeDatabaseContext = (scheduler: SchedulerLike) => {
|
|
378
|
-
const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${d}), logFunctionPath, traceAnchor, observability);
|
|
379
|
-
${
|
|
384
|
+
const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${d}), logFunctionPath, traceAnchor, observability, options.sensitive);
|
|
385
|
+
${h} return ${C};
|
|
380
386
|
};
|
|
381
387
|
const databaseContext = makeDatabaseContext(scheduler);
|
|
382
388
|
const db = databaseContext.db;
|
|
383
|
-
${
|
|
389
|
+
${M}
|
|
384
390
|
|
|
385
391
|
// \`ctx.trace\` / \`ctx.metrics\`: spans and measurements to the same sink.
|
|
386
392
|
// The trace anchor is threaded explicitly for the same reason \`identity\`
|
|
387
393
|
// is — a deferred caller (a subscription re-run) must not inherit the
|
|
388
394
|
// writing mutation's trace from the shared per-request field.
|
|
389
|
-
const trace = this.makeTracer(logFunctionPath, observability, traceAnchor);
|
|
390
|
-
const metrics = this.makeMetrics(logFunctionPath, observability);
|
|
391
|
-
${
|
|
395
|
+
const trace = this.makeTracer(logFunctionPath, observability, traceAnchor, options.sensitive);
|
|
396
|
+
const metrics = this.makeMetrics(logFunctionPath, observability, options.sensitive);
|
|
397
|
+
${c}
|
|
392
398
|
// \`ctx.now\`: the wall-clock instant (epoch ms) this function began,
|
|
393
399
|
// captured ONCE so the whole handler body sees a single stable value.
|
|
394
400
|
// A \`query\` handler is re-run by every live subscription that reads it,
|
|
@@ -418,7 +424,7 @@ ${L}
|
|
|
418
424
|
// \`traceparent\` propagation, so time spent in a downstream service
|
|
419
425
|
// is visible and its spans join this trace. Degrades to the bare
|
|
420
426
|
// global when no sink is configured.
|
|
421
|
-
fetch: this.makeFetch(logFunctionPath, traceAnchor, observability),
|
|
427
|
+
fetch: this.makeFetch(logFunctionPath, traceAnchor, observability, options.sensitive),
|
|
422
428
|
// A GETTER, not a plain field, so the reactive cache can tell a
|
|
423
429
|
// handler that reads the caller's address from the majority that
|
|
424
430
|
// never do. \`ctx.ip\` is per-request state the memo must be keyed
|
|
@@ -439,25 +445,32 @@ ${L}
|
|
|
439
445
|
// to the handler so code that signs its own URLs (a copy-in
|
|
440
446
|
// \`createStorage\`) can fall back to it too. \`undefined\` for a
|
|
441
447
|
// query and for a deferred dispatch, for the reasons given there.
|
|
442
|
-
origin: requestOrigin,${
|
|
448
|
+
origin: requestOrigin,${j}
|
|
443
449
|
scheduler,
|
|
444
450
|
span,
|
|
445
451
|
storage: contextStorage,
|
|
446
|
-
trace,${
|
|
452
|
+
trace,${v}${g}${A}${o}${B}${F}${U}${a}${S?`
|
|
453
|
+
[Symbol.for("lunora.admitSensitiveArgs")]: (args: Record<string, unknown>, names: ReadonlySet<string>): void => this.admitSensitiveValues(options.sensitive, args, names),`:""}
|
|
447
454
|
};
|
|
448
|
-
${
|
|
449
|
-
`:""}${
|
|
450
|
-
${
|
|
455
|
+
${I?` const isAction = contextKind === "action";
|
|
456
|
+
`:""}${O}
|
|
457
|
+
${f?` const captureScope = (): (() => void) => {
|
|
451
458
|
const requestScope = this.captureRequestScope();
|
|
452
459
|
return () => this.restoreRequestScope(requestScope);
|
|
453
460
|
};
|
|
454
461
|
|
|
455
462
|
`:""} const installRun = (target: Record<string, unknown>, kind: typeof contextKind): void => {
|
|
456
|
-
target.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) =>
|
|
463
|
+
target.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => {
|
|
464
|
+
// The callee runs inside this invocation, so its marked arguments are scrubbed here too.
|
|
465
|
+
this.admitSensitiveArgs(options.sensitive, reference.__lunoraRef, fnArgs);
|
|
466
|
+
|
|
467
|
+
return dispatchRun("action", reference.__lunoraRef, fnArgs, target, kind${f?", undefined, captureScope":""});
|
|
468
|
+
};
|
|
457
469
|
// The composed mutation runs under the SAME wrapper the top-level RPC
|
|
458
470
|
// uses, so "do the transactional work in a mutation and call it from the
|
|
459
471
|
// action" — the recipe the docs give for atomicity — actually is atomic.
|
|
460
472
|
target.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) => {
|
|
473
|
+
this.admitSensitiveArgs(options.sensitive, reference.__lunoraRef, fnArgs);
|
|
461
474
|
// An action can start sibling mutations concurrently. Each owns its
|
|
462
475
|
// context and deferral queues; only calls from an admitted mutation
|
|
463
476
|
// inherit that mutation's transaction.
|
|
@@ -469,7 +482,7 @@ ${c?` const captureScope = (): (() => void) => {
|
|
|
469
482
|
|
|
470
483
|
return Object.defineProperties({}, {
|
|
471
484
|
...Object.getOwnPropertyDescriptors(target),
|
|
472
|
-
db: { configurable: true, enumerable: true, value: mutationDatabase.db, writable: true },${
|
|
485
|
+
db: { configurable: true, enumerable: true, value: mutationDatabase.db, writable: true },${W}
|
|
473
486
|
scheduler: { configurable: true, enumerable: true, value: mutationScheduler, writable: true },
|
|
474
487
|
storage: { configurable: true, enumerable: true, value: withDeferredDeletes(storage), writable: true },
|
|
475
488
|
});
|
|
@@ -479,7 +492,7 @@ ${c?` const captureScope = (): (() => void) => {
|
|
|
479
492
|
installRun(mutationCtx, kind);
|
|
480
493
|
}
|
|
481
494
|
|
|
482
|
-
return dispatchRun("mutation", reference.__lunoraRef, fnArgs, mutationCtx, kind, async (work) => this.runMutationTransaction(mutationCtx, work)${
|
|
495
|
+
return dispatchRun("mutation", reference.__lunoraRef, fnArgs, mutationCtx, kind, async (work) => this.runMutationTransaction(mutationCtx, work)${f?", captureScope":""});
|
|
483
496
|
};
|
|
484
497
|
// \`ctx.runQuery(ref, args, { untracked: true })\` runs the sub-query on
|
|
485
498
|
// its OWN query context, built without the subscription's read-footprint
|
|
@@ -494,18 +507,21 @@ ${c?` const captureScope = (): (() => void) => {
|
|
|
494
507
|
// to the shared per-request fields, which a concurrent RPC may have
|
|
495
508
|
// re-set, and an RLS-scoped sub-query would then read as the wrong user
|
|
496
509
|
// from the wrong address. A tracked call runs on \`queryContext()\` below.
|
|
497
|
-
target.runQuery = (reference: FunctionReference, fnArgs: Record<string, unknown>, runOptions?: { untracked?: boolean }) =>
|
|
498
|
-
|
|
510
|
+
target.runQuery = (reference: FunctionReference, fnArgs: Record<string, unknown>, runOptions?: { untracked?: boolean }) => {
|
|
511
|
+
this.admitSensitiveArgs(options.sensitive, reference.__lunoraRef, fnArgs);
|
|
512
|
+
|
|
513
|
+
return dispatchRun(
|
|
499
514
|
"query",
|
|
500
515
|
reference.__lunoraRef,
|
|
501
516
|
fnArgs,
|
|
502
517
|
runOptions?.untracked === true
|
|
503
|
-
? this.buildCtx({ bookmarks: options.bookmarks, functionPath: options.functionPath, headroom: options.headroom, identity: caller, kind: "query", scope: options.scope })
|
|
518
|
+
? this.buildCtx({ bookmarks: options.bookmarks, functionPath: options.functionPath, headroom: options.headroom, identity: caller, kind: "query", scope: options.scope, sensitive: options.sensitive })
|
|
504
519
|
: queryContext(),
|
|
505
|
-
kind,${
|
|
520
|
+
kind,${f?`
|
|
506
521
|
undefined,
|
|
507
522
|
captureScope,`:""}
|
|
508
523
|
);
|
|
524
|
+
};
|
|
509
525
|
};
|
|
510
526
|
|
|
511
527
|
// A query composed from a mutation or action runs on a QUERY view of
|
|
@@ -526,7 +542,7 @@ ${c?` const captureScope = (): (() => void) => {
|
|
|
526
542
|
|
|
527
543
|
if (queryView === undefined) {
|
|
528
544
|
const descriptors: PropertyDescriptorMap = Object.getOwnPropertyDescriptors(ctx);
|
|
529
|
-
${
|
|
545
|
+
${t.map(p=>` delete descriptors.${p};
|
|
530
546
|
`).join("")} // Writable and configurable, like the plain fields they replace.
|
|
531
547
|
const field = (value: unknown): PropertyDescriptor => ({ configurable: true, enumerable: true, value, writable: true });
|
|
532
548
|
|
|
@@ -545,7 +561,7 @@ ${e.map(p=>` delete descriptors.${p};
|
|
|
545
561
|
installRun(ctx, contextKind);
|
|
546
562
|
|
|
547
563
|
return ctx;
|
|
548
|
-
}`},
|
|
564
|
+
}`},me=`// Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
|
|
549
565
|
// referencing call fails loudly with a clear error instead of overflowing the
|
|
550
566
|
// stack. Tracked across the awaited handler chain (one DO invocation is
|
|
551
567
|
// single-threaded), so the counter reflects true nesting depth.
|
|
@@ -611,7 +627,7 @@ const dispatchRun = async (
|
|
|
611
627
|
} finally {
|
|
612
628
|
runDepth -= 1;
|
|
613
629
|
}
|
|
614
|
-
};`,
|
|
630
|
+
};`,Er=n=>n?me.replace(" runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,",` runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,
|
|
615
631
|
captureScope?: () => () => void,`).replace(` try {
|
|
616
632
|
// A mutation composed from an action`,` try {
|
|
617
633
|
let loaded = getLoadedFunction(functionPath);
|
|
@@ -632,7 +648,20 @@ const dispatchRun = async (
|
|
|
632
648
|
|
|
633
649
|
const loadedFunction = loaded;
|
|
634
650
|
|
|
635
|
-
// A mutation composed from an action`).replaceAll("registered.handler","loadedFunction.handler"):
|
|
651
|
+
// A mutation composed from an action`).replaceAll("registered.handler","loadedFunction.handler"):me,Ir=` public override async handleRpc(functionPath: string, args: Record<string, unknown>, headroom?: TransactionHeadroomTracker, scope?: QueryReadScope, bookmarks?: DispatchBookmark, sensitive?: SensitiveScope): Promise<unknown> {
|
|
652
|
+
// A dispatch that did not come through the shard's \`/rpc\` (a scheduled call,
|
|
653
|
+
// a lifecycle hook, an admin run) has no scope of its own, so it opens one here
|
|
654
|
+
// and releases it when the handler settles.
|
|
655
|
+
if (sensitive === undefined) {
|
|
656
|
+
const own = this.openSensitiveScope(functionPath, args);
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
return await this.handleRpc(functionPath, args, headroom, scope, bookmarks, own);
|
|
660
|
+
} finally {
|
|
661
|
+
own.clear();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
636
665
|
__PREPARE_FUNCTION__ const registered = LUNORA_FUNCTIONS[functionPath];
|
|
637
666
|
|
|
638
667
|
// Internal functions are reachable server-side only: via \`ctx.run*\`
|
|
@@ -643,7 +672,7 @@ __PREPARE_FUNCTION__ const registered = LUNORA_FUNCTIONS[functionPath
|
|
|
643
672
|
throw new LunoraError("FUNCTION_NOT_FOUND", \`function not registered: \${functionPath}\`);
|
|
644
673
|
}
|
|
645
674
|
|
|
646
|
-
this.
|
|
675
|
+
this.ensureServable();
|
|
647
676
|
|
|
648
677
|
// \`headroom\`, when the caller supplied one, is threaded straight
|
|
649
678
|
// through to \`buildCtx\` — bypassing its \`this.transactionHeadroom()\`
|
|
@@ -659,7 +688,7 @@ __PREPARE_FUNCTION__ const registered = LUNORA_FUNCTIONS[functionPath
|
|
|
659
688
|
// our dep set — and ours out of theirs. A dispatch with no scope (a
|
|
660
689
|
// mutation, an action, a cache-less shard) builds unbound hooks that
|
|
661
690
|
// stamp no deps.
|
|
662
|
-
const ctx = this.buildCtx({ bookmarks, functionPath, headroom, scope, trusted: registered.lifecycle === "init" });
|
|
691
|
+
const ctx = this.buildCtx({ bookmarks, functionPath, headroom, scope, sensitive, trusted: registered.lifecycle === "init" });
|
|
663
692
|
|
|
664
693
|
// A mutation's writes must commit all-or-nothing, so its dispatch runs
|
|
665
694
|
// under \`runMutationTransaction\` — the ONE place that opens the span,
|
|
@@ -816,14 +845,14 @@ __COMMITTED_CATCH__ } finally {
|
|
|
816
845
|
// \`ctx.scheduler\` call that reached the buffer in the first place.
|
|
817
846
|
protected override scheduleOutboxScheduler(): SchedulerLike | undefined {
|
|
818
847
|
return config.scheduler?.((this.env ?? {}) as Record<string, unknown>) as SchedulerLike | undefined;
|
|
819
|
-
}`,
|
|
848
|
+
}`,Cr=` protected override async executeSubscription(functionPath: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): Promise<{ ranges?: Map<string, KeyRange[]>; result: unknown; tables: Set<string> } | null> {
|
|
820
849
|
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
821
850
|
|
|
822
851
|
if (!registered || registered.kind !== "query" || registered.visibility === "internal") {
|
|
823
852
|
return null;
|
|
824
853
|
}
|
|
825
854
|
|
|
826
|
-
__PREPARE_FUNCTION__ this.
|
|
855
|
+
__PREPARE_FUNCTION__ this.ensureServable();
|
|
827
856
|
|
|
828
857
|
const footprint = createReadFootprint();
|
|
829
858
|
// Identity is threaded EXPLICITLY from the (deferred/interleaved)
|
|
@@ -846,7 +875,7 @@ __PREPARE_FUNCTION__ this.ensureMigrated();
|
|
|
846
875
|
return null;
|
|
847
876
|
}
|
|
848
877
|
|
|
849
|
-
this.
|
|
878
|
+
this.ensureServable();
|
|
850
879
|
|
|
851
880
|
return {
|
|
852
881
|
...(registered.durable ? { durable: registered.durable as { ttlMs?: number } } : {}),
|
|
@@ -857,7 +886,7 @@ __PREPARE_FUNCTION__ this.ensureMigrated();
|
|
|
857
886
|
// \`ctx.auth\` stream as nobody — or as a concurrent RPC's caller.
|
|
858
887
|
iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath, identity }), args, signal),
|
|
859
888
|
};
|
|
860
|
-
}`,
|
|
889
|
+
}`,Lr=` } catch (error) {
|
|
861
890
|
// The write committed before this failed. Mark the error so the caller can tell
|
|
862
891
|
// a durable write from a rollback: the rethrow still reaches the caller unchanged.
|
|
863
892
|
if (typeof error === "object" && error !== null) {
|
|
@@ -865,13 +894,13 @@ __PREPARE_FUNCTION__ this.ensureMigrated();
|
|
|
865
894
|
}
|
|
866
895
|
|
|
867
896
|
throw error;
|
|
868
|
-
`,
|
|
897
|
+
`,Pr=(n,t=!1)=>{const e=t?` const registeredBeforeLoad = LUNORA_FUNCTIONS[functionPath];
|
|
869
898
|
|
|
870
899
|
if (registeredBeforeLoad?.visibility === "internal" && !this.isSystemDispatch()) {
|
|
871
900
|
throw new LunoraError("FUNCTION_NOT_FOUND", "function not registered: " + functionPath);
|
|
872
901
|
}
|
|
873
902
|
|
|
874
|
-
`:"",a=
|
|
903
|
+
`:"",a=t?` const requestScope = this.captureRequestScope();
|
|
875
904
|
const preparation = this.prepareFunction(functionPath, "rpc");
|
|
876
905
|
|
|
877
906
|
if (preparation !== undefined) {
|
|
@@ -882,21 +911,33 @@ __PREPARE_FUNCTION__ this.ensureMigrated();
|
|
|
882
911
|
}
|
|
883
912
|
}
|
|
884
913
|
|
|
885
|
-
`:"";return
|
|
886
|
-
|
|
914
|
+
`:"";return Ir.replace("__COMMITTED_CATCH__",n?Lr:"").replace("__PREPARE_FUNCTION__",`${e}${a}`)},Nr=n=>{const t={};for(const e of n.tables){const a={};for(const[r,o]of Object.entries(e.shape)){const d=re(o);d.kind==="id"&&d.tableName!==void 0&&(a[r]=d.tableName)}Object.keys(a).length>0&&(t[e.name]=a)}return t},$r=n=>{const t={};for(const e of n.tables){const a=[];for(const[r,o]of Object.entries(e.shape))re(o).kind==="storage"&&a.push(r);a.length>0&&(t[e.name]=a)}return t},Mr=n=>{const t={};for(const e of n.tables){const a=[...e.indexes.map(r=>({fields:[...r.fields],name:r.name,type:"index",...r.unique===!0?{unique:!0}:{}})),...e.searchIndexes.map(r=>({fields:[r.field,...r.filterFields??[]],name:r.name,type:"search"})),...(e.geoIndexes??[]).map(r=>({fields:[r.field],name:r.name,type:"geo"})),...e.rankIndexes.map(r=>({fields:r.sortBy.map(o=>o.field),name:r.name,type:"rank"})),...e.vectorIndexes.map(r=>({fields:r.field===void 0?[]:[r.field],name:r.name,type:"vector"}))];a.length>0&&(t[e.name]=a)}return t},qr=n=>{const t=[];for(const e of n.tables)!e.ttl||e.shardMode==="global"||t.push({...e.ttl.after===void 0?{}:{after:e.ttl.after},field:e.ttl.field,...e.softDelete?{softDeleteField:e.softDelete.field}:{},table:e.name});return t},Fr=n=>{if(n.kind!=="union"||n.members===void 0||n.members.length===0)return;const t=[];for(const e of n.members){if(e.kind!=="literal"||e.literalValue===void 0)return;try{const a=JSON.parse(e.literalValue);if(typeof a!="string")return;t.push(a)}catch{return}}return t},Ur=(n,t,e)=>{const a=re(t),r={name:n,optional:br(t),type:a.kind};if(a.kind==="id"&&a.tableName!==void 0){r.ref=a.tableName;const d=e.get(n);d!==void 0&&(r.onDelete=d)}a.kind==="storage"&&(r.isStorage=!0,a.bucket!==void 0&&(r.bucket=a.bucket));const o=Fr(a);return o!==void 0&&(r.enumValues=o),a.column?.notNull===!1&&(r.nullable=!0),r},Br=n=>{const t={};for(const e of n.tables){const a=[{name:"_id",optional:!1,pk:!0,type:"id"},{name:"_creationTime",optional:!1,type:"number"},...e.commitOrdered===!0?[{name:"_commitSeq",optional:!1,type:"number"}]:[]],r=new Map(e.relations.flatMap(o=>o.kind==="one"?[[o.field,o.onDelete]]:[]));for(const[o,d]of Object.entries(e.shape))a.push(Ur(o,d,r));t[e.name]=a}return t},jr=({hasMutators:n,hasSensitiveShapes:t,hasShapes:e,rlsData:a})=>{const r="{ columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string } | undefined",o=t?` protected override resolveShape(name: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): ${r} {
|
|
915
|
+
const sensitive = this.openSensitiveScope(\`__shape__:\${name}\`, args);
|
|
916
|
+
|
|
917
|
+
try {
|
|
918
|
+
return this.resolveShapeWithScope(name, args, identity, sensitive);
|
|
919
|
+
} catch (error) {
|
|
920
|
+
throw this.maskSensitiveError(error, sensitive);
|
|
921
|
+
} finally {
|
|
922
|
+
sensitive.clear();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
private resolveShapeWithScope(name: string, args: Record<string, unknown>, identity: SubscriptionIdentity | undefined, sensitive: SensitiveScope): ${r} {`:` protected override resolveShape(name: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): ${r} {`,m=e?`
|
|
927
|
+
${o}
|
|
887
928
|
const shape = LUNORA_SHAPES[name];
|
|
888
929
|
|
|
889
930
|
if (!shape) {
|
|
890
931
|
return undefined;
|
|
891
932
|
}
|
|
892
933
|
|
|
893
|
-
this.
|
|
934
|
+
this.ensureServable();
|
|
894
935
|
|
|
895
936
|
// Trusted ctx from the socket's OWN verified identity — the client
|
|
896
937
|
// supplies only the shape name + args; \`compileWhere\` validates the
|
|
897
938
|
// args then runs the shape's \`where(ctx, args)\` under that identity,
|
|
898
939
|
// so which rows replicate is a server decision (reads-as-permissions).
|
|
899
|
-
const ctx = this.buildCtx({ functionPath: \`__shape__:\${name}\`, identity });
|
|
940
|
+
const ctx = this.buildCtx({ functionPath: \`__shape__:\${name}\`, identity${t?", sensitive":""} });
|
|
900
941
|
// \`ownerField\` resolves an \`owner: true\` shape: only here is the table's
|
|
901
942
|
// \`.ownedBy(field)\` column in scope, so the DO hands it to the shape
|
|
902
943
|
// rather than every shape restating the ownership check in its \`where\`.
|
|
@@ -945,14 +986,14 @@ __PREPARE_FUNCTION__ this.ensureMigrated();
|
|
|
945
986
|
|
|
946
987
|
return { ...(shape.columns === undefined ? {} : { columns: shape.columns }), effectiveWhere, global: isGlobal, table: shape.table };
|
|
947
988
|
}
|
|
948
|
-
`:"",
|
|
989
|
+
`:"",g=e?[...new Set(a.policies.filter(c=>c.on==="read"&&c.table!=="").map(c=>c.table))].toSorted((c,y)=>c.localeCompare(y)):[],h=g.length>0?`
|
|
949
990
|
/** 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). */
|
|
950
|
-
assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(
|
|
991
|
+
assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(g)}, (schema as unknown as { rlsMode?: string }).rlsMode === "required");
|
|
951
992
|
`:"";return{customMutatorOverride:n?`
|
|
952
993
|
protected override isCustomMutator(functionPath: string): boolean {
|
|
953
994
|
return LUNORA_MUTATOR_PATHS.has(functionPath);
|
|
954
995
|
}
|
|
955
|
-
`:"",shapeGuardImport:e?"assertShapeShardable, ":"",shapeReadPolicyAssertion:
|
|
996
|
+
`:"",shapeGuardImport:e?"assertShapeShardable, ":"",shapeReadPolicyAssertion:h,shapeReadPolicyImport:h===""?"":"assertShapesDeclareReadPolicies, ",shapeResolveOverride:m}},Wr=(n,t)=>n?`
|
|
956
997
|
protected override async readGlobalShapeRows(resolved: { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string }, identity?: SubscriptionIdentity): Promise<Array<{ doc: Record<string, unknown>; id: string }>> {
|
|
957
998
|
const env = this.env as Record<string, unknown>;
|
|
958
999
|
// A shape read performs no writes, so the widened request only needs
|
|
@@ -972,7 +1013,7 @@ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as
|
|
|
972
1013
|
...(identity?.identity === undefined ? {} : { identity: identity.identity }),
|
|
973
1014
|
...(identity?.userId === undefined ? {} : { userId: identity.userId }),
|
|
974
1015
|
};
|
|
975
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1016
|
+
const globalDb: DatabaseWriterLike = ${t}?.(env, globalRequest) ?? globalDbStub;
|
|
976
1017
|
const rows: Array<{ doc: Record<string, unknown>; id: string }> = [];
|
|
977
1018
|
|
|
978
1019
|
let cursor: null | string = null;
|
|
@@ -1014,11 +1055,11 @@ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as
|
|
|
1014
1055
|
// a \`defineShape\`. The Hyperdrive factory simply never reads it.
|
|
1015
1056
|
const bookmark = this.getInboundBookmark();
|
|
1016
1057
|
const globalRequest = { ...this.globalCdcOptions(config.cdc ?? false), ...(bookmark === undefined ? {} : { bookmark }) };
|
|
1017
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1058
|
+
const globalDb: DatabaseWriterLike = ${t}?.(env, globalRequest) ?? globalDbStub;
|
|
1018
1059
|
|
|
1019
1060
|
return globalDb.cdcChangedTables?.(sinceSeq, cursorOnly === undefined ? {} : { cursorOnly });
|
|
1020
1061
|
}
|
|
1021
|
-
`:"",
|
|
1062
|
+
`:"",Hr=n=>({externalSourceOverride:n?`
|
|
1022
1063
|
protected override async pollExternalSources(trace?: TraceRefLike): Promise<number | undefined> {
|
|
1023
1064
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
1024
1065
|
const sourced = Object.entries((schema as unknown as SchemaLike).tables)
|
|
@@ -1171,8 +1212,8 @@ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(r)}, (schema as
|
|
|
1171
1212
|
`:"",sourceClientCacheConst:n?`
|
|
1172
1213
|
const sourceClientCache = new WeakMap<object, Map<string, SourceClientLike>>();
|
|
1173
1214
|
const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
1174
|
-
`:""}),
|
|
1175
|
-
`:"",d=
|
|
1215
|
+
`:""}),Vr=(n,t)=>{const e=t.length>0,r=n?$("vectorsStub: VectorSearchLike","ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().",["deleteByIds","getByIds","query","upsert","upsertMany","upsertNow"]):"",o=e?` const vectorShardKey = this.currentShardKey();
|
|
1216
|
+
`:"",d=e?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",g=`, { ${["deferAfterCommit: (work) => this.deferAfterCommit(work)",...e?["namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey",`shardedIndexNames: [${t.map(S=>JSON.stringify(S)).join(", ")}]`]:[]].join(", ")} }`,h=n?`
|
|
1176
1217
|
const vectorSync = this.vectorSync(env);
|
|
1177
1218
|
// Vectorize lives outside this shard's SQLite, so a query whose result
|
|
1178
1219
|
// depends on a similarity search cannot be proven current on reconnect —
|
|
@@ -1189,7 +1230,7 @@ const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
|
1189
1230
|
}
|
|
1190
1231
|
|
|
1191
1232
|
const lunora = createVectors({ indexes: config.vectors(env) });
|
|
1192
|
-
${o} const vectors = createContextVectors(lunora${
|
|
1233
|
+
${o} const vectors = createContextVectors(lunora${g});
|
|
1193
1234
|
|
|
1194
1235
|
return {
|
|
1195
1236
|
backfill: createVectorBackfillSync({ ${d}schema: schema as unknown as VectorSchemaLike, upsertMany: lunora.upsertMany, vectors }),
|
|
@@ -1215,14 +1256,14 @@ ${o} const vectors = createContextVectors(lunora${v});
|
|
|
1215
1256
|
ordered: async (read, work) => this.runOrderedAfterWrites(read, work),
|
|
1216
1257
|
});
|
|
1217
1258
|
}
|
|
1218
|
-
`:"",vectorsBuild:
|
|
1259
|
+
`:"",vectorsBuild:h,vectorsStub:r}},on=({advisories:n=[],advisorProcedures:t=[],agents:e=[],capabilities:a=new Set,containers:r=[],env:o,flagKeys:d=[],hasFlags:m=!1,hasNotify:g=!1,hasRooms:h=!1,hasSecretValues:x=!1,hasSensitiveArgs:S=!1,hasSockets:f=!1,lazyFunctions:c=!1,hasVectors:y=!0,liveStreamDeclarations:M=[],liveStreams:A=!1,maskMetadata:q,mutators:F=[],queues:R=[],rlsMetadata:U,schema:i,schemaSnapshot:v,shapes:E=[],storageRules:B,studioFeatures:I,services:C=[],topics:j=[],useUmbrella:W=!1,workflows:O=[]})=>{const l=Rr(W),H=F.length>0,p=E.length>0,D=E.some(s=>wr(s.args).length>0),V=S||D,ge=v===void 0?"":Jt(v),fe=v===void 0?"":`
|
|
1219
1260
|
/** 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. */
|
|
1220
|
-
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(
|
|
1221
|
-
`,
|
|
1222
|
-
return { ${
|
|
1261
|
+
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(Yt(v))}, json: ${JSON.stringify(ge)} };
|
|
1262
|
+
`,be=v===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",k=er(a),L=tr(m,l.flags),K=rr(d,m,l.flags),G=fr(g),Q=nr(o),{build:we,contextField:ye,importLines:ve,specs:ke}=ar(xr(R)),{build:Se,contextField:Re,importLines:xe,specs:Ae}=sr(j,R),J=or(C,l.server),{build:Oe,contextField:De,importLines:Te,specs:_e}=ir(r,i.jurisdiction),{build:Ee,contextField:Ie,importLines:Ce,specs:Le}=dr(O),{build:Pe,contextField:Ne,importLines:$e,specs:Me}=cr(e,x&&e.length>0),{build:qe,configField:Fe,contextField:Ue,imports:Be,stub:je}=lr(a.has("payments")),We=n,{advisoryRules:He,advisoryRows:Ve}=Or(We),Ke=t,ne=U??{policies:[],roles:[]},Ge=q??{columns:[]},Qe=B??{rules:[]},Je=I??{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:Ye,override:ze}=ur(O),{constant:Xe,override:Ze}=hr(R),b=y&&i.vectorIndexes.length>0,Y=new Set(i.tables.filter(s=>Ar(s)).map(s=>s.name)),ae=b&&i.vectorIndexes.some(s=>Y.has(s.table)),T=i.tables.some(s=>s.shardMode==="global"),z=pr(Y),X=i.tables.some(s=>zt(s)),se=i.tables.some(s=>Xt(s)),P=i.tables.some(s=>s.externalSource!==void 0),oe=i.tables.some(s=>s.memory===!0),Z=i.tables.some(s=>s.shardMode!=="global"&&s.searchIndexes.length>0);if(se&&X)throw new he("INTERNAL",'lunora codegen: mixing `.global()` (D1) and `.global({ backend: "hyperdrive" })` tables in one app is not supported yet — use a single global backend.');const ee=i.tables.length>0,et=Nr(i),tt=Mr(i),rt=Br(i),nt=$r(i),ie=qr(i),te=mr(b,O.length>0,R.length>0,m,Z);p&&te.push("WhereInput"),D&&te.push("SensitiveScope");const de=gr(T),{customMutatorOverride:at,shapeGuardImport:st,shapeReadPolicyAssertion:ot,shapeReadPolicyImport:it,shapeResolveOverride:dt}=jr({hasMutators:H,hasSensitiveShapes:D,hasShapes:p,rlsData:ne}),_=A?M:[];for(const s of _)if(yr(s.exportName,"live stream export name"),!vr.test(s.filePath))throw new he("INTERNAL",`@lunora/codegen: refusing to emit import for unsafe live stream file path: ${JSON.stringify(s.filePath)}`);const ct=c?[]:_.map(s=>`import { ${s.exportName} as liveStream_${s.exportName} } from "../${s.filePath}.js";`),lt=` protected override liveStreams() {
|
|
1263
|
+
return { ${_.map(s=>`${kr(s.id)}: liveStream_${s.exportName}`).join(", ")} };
|
|
1223
1264
|
}
|
|
1224
|
-
`,
|
|
1225
|
-
${
|
|
1265
|
+
`,ut=` private readonly liveStreamLoaders = new Map<string, () => Promise<{ validatePublish: (topic: string, payload: unknown) => unknown }>>([
|
|
1266
|
+
${_.map(s=>` [${JSON.stringify(s.id)}, () => import("../${s.filePath}.js").then((module) => module.${s.exportName})],`).join(`
|
|
1226
1267
|
`)}
|
|
1227
1268
|
]);
|
|
1228
1269
|
|
|
@@ -1243,11 +1284,11 @@ ${D.map(s=>` [${JSON.stringify(s.id)}, () => import("../${s.filePath}
|
|
|
1243
1284
|
protected override liveStreams() {
|
|
1244
1285
|
return Object.fromEntries(this.loadedLiveStreams);
|
|
1245
1286
|
}
|
|
1246
|
-
`;let
|
|
1247
|
-
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",
|
|
1248
|
-
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;`:"",
|
|
1249
|
-
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",
|
|
1250
|
-
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",
|
|
1287
|
+
`;let ce="";_.length>0&&(ce=c?ut:lt);const N=[`import type { ${te.join(", ")} } from "${l.do}";`,`import { applyCdcChanges, createStableIdFactory, ${Z?"backfillSearchIndexes, ":""}${b?"backfillVectorIndexes, ":""}buildReprojectionMigration, ${oe?"clearMemoryTables, ":""}${st}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${P?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}markUnvouchableReads, ${ae?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${de.importFragment}ShardDO as ShardDOBase } from "${l.do}";`,...P?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${l.do}";`]:[],p?`import { asBucketStorage, ${it}beginDeferredDeletes, beginDeferredSchedules, composeShapeReadWhere, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${l.server}";`:`import { asBucketStorage, beginDeferredDeletes, beginDeferredSchedules, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${l.server}";`];ee&&N.push(`import { bindOrm, bindTableFacade } from "${l.server}";`),b&&N.push('import type { SchemaLike as VectorSchemaLike, VectorBackfillSync, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectorBackfillSync, createVectors, createVectorSyncHook, vectorBackfillTargets } from "@lunora/bindings/vectors";'),N.push(...k.importLines,...L.importLines,...G.importLines,...Q.importLines,...Te,...Ce,...ve,...xe,...J.importLines,...$e,...Be,...V?[`import { sensitiveArgumentNames } from "${l.values}";`]:[],"",'import schema from "../schema.js";',...ct,`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...c?["getLoadedFunction","loadFunction"]:[],...H?["LUNORA_MUTATOR_PATHS"]:[],...h?["LUNORA_ROOMS"]:[],...f?["LUNORA_SOCKETS"]:[],...p?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const ht=b?`
|
|
1288
|
+
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",pt=se?`
|
|
1289
|
+
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;`:"",mt=X?`
|
|
1290
|
+
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",gt=P?`
|
|
1291
|
+
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",ft="ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().",bt="ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().",wt="ctx.storage: no storage configured. Pass `storage` to createShardDO().",yt=T?$("globalDbStub: DatabaseWriterLike",ft,["aggregate","count","delete","explain","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",{vectorSyncMethod:vt,vectorsBuild:kt,vectorsStub:St}=Vr(b,ae?i.vectorIndexes.filter(s=>Y.has(s.table)).map(s=>s.name):[]),Rt=`{
|
|
1251
1292
|
...this.ctxDbTuning(),
|
|
1252
1293
|
auth: { identity: identity ?? null, userId: userId ?? null },
|
|
1253
1294
|
broadcast: (delta) => {
|
|
@@ -1287,7 +1328,7 @@ ${D.map(s=>` [${JSON.stringify(s.id)}, () => import("../${s.filePath}
|
|
|
1287
1328
|
// fallback rather than reporting into a no-op.
|
|
1288
1329
|
...(options.onReadRange !== undefined
|
|
1289
1330
|
? { onReadRange: options.onReadRange }
|
|
1290
|
-
: options.scope === undefined ? {} : { onReadRange: this.getCtxDbReadRangeHook(options.scope) }),${
|
|
1331
|
+
: options.scope === undefined ? {} : { onReadRange: this.getCtxDbReadRangeHook(options.scope) }),${b?`
|
|
1291
1332
|
// Vectorize is outside this shard's SQLite and cannot roll back, so
|
|
1292
1333
|
// the sync is held until the mutation's transaction has COMMITTED
|
|
1293
1334
|
// (\`deferAfterCommit\`) instead of running inline. Inline, a write
|
|
@@ -1299,11 +1340,11 @@ ${D.map(s=>` [${JSON.stringify(s.id)}, () => import("../${s.filePath}
|
|
|
1299
1340
|
scheduler,
|
|
1300
1341
|
schema: schema as unknown as SchemaLike,
|
|
1301
1342
|
sql: this.sql as SqlExec,
|
|
1302
|
-
storage,${
|
|
1343
|
+
storage,${T?`
|
|
1303
1344
|
globalDb,`:""}
|
|
1304
|
-
}`,
|
|
1305
|
-
vectors,`:"",
|
|
1306
|
-
orm: bindOrm(facade),`:"",
|
|
1345
|
+
}`,xt=b?`
|
|
1346
|
+
vectors,`:"",At=ee?`
|
|
1347
|
+
orm: bindOrm(facade),`:"",le=X?"config.hyperdriveGlobal":"config.d1",Ot=T?` const bookmark = this.getInboundBookmark();
|
|
1307
1348
|
const globalRequest = {
|
|
1308
1349
|
...this.globalCdcOptions(config.cdc ?? false),
|
|
1309
1350
|
...(bookmark === undefined ? {} : { bookmark }),
|
|
@@ -1311,26 +1352,65 @@ ${D.map(s=>` [${JSON.stringify(s.id)}, () => import("../${s.filePath}
|
|
|
1311
1352
|
onBookmark: (bookmarkValue: string | undefined) => { if (options.bookmarks !== undefined) { options.bookmarks.value = bookmarkValue; } },
|
|
1312
1353
|
...(userId === undefined ? {} : { userId }),
|
|
1313
1354
|
};
|
|
1314
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1315
|
-
`:"",
|
|
1355
|
+
const globalDb: DatabaseWriterLike = ${le}?.(env, globalRequest) ?? globalDbStub;
|
|
1356
|
+
`:"",Dt=Wr(p&&T,le),Tt=h?` protected override roomHandlerPath(topic: string): string | undefined {
|
|
1316
1357
|
// Own properties only: a plain object would resolve \`toString\` and the like to inherited functions.
|
|
1317
1358
|
return Object.hasOwn(LUNORA_ROOMS, topic) ? LUNORA_ROOMS[topic] : undefined;
|
|
1318
1359
|
}
|
|
1319
|
-
`:"",
|
|
1360
|
+
`:"",_t=f?` protected override socketRoute(path: string): { auth: boolean; functionPath: string } | undefined {
|
|
1320
1361
|
return LUNORA_SOCKETS[path];
|
|
1321
1362
|
}
|
|
1322
|
-
`:"",{externalSourceOverride:
|
|
1363
|
+
`:"",{externalSourceOverride:Et,sourceBootstrap:It,sourceClientCacheConst:Ct}=Hr(P),Lt=`
|
|
1364
|
+
private shardInitDeferred = false;
|
|
1365
|
+
private deferredShardInit?: Promise<void>;
|
|
1366
|
+
|
|
1323
1367
|
protected override async runShardInit(): Promise<void> {
|
|
1324
1368
|
this.ensureMigrated();
|
|
1325
|
-
${
|
|
1369
|
+
${oe?` clearMemoryTables(this.sql as SqlExec, schema as unknown as SchemaLike);
|
|
1326
1370
|
`:""}
|
|
1371
|
+
if (this.blockedUniqueIndexes.length > 0) {
|
|
1372
|
+
this.shardInitDeferred = true;
|
|
1373
|
+
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1327
1377
|
await this.dispatchShardInit();
|
|
1328
1378
|
}
|
|
1329
|
-
|
|
1379
|
+
|
|
1380
|
+
protected override async ensureShardInit(): Promise<void> {
|
|
1381
|
+
await super.ensureShardInit();
|
|
1382
|
+
|
|
1383
|
+
if (!this.shardInitDeferred) {
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
try {
|
|
1388
|
+
this.ensureMigrated();
|
|
1389
|
+
} catch (error: unknown) {
|
|
1390
|
+
this.recordShardInitError("__shard_init__", error);
|
|
1391
|
+
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (this.blockedUniqueIndexes.length > 0) {
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
this.deferredShardInit ??= (async (): Promise<void> => {
|
|
1400
|
+
try {
|
|
1401
|
+
await this.dispatchShardInit();
|
|
1402
|
+
} finally {
|
|
1403
|
+
this.shardInitDeferred = false;
|
|
1404
|
+
}
|
|
1405
|
+
})();
|
|
1406
|
+
|
|
1407
|
+
await this.deferredShardInit;
|
|
1408
|
+
}
|
|
1409
|
+
`,Pt=ie.length>0?`
|
|
1330
1410
|
if (LUNORA_TTL_SWEEPS.length > 0) {
|
|
1331
1411
|
void this.scheduleTtlSweep();
|
|
1332
1412
|
}
|
|
1333
|
-
`:"",
|
|
1413
|
+
`:"",Nt=`
|
|
1334
1414
|
public constructor(state: ShardDOState, env: unknown) {
|
|
1335
1415
|
super(state, env, {
|
|
1336
1416
|
// Every writer this file builds spreads the ctxDbTuning() slice —
|
|
@@ -1347,7 +1427,7 @@ ${te?` clearMemoryTables(this.sql as SqlExec, schema as unknown as Sc
|
|
|
1347
1427
|
// exists — a trigger that fails first still flushes and fuses.
|
|
1348
1428
|
...(config.observability === undefined ? {} : { telemetrySink: () => config.observability?.((env ?? {}) as Record<string, unknown>) }),
|
|
1349
1429
|
});
|
|
1350
|
-
${
|
|
1430
|
+
${It}${Pt} }
|
|
1351
1431
|
|
|
1352
1432
|
protected override isCacheableQuery(functionPath: string): boolean {
|
|
1353
1433
|
// NOT \`kind === "query"\` alone. A cache hit answers without running
|
|
@@ -1371,7 +1451,7 @@ ${Dt}${_t} }
|
|
|
1371
1451
|
protected override isPaidFunction(functionPath: string): boolean {
|
|
1372
1452
|
return LUNORA_FUNCTIONS[functionPath]?.x402 !== undefined;
|
|
1373
1453
|
}
|
|
1374
|
-
`,
|
|
1454
|
+
`,ue=c?`
|
|
1375
1455
|
protected override prepareFunction(functionPath: string, reason: "rpc" | "subscription" | "stream" | "reactor"): Promise<void> | undefined {
|
|
1376
1456
|
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
1377
1457
|
const allowsInternal = reason === "reactor" || (reason === "rpc" && this.isSystemDispatch());
|
|
@@ -1386,25 +1466,38 @@ ${Dt}${_t} }
|
|
|
1386
1466
|
|
|
1387
1467
|
return loadFunction(functionPath).then(() => undefined);
|
|
1388
1468
|
}
|
|
1389
|
-
`:""
|
|
1469
|
+
`:"",$t=ue===""?`
|
|
1390
1470
|
|
|
1391
|
-
`:`${
|
|
1392
|
-
`,
|
|
1471
|
+
`:`${ue}
|
|
1472
|
+
`,Mt=V?`
|
|
1473
|
+
protected override sensitiveArgNames(functionPath: string): ReadonlySet<string> {
|
|
1474
|
+
const registered = LUNORA_FUNCTIONS[functionPath];${D?`
|
|
1475
|
+
const shape = functionPath.startsWith("__shape__:") ? LUNORA_SHAPES[functionPath.slice("__shape__:".length)] : undefined;`:""}
|
|
1476
|
+
const fromValidators = sensitiveArgumentNames(registered?.args ?? ${D?"shape?.args ?? ":""}{});
|
|
1477
|
+
|
|
1478
|
+
if (registered?.sensitiveArgs === undefined) {
|
|
1479
|
+
return fromValidators;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
return new Set([...registered.sensitiveArgs, ...fromValidators]);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
`:"",qt=Er(c),Ft=c?` const preparation = this.prepareFunction(functionPath, "subscription");
|
|
1393
1486
|
if (preparation !== undefined) await preparation;
|
|
1394
|
-
`:"",
|
|
1487
|
+
`:"",Ut=Cr.replace("__PREPARE_FUNCTION__",Ft),Bt=Pr(h,c),jt=ee?`
|
|
1395
1488
|
const facade = db as unknown as Record<string, ReturnType<typeof bindTableFacade>>;
|
|
1396
1489
|
${i.tables.map(s=>` facade[${JSON.stringify(s.name)}] = bindTableFacade(db, ${JSON.stringify(s.name)});`).join(`
|
|
1397
1490
|
`)}
|
|
1398
|
-
`:"",
|
|
1491
|
+
`:"",Wt=`${k.everyBuild}${L.build}${Q.build}
|
|
1399
1492
|
const secrets = createSecrets(env);
|
|
1400
|
-
`,
|
|
1401
|
-
secrets,`,
|
|
1493
|
+
`,Ht=`${k.everyFields}${L.contextField}${G.contextField}${Q.contextField}
|
|
1494
|
+
secrets,`,Vt=G.build,Kt=A?`
|
|
1402
1495
|
const live = {
|
|
1403
1496
|
publish: async (ref: { __lunoraLive: string; topic: string }, payload: unknown, publishOptions?: { shardKey?: string }): Promise<void> => {
|
|
1404
1497
|
await this.publishLiveFromAction(ref.topic, payload, { ...publishOptions, stream: ref.__lunoraLive });
|
|
1405
1498
|
},
|
|
1406
1499
|
};
|
|
1407
|
-
`:"",
|
|
1500
|
+
`:"",Gt=[...k.actionFields,...C.length>0?["services"]:[],...A?["live"]:[]],Qt=`${k.actionBuild}${J.build}${Kt}`;return`${Sr}${N.join(`
|
|
1408
1501
|
`)}
|
|
1409
1502
|
|
|
1410
1503
|
type FunctionKind = "action" | "mutation" | "query";
|
|
@@ -1414,13 +1507,13 @@ interface FunctionReference {
|
|
|
1414
1507
|
}
|
|
1415
1508
|
|
|
1416
1509
|
/** Foreign-key columns per table (\`v.id("target")\` fields) for the data browser. */
|
|
1417
|
-
const LUNORA_TABLE_REFS = ${
|
|
1510
|
+
const LUNORA_TABLE_REFS = ${u(et,"Record<string, Record<string, string>>")};
|
|
1418
1511
|
|
|
1419
1512
|
/** Declared indexes per table (secondary, search, geo, rank, vector) for the schema viewer. */
|
|
1420
|
-
const LUNORA_TABLE_INDEXES = ${
|
|
1513
|
+
const LUNORA_TABLE_INDEXES = ${u(tt,'Record<string, Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }>>')};
|
|
1421
1514
|
|
|
1422
1515
|
/** Columns per table (typed, with PK/FK markers) for the studio's schema diagram, served via \`__lunora_admin__:describeTable\`. */
|
|
1423
|
-
const LUNORA_TABLE_COLUMNS = ${
|
|
1516
|
+
const LUNORA_TABLE_COLUMNS = ${u(rt,`Record<
|
|
1424
1517
|
string,
|
|
1425
1518
|
Array<{
|
|
1426
1519
|
bucket?: string;
|
|
@@ -1437,16 +1530,16 @@ const LUNORA_TABLE_COLUMNS = ${h(Xe,`Record<
|
|
|
1437
1530
|
>`)};
|
|
1438
1531
|
|
|
1439
1532
|
/** Storage-key columns per table (\`v.storage(...)\` fields) for the file browser's records↔files join. */
|
|
1440
|
-
const LUNORA_STORAGE_COLUMNS = ${
|
|
1533
|
+
const LUNORA_STORAGE_COLUMNS = ${u(nt,"Record<string, string[]>")};
|
|
1441
1534
|
|
|
1442
1535
|
/** Declarative TTL policies (\`.ttl(field, { after? })\`) the DO alarm sweep auto-expires rows for. */
|
|
1443
|
-
const LUNORA_TTL_SWEEPS = ${
|
|
1536
|
+
const LUNORA_TTL_SWEEPS = ${Zt(ie,"Array<{ after?: number; field: string; softDeleteField?: string; table: string }>")};
|
|
1444
1537
|
|
|
1445
1538
|
/** Static schema advisories (computed by @lunora/advisor at codegen time) served via \`__lunora_admin__:getAdvisories\`. */
|
|
1446
|
-
const LUNORA_ADVISORY_RULES = ${
|
|
1539
|
+
const LUNORA_ADVISORY_RULES = ${u(He,"Record<string, Partial<AdvisoryFinding>>")};
|
|
1447
1540
|
|
|
1448
1541
|
/** Per-finding rows; rule-level text lives once in \`LUNORA_ADVISORY_RULES\` and is merged back on first read. */
|
|
1449
|
-
const LUNORA_ADVISORY_ROWS = ${
|
|
1542
|
+
const LUNORA_ADVISORY_ROWS = ${u(Ve,"Array<Partial<AdvisoryFinding> & { name: string }>")};
|
|
1450
1543
|
|
|
1451
1544
|
const LUNORA_ADVISORIES = ((): (() => AdvisoryFinding[]) => {
|
|
1452
1545
|
let cached: AdvisoryFinding[] | undefined;
|
|
@@ -1454,20 +1547,23 @@ const LUNORA_ADVISORIES = ((): (() => AdvisoryFinding[]) => {
|
|
|
1454
1547
|
})();
|
|
1455
1548
|
|
|
1456
1549
|
/** Every declared procedure (discovered by @lunora/codegen) served via \`__lunora_admin__:getAdvisorProcedures\` — the health map's denominator. */
|
|
1457
|
-
const LUNORA_ADVISOR_PROCEDURES = ${
|
|
1550
|
+
const LUNORA_ADVISOR_PROCEDURES = ${u(Ke,"AdvisorProcedure[]")};
|
|
1458
1551
|
|
|
1459
1552
|
/** Read-only RLS metadata (policies + roles discovered from \`.use(rls(...))\` chains) served via \`__lunora_admin__:rlsPolicies\` for the studio's RLS inspector. */
|
|
1460
|
-
const LUNORA_RLS_METADATA = ${
|
|
1461
|
-
${
|
|
1553
|
+
const LUNORA_RLS_METADATA = ${u(ne,"RlsPoliciesResult")};
|
|
1554
|
+
${ot}
|
|
1462
1555
|
/** 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. */
|
|
1463
|
-
const LUNORA_MASK_METADATA = ${
|
|
1556
|
+
const LUNORA_MASK_METADATA = ${u(Ge,"MaskPoliciesResult")};
|
|
1464
1557
|
|
|
1465
1558
|
/** Read-only storage access-rule metadata (discovered from \`.use(storageRules(...))\` chains) served via \`__lunora_admin__:storageRules\` for the studio's access-rules view. */
|
|
1466
|
-
const LUNORA_STORAGE_RULES = ${
|
|
1559
|
+
const LUNORA_STORAGE_RULES = ${u(Qe,"StorageRulesResult")};
|
|
1467
1560
|
|
|
1468
1561
|
/** 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. */
|
|
1469
|
-
const LUNORA_STUDIO_FEATURES = ${
|
|
1470
|
-
|
|
1562
|
+
const LUNORA_STUDIO_FEATURES = ${u(Je,"StudioFeaturesResult")};
|
|
1563
|
+
|
|
1564
|
+
/** How long a shard with a UNIQUE index blocked by duplicate rows serves the cached diagnostic before re-attempting the build. */
|
|
1565
|
+
const BLOCKED_MIGRATION_RETRY_MS = 5000;
|
|
1566
|
+
${fe}${z.constant}${K.constant}${Ye}${Xe}${_e}${Le}${ke}${Ae}${J.specs}${Me}
|
|
1471
1567
|
export interface ShardDOConfig {
|
|
1472
1568
|
/** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
|
|
1473
1569
|
cdc?: boolean;
|
|
@@ -1484,25 +1580,27 @@ export interface ShardDOConfig {
|
|
|
1484
1580
|
/** \`unknown\` because \`@lunora/scheduler\`'s \`Scheduler\` is not assignable to \`SchedulerLike\`; the shard casts it. */
|
|
1485
1581
|
scheduler?: (env: Record<string, unknown>) => unknown;
|
|
1486
1582
|
/** \`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. */
|
|
1487
|
-
storage?: (env: Record<string, unknown>, origin?: string) => unknown;${
|
|
1583
|
+
storage?: (env: Record<string, unknown>, origin?: string) => unknown;${ht}${k.configFields}${L.configField}${Fe}${pt}${mt}${gt}${z.configField}
|
|
1488
1584
|
}
|
|
1489
|
-
${
|
|
1490
|
-
${
|
|
1585
|
+
${$("schedulerStub",bt,["cancel","runAfter","runAt"])}${$("storageStub",wt,["createMultipartUpload","delete","download","generateUploadUrl","getMetadata","getPresignedUrl","getSignedUrl","getUrl","head","list","resumeMultipartUpload","store","upload"],{sync:["getUrl","resumeMultipartUpload"]})}${yt}${Ct}${St}${k.stubs}${je}
|
|
1586
|
+
${qt}
|
|
1491
1587
|
|
|
1492
1588
|
/**
|
|
1493
1589
|
* Build the project's shard Durable Object. Export the result as \`ShardDO\`
|
|
1494
1590
|
* from the worker entry so wrangler binds it by name.
|
|
1495
1591
|
*/
|
|
1496
1592
|
export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOState, env: unknown) => ShardDOBase =>
|
|
1497
|
-
class extends ShardDOBase {${
|
|
1498
|
-
private
|
|
1499
|
-
|
|
1500
|
-
${
|
|
1501
|
-
${
|
|
1593
|
+
class extends ShardDOBase {${Nt}
|
|
1594
|
+
private blockedUniqueIndexes: readonly string[] = [];
|
|
1595
|
+
private migrated = false;
|
|
1596
|
+
private migrationRetryAt = 0;${$t}${Mt}${Bt}
|
|
1597
|
+
${de.override}
|
|
1598
|
+
${Ut}
|
|
1599
|
+
${at}${dt}${Dt}${Et}
|
|
1502
1600
|
protected override lifecycleHookPaths(event: "connect" | "disconnect" | "init" | "reactor" | "whisper"): readonly string[] {
|
|
1503
1601
|
return LUNORA_LIFECYCLE_HOOKS[event];
|
|
1504
1602
|
}
|
|
1505
|
-
${
|
|
1603
|
+
${Tt}${_t}${ce}${z.override}${Lt}
|
|
1506
1604
|
// One \`onQueryChange\` dispatch. Mirrors \`executeSubscription\` — the
|
|
1507
1605
|
// socket-terminated half of the same reactivity — and differs only in who
|
|
1508
1606
|
// consumes the result: the base's \`dispatchReactors\` stores the digest as
|
|
@@ -1540,7 +1638,7 @@ ${c?` const requestScope = this.captureRequestScope();
|
|
|
1540
1638
|
return undefined;
|
|
1541
1639
|
}
|
|
1542
1640
|
|
|
1543
|
-
this.
|
|
1641
|
+
this.ensureServable();
|
|
1544
1642
|
|
|
1545
1643
|
const footprint = createReadFootprint();
|
|
1546
1644
|
const ctx = this.buildCtx({ functionPath, headroom: this.subscriptionHeadroom(), onRead: footprint.onRead, onReadRange: footprint.onReadRange, trusted: true });
|
|
@@ -1601,7 +1699,7 @@ ${c?` const requestScope = this.captureRequestScope();
|
|
|
1601
1699
|
protected override studioFeatures(): StudioFeaturesResult {
|
|
1602
1700
|
return LUNORA_STUDIO_FEATURES();
|
|
1603
1701
|
}
|
|
1604
|
-
${
|
|
1702
|
+
${K.evaluateOverride}${K.subscriptionOverride}${ze}${Ze}
|
|
1605
1703
|
protected override advisories(): AdvisoryFinding[] {
|
|
1606
1704
|
return LUNORA_ADVISORIES();
|
|
1607
1705
|
}
|
|
@@ -1610,17 +1708,44 @@ ${j.evaluateOverride}${j.subscriptionOverride}${Ge}${Je}
|
|
|
1610
1708
|
return LUNORA_ADVISOR_PROCEDURES();
|
|
1611
1709
|
}
|
|
1612
1710
|
|
|
1613
|
-
${
|
|
1711
|
+
${Tr}
|
|
1614
1712
|
|
|
1713
|
+
/**
|
|
1714
|
+
* Migrate the base schema. A UNIQUE index that existing duplicate rows block
|
|
1715
|
+
* is deferred, not thrown: the rest of the pass completes so the admin path
|
|
1716
|
+
* (including the data migration that de-duplicates the table) keeps working.
|
|
1717
|
+
* User dispatch goes through \`ensureServable\`, which refuses until the
|
|
1718
|
+
* index exists. \`migrated\` stays false while one is outstanding, but the
|
|
1719
|
+
* retry (a schema-ledger write plus a full-table duplicate scan per blocked
|
|
1720
|
+
* index) runs at most once per \`BLOCKED_MIGRATION_RETRY_MS\`; in between,
|
|
1721
|
+
* callers see the cached diagnostics. The first call after a cold start
|
|
1722
|
+
* always migrates, and an admin data migration resets the clock so the
|
|
1723
|
+
* de-dup that unblocks the index takes effect on the next call.
|
|
1724
|
+
*/
|
|
1615
1725
|
protected override ensureMigrated(): void {
|
|
1616
1726
|
if (this.migrated) {
|
|
1617
1727
|
return;
|
|
1618
1728
|
}
|
|
1619
1729
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1730
|
+
const now = Date.now();
|
|
1731
|
+
|
|
1732
|
+
if (this.blockedUniqueIndexes.length > 0 && now < this.migrationRetryAt) {
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
this.blockedUniqueIndexes = runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false, deferBlockedUniqueIndexes: true${be} });
|
|
1737
|
+
this.migrated = this.blockedUniqueIndexes.length === 0;
|
|
1738
|
+
this.migrationRetryAt = now + BLOCKED_MIGRATION_RETRY_MS;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
private ensureServable(): void {
|
|
1742
|
+
this.ensureMigrated();
|
|
1743
|
+
|
|
1744
|
+
if (this.blockedUniqueIndexes.length > 0) {
|
|
1745
|
+
throw new LunoraError("INTERNAL", this.blockedUniqueIndexes.join("; "));
|
|
1746
|
+
}
|
|
1622
1747
|
}
|
|
1623
|
-
${
|
|
1748
|
+
${Z?`
|
|
1624
1749
|
protected override runShardSearchBackfill(options: { maxPages?: number }): SearchBackfillProgress {
|
|
1625
1750
|
// Migrations create the fts5 companions; without them every backfill
|
|
1626
1751
|
// statement would raise "no such table" on a shard that has never
|
|
@@ -1629,7 +1754,7 @@ ${J?`
|
|
|
1629
1754
|
|
|
1630
1755
|
return backfillSearchIndexes(this.sql as SqlExec, schema as unknown as SchemaLike, options);
|
|
1631
1756
|
}
|
|
1632
|
-
`:""}${
|
|
1633
|
-
${
|
|
1757
|
+
`:""}${vt}
|
|
1758
|
+
${_r({hasSensitiveArgs:V,lazyFunctions:c,actionOnlyFields:Gt,agentsBuild:Pe,agentsContextField:Ne,containersBuild:Oe,containersContextField:De,databaseOptions:Rt,everyContextBuild:Wt,everyContextField:Ht,facadeBlock:jt,globalDatabaseLine:Ot,notifyBuild:Vt,ormContextField:At,paymentsBuild:qe,paymentsContextField:Ue,queuesBuild:we,queuesContextField:ye,topicsBuild:Se,topicsContextField:Re,vectorsBuild:kt,vectorsContextField:xt,workflowsBuild:Ee,workflowsContextField:Ie,actionOnlyBuild:Qt})}
|
|
1634
1759
|
};
|
|
1635
|
-
`};export{
|
|
1760
|
+
`};export{$r as b,on as e};
|