@lunora/codegen 1.0.0-alpha.23 → 1.0.0-alpha.230

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.
Files changed (96) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +1 -1
  3. package/dist/index.d.mts +3344 -1166
  4. package/dist/index.d.ts +3344 -1166
  5. package/dist/index.mjs +1 -34
  6. package/dist/packem_shared/AGENTS_FILENAME-DoPI92Wu.mjs +1 -0
  7. package/dist/packem_shared/CONTAINERS_FILENAME-B6TFs82W.mjs +1 -0
  8. package/dist/packem_shared/CodegenDiagnosticError-BUXlG3P-.mjs +1 -0
  9. package/dist/packem_shared/DEFAULT_TARGET-kmm2i4Fw.mjs +1 -0
  10. package/dist/packem_shared/FLAGS_FILENAME-BedPDCsR.mjs +1 -0
  11. package/dist/packem_shared/GENERATED_HEADER-Cfw75xC4.mjs +1 -0
  12. package/dist/packem_shared/LUNORA_ERROR_CODES-C2s0xWQD.mjs +1 -0
  13. package/dist/packem_shared/MUTATORS_FILENAME-B6cKrN_m.mjs +1 -0
  14. package/dist/packem_shared/NOTIFY_FILENAME-Cri3NT5U.mjs +1 -0
  15. package/dist/packem_shared/OPENRPC_VERSION-DDd0VJyT.mjs +3 -0
  16. package/dist/packem_shared/PROJECT_CONFIG_FILENAMES-DzOu-lSc.mjs +1 -0
  17. package/dist/packem_shared/QUEUES_FILENAME-CxkPcMH1.mjs +1 -0
  18. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-Cssrg7G6.mjs +9 -0
  19. package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-C0oHwiJZ.mjs +2 -0
  20. package/dist/packem_shared/SDK_LANGUAGES-Df_CnJx5.mjs +372 -0
  21. package/dist/packem_shared/SHAPES_FILENAME-zJgzynXR.mjs +1 -0
  22. package/dist/packem_shared/SchemaSnapshotParseError-lUWe6lPo.mjs +18 -0
  23. package/dist/packem_shared/WORKFLOWS_FILENAME-B_ZJxRSx.mjs +1 -0
  24. package/dist/packem_shared/buildOpenApiDocument-D6SBl-tk.mjs +3 -0
  25. package/dist/packem_shared/chain-H2N_uKgj.mjs +1 -0
  26. package/dist/packem_shared/classify-procedure-call-BztLe76A.mjs +1 -0
  27. package/dist/packem_shared/context-property-calls-CjDzLU8p.mjs +1 -0
  28. package/dist/packem_shared/declared-output-Iaar469J.mjs +1 -0
  29. package/dist/packem_shared/describeErrorLevelFindings-D23VSkFP.mjs +1 -0
  30. package/dist/packem_shared/discoverAuthApiCalls-FxrdBeVt.mjs +1 -0
  31. package/dist/packem_shared/discoverCrons-MfKw7IFf.mjs +1 -0
  32. package/dist/packem_shared/discoverFunctions-BsmX-sEq.mjs +1 -0
  33. package/dist/packem_shared/discoverHttpRoutes-BcXI9CNt.mjs +1 -0
  34. package/dist/packem_shared/discoverHyperdriveCalls-D9qXqTq3.mjs +1 -0
  35. package/dist/packem_shared/discoverInserts-CFJ3IVeI.mjs +1 -0
  36. package/dist/packem_shared/discoverMaskProcedures-CV_Cc2_g.mjs +1 -0
  37. package/dist/packem_shared/discoverMigrations-DbrHwcGE.mjs +1 -0
  38. package/dist/packem_shared/discoverNondeterministicCalls-CSQ_GDcA.mjs +1 -0
  39. package/dist/packem_shared/discoverQueries-C7y8i_Ga.mjs +1 -0
  40. package/dist/packem_shared/discoverR2sqlCalls-fu7fC0i_.mjs +1 -0
  41. package/dist/packem_shared/discoverRlsMetadata-CPmAGL0W.mjs +1 -0
  42. package/dist/packem_shared/discoverRlsProcedures-BO4qC77y.mjs +1 -0
  43. package/dist/packem_shared/discoverSandboxUsage-CgmfgDTS.mjs +1 -0
  44. package/dist/packem_shared/discoverSchema-D8DBDHEg.mjs +1 -0
  45. package/dist/packem_shared/discoverStorageRulesMetadata-D8VYflWt.mjs +1 -0
  46. package/dist/packem_shared/emit-D_prE7y6.mjs +2691 -0
  47. package/dist/packem_shared/emitApp-rCWsXXGd.mjs +853 -0
  48. package/dist/packem_shared/exported-procedure-chains-CmCDiPBV.mjs +1 -0
  49. package/dist/packem_shared/formatAdvisories-DwmUKYuc.mjs +2 -0
  50. package/dist/packem_shared/index-BoaHhVtM.mjs +1 -0
  51. package/dist/packem_shared/isD1GlobalTable-udvHAAzD.mjs +1 -0
  52. package/dist/packem_shared/isTypedSchema-CKzc3ioR.mjs +4 -0
  53. package/dist/packem_shared/listLunoraSourceFiles-DP-5e-RE.mjs +1 -0
  54. package/dist/packem_shared/loadProjectConfig-CKsFNgn3.mjs +1 -0
  55. package/dist/packem_shared/module-specifiers-WF8iAydP.mjs +1 -0
  56. package/dist/packem_shared/parse-validator-C94Yiabo.mjs +1 -0
  57. package/dist/packem_shared/paths-C-9fwEgQ.mjs +1 -0
  58. package/dist/packem_shared/queries-B-f4jO-x.mjs +1 -0
  59. package/dist/packem_shared/readPackageDependencies-C6h6FZuP.mjs +1 -0
  60. package/dist/packem_shared/redact-Cy14LEnc.mjs +1 -0
  61. package/dist/packem_shared/schemaFromIr-BWFnvCVg.mjs +1 -0
  62. package/dist/packem_shared/unwrap-handler-return-CoV_GK33.mjs +1 -0
  63. package/package.json +16 -7
  64. package/dist/packem_shared/CONTAINERS_FILENAME-DlP6YM_Q.mjs +0 -224
  65. package/dist/packem_shared/CodegenDiagnosticError-DeblMkzO.mjs +0 -23
  66. package/dist/packem_shared/FLAGS_FILENAME-B1vE0LIo.mjs +0 -139
  67. package/dist/packem_shared/GENERATED_HEADER-DUkXYD5R.mjs +0 -3260
  68. package/dist/packem_shared/LUNORA_ERROR_CODES-CySpQPD3.mjs +0 -61
  69. package/dist/packem_shared/LUNORA_SOLUTION_RULES-BTejmZp-.mjs +0 -131
  70. package/dist/packem_shared/MUTATORS_FILENAME-BhqdPtKp.mjs +0 -81
  71. package/dist/packem_shared/OPENRPC_VERSION-CjBUaolx.mjs +0 -60
  72. package/dist/packem_shared/QUEUES_FILENAME-B5_eWCRe.mjs +0 -119
  73. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-BUcGCXi3.mjs +0 -1130
  74. package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs +0 -245
  75. package/dist/packem_shared/SHAPES_FILENAME-ChV7MqgE.mjs +0 -94
  76. package/dist/packem_shared/WORKFLOWS_FILENAME-CCisG0Vy.mjs +0 -124
  77. package/dist/packem_shared/buildOpenApiDocument-WxYTLh11.mjs +0 -183
  78. package/dist/packem_shared/discover-ast-CT6BgBr4.mjs +0 -13
  79. package/dist/packem_shared/discoverAuthApiCalls-CoirYbg6.mjs +0 -62
  80. package/dist/packem_shared/discoverCrons-Cev7RRAf.mjs +0 -257
  81. package/dist/packem_shared/discoverFunctions-BWMczzBx.mjs +0 -465
  82. package/dist/packem_shared/discoverHttpRoutes-CfP6cMzt.mjs +0 -131
  83. package/dist/packem_shared/discoverInserts-C7zxXkUf.mjs +0 -61
  84. package/dist/packem_shared/discoverMaskProcedures-Cm81kwrb.mjs +0 -217
  85. package/dist/packem_shared/discoverMigrations-Bi5nJ0mJ.mjs +0 -97
  86. package/dist/packem_shared/discoverNondeterministicCalls-C4M8AXmQ.mjs +0 -122
  87. package/dist/packem_shared/discoverQueries-B0wGT-xe.mjs +0 -62
  88. package/dist/packem_shared/discoverR2sqlCalls-BVNMd428.mjs +0 -80
  89. package/dist/packem_shared/discoverRlsMetadata-BS9GOGC5.mjs +0 -280
  90. package/dist/packem_shared/discoverSchema-KYq55qu3.mjs +0 -864
  91. package/dist/packem_shared/discoverStorageRulesMetadata-Da8BKXcI.mjs +0 -97
  92. package/dist/packem_shared/emitApp-DD0i1ks0.mjs +0 -666
  93. package/dist/packem_shared/formatAdvisories-DdjK7sgh.mjs +0 -81
  94. package/dist/packem_shared/parse-validator-Cabb60UV.mjs +0 -133
  95. package/dist/packem_shared/paths-BRd6JHuF.mjs +0 -11
  96. package/dist/packem_shared/schemaFromIr-DTYsLBaA.mjs +0 -57
@@ -0,0 +1,2691 @@
1
+ import{agentComponent as ft}from"@lunora/agent/component";import{LunoraError as Z}from"@lunora/errors";import{serializeSchemaSnapshot as An,hashSchemaSnapshot as On}from"./SCHEMA_SNAPSHOT_VERSION-C0oHwiJZ.mjs";import{d as In}from"./declared-output-Iaar469J.mjs";import{isHyperdriveGlobalTable as Cn,isD1GlobalTable as Dn}from"./isD1GlobalTable-udvHAAzD.mjs";import{s as A}from"./paths-C-9fwEgQ.mjs";import{acceptsAbsentIr as Ln}from"./LUNORA_ERROR_CODES-C2s0xWQD.mjs";const bt=[{contextProperty:"access",key:"access",moduleSpecifier:"@lunora/cloudflare-access",requiredPackage:"@lunora/cloudflare-access"},{appMethod:{configKey:"ai",doc:"Override the Workers AI binding backing `ctx.ai` (defaults to `env.AI`).",method:"ai"},contextProperty:"ai",key:"ai",moduleSpecifier:"@lunora/ai",requiredPackage:"@lunora/ai"},{appMethod:{configKey:"analytics",doc:"Override the Analytics Engine dataset backing `ctx.analytics` (defaults to `env.ANALYTICS`).",method:"analytics"},contextProperty:"analytics",key:"analytics",moduleSpecifier:"@lunora/bindings/analytics",requiredPackage:"@lunora/bindings",serverCtxField:{field:`
2
+ /** Analytics Engine telemetry sink. Fire-and-forget and sampled; do not read it back in-handler. */
3
+ readonly analytics: import("@lunora/bindings/analytics").AnalyticsClient;`,tier:"every"}},{appMethod:{configKey:"browser",doc:"Build the `ctx.browser` helper, e.g. `(env) => createBrowser({ binding: env.BROWSER, launch })`. REQUIRED: unlike the binding-backed capabilities, `ctx.browser` is not auto-constructed — without this thunk every method throws, because the generated worker deliberately stays free of the optional `@cloudflare/playwright` peer.",method:"browser"},contextProperty:"browser",key:"browser",moduleSpecifier:"@lunora/browser",requiredPackage:"@lunora/browser",serverCtxField:{field:`
4
+ /** Browser Rendering (screenshots/PDF/scrape). Non-deterministic — available only in actions. */
5
+ readonly browser: import("@lunora/browser").Browser;`,tier:"action"}},{contextProperty:"containers",key:"container",moduleSpecifier:"@lunora/container"},{contextProperty:"flags",key:"flags",moduleSpecifier:"@lunora/flags"},{appMethod:{configKey:"sql",doc:"Wire the Hyperdrive SQL client backing `ctx.sql` — build it with `createHyperdrive` + `fromPostgresJs`/`fromNodePg`/`fromMysql2`.",method:"hyperdrive"},contextProperty:"sql",key:"hyperdrive",moduleSpecifier:"@lunora/hyperdrive",requiredPackage:"@lunora/hyperdrive",serverCtxField:{field:`
6
+ /**
7
+ * External database access via Hyperdrive. Non-deterministic — available only in actions. Writes here are NOT tracked by Lunora live queries; subscriptions will not re-run on external DB changes.
8
+ */
9
+ readonly sql: import("@lunora/hyperdrive").SqlClient;`,tier:"action"}},{appMethod:{configKey:"images",doc:"Override the Images binding backing `ctx.images` (defaults to `env.IMAGES`).",method:"images"},contextProperty:"images",key:"images",moduleSpecifier:"@lunora/bindings/images",requiredPackage:"@lunora/bindings",serverCtxField:{field:`
10
+ /** Cloudflare Images transforms (resize/format/optimize). Non-deterministic — available only in actions. */
11
+ readonly images: import("@lunora/bindings/images").Images;`,tier:"action"}},{appMethod:{configKey:"kv",doc:"Override the Workers KV binding backing `ctx.kv` (defaults to `env.KV`).",method:"kv"},contextProperty:"kv",key:"kv",moduleSpecifier:"@lunora/bindings/kv",requiredPackage:"@lunora/bindings",serverCtxField:{field:`
12
+ readonly kv: import("@lunora/bindings/kv").Kv;`,tier:"every"}},{key:"mail",moduleSpecifier:"@lunora/mail"},{contextProperty:"notify",key:"notify",moduleSpecifier:"@lunora/notify"},{appMethod:{configKey:"payment",doc:"Wire the payment options backing `ctx.payments`.",method:"payment"},contextProperty:"payments",key:"payments",moduleSpecifier:"@lunora/payment",requiredPackage:"@lunora/payment"},{appMethod:{configKey:"x402",doc:"Wire the x402 agent-wallet pay rail backing `ctx.x402` — a payment-enabled `fetch` that answers `402` challenges under a mandatory spend policy (ActionCtx-only; spends real funds).",method:"x402"},contextProperty:"x402",key:"x402",moduleSpecifier:"@lunora/x402/pay",requiredPackage:"@lunora/x402"},{contextProperty:"pipelines",key:"pipelines",moduleSpecifier:"@lunora/bindings/pipelines",requiredPackage:"@lunora/bindings",serverCtxField:{field:`
13
+ /** Pipelines ingestion sink (durable, R2-backed). Fire-and-forget and batched; do not read it back in-handler. */
14
+ readonly pipelines: import("@lunora/bindings/pipelines").PipelineClient;`,tier:"action"}},{appMethod:{configKey:"r2sql",doc:"Wire the R2 SQL client backing `ctx.r2sql` — build it with `createR2Sql({ accountId, apiToken, bucket })` (defaults to env `R2_SQL_TOKEN` / `R2_SQL_ACCOUNT_ID` / `R2_SQL_BUCKET`).",method:"r2sql"},contextProperty:"r2sql",key:"r2sql",moduleSpecifier:"@lunora/bindings/r2sql",requiredPackage:"@lunora/bindings",serverCtxField:{field:`
15
+ /**
16
+ * R2 SQL over Apache Iceberg tables (window functions, DISTINCT, set operations). Non-deterministic — available only in actions. Reads here are NOT tracked by Lunora live queries.
17
+ */
18
+ readonly r2sql: import("@lunora/bindings/r2sql").R2SqlClient;`,tier:"action"}},{contextProperty:"scheduler",key:"scheduler",moduleSpecifier:"@lunora/scheduler"},{contextProperty:"storage",key:"storage",moduleSpecifier:"@lunora/storage"},{appMethod:{configKey:"vectors",doc:"Wire the Vectorize index map backing `ctx.vectors`.",method:"vectors"},contextProperty:"vectors",key:"vectors",moduleSpecifier:"@lunora/bindings/vectors"},{contextProperty:"workflows",key:"workflows",moduleSpecifier:"@lunora/workflow"}],En=bt,_n=new Map(En.flatMap(e=>e.serverCtxField?[[e.key,e.serverCtxField]]:[])),ga=bt.flatMap(e=>"appMethod"in e?[{appMethod:e.appMethod,key:e.key}]:[]),yt=/^(?:"(?:[^"\\]|\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4}))*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u,Pn=new Set(["any","bigint","boolean","date","id","null","number","storage","string","timestamp"]),wt=e=>e.column!==void 0,Mn=(e,t)=>{switch(e){case"bigint":return`if (typeof ${t} !== "bigint") return DEFER;
19
+ `;case"boolean":return`if (typeof ${t} !== "boolean") return DEFER;
20
+ `;case"date":case"number":case"timestamp":return`if (typeof ${t} !== "number" || !Number.isFinite(${t})) return DEFER;
21
+ `;case"null":return`if (${t} !== null) return DEFER;
22
+ `;default:return`if (typeof ${t} !== "string") return DEFER;
23
+ `}},Fn=(e,t)=>e.stringMaxLength===void 0?"":`if (${t}.length > ${String(e.stringMaxLength)}) return DEFER;
24
+ `,Bn=(e,t)=>{const n=e.literalValue?.trim();if(!(n===void 0||!yt.test(n)))return{out:t,pre:`if (${t} !== ${n}) return DEFER;
25
+ `}},ze=(e,t,n)=>{if(!wt(e)&&!(e.unmodelledRefinement||e.sourceText!==void 0)){if(Pn.has(e.kind))return e.kind==="any"?{out:t,pre:""}:{out:t,pre:Mn(e.kind,t)+Fn(e,t)};switch(e.kind){case"array":return qn(e,t,n);case"literal":return Bn(e,t);case"object":return Un(e,t,n);default:return}}},qn=(e,t,n)=>{const{inner:r}=e;if(!r)return;const a=n.next(),o=`__arr${String(a)}`,i=`__i${String(a)}`,s=`__e${String(a)}`,c=ze(r,s,n);if(!c)return;const d=`if (!Array.isArray(${t})) return DEFER;
26
+ const ${o} = new Array(${t}.length);
27
+ for (let ${i} = 0; ${i} < ${t}.length; ${i}++) {
28
+ const ${s} = ${t}[${i}];
29
+ ${c.pre}${o}[${i}] = ${c.out};
30
+ }
31
+ `;return{out:o,pre:d}},jn=(e,t,n,r)=>{const a=JSON.stringify(e);if(t.kind==="optional"){if(t.hasRefinement||t.sourceText!==void 0||wt(t))return;const{inner:i}=t;if(!i)return;const s=ze(i,n,r);if(!s)return;const c=r.next(),d=`__has${String(c)}`,h=`__val${String(c)}`,g=`let ${d} = false;
32
+ let ${h};
33
+ if (${n} !== undefined) {
34
+ ${s.pre}${h} = ${s.out};
35
+ ${d} = true;
36
+ }
37
+ `;return{entry:`...(${d} ? { ${a}: ${h} } : {})`,pre:g}}const o=ze(t,n,r);if(o)return{entry:`${a}: ${o.out}`,pre:o.pre}},Wn=new Set(Object.getOwnPropertyNames(Object.prototype)),kt=(e,t,n)=>{let r="";const a=[];for(const o of Object.keys(e)){const i=e[o];if(!i)return;const s=JSON.stringify(o);let c=`${t}[${s}]`;if(Wn.has(o)){const h=`__f${String(n.next())}`;r+=`const ${h} = Object.hasOwn(${t}, ${s}) ? ${t}[${s}] : undefined;
38
+ `,c=h}const d=jn(o,i,c,n);if(!d)return;r+=d.pre,a.push(d.entry)}return{entries:a.join(", "),pre:r}},xt=e=>{const t=`Object.getPrototypeOf(${e})`;return`if (${t} !== Object.prototype && ${t} !== null) return DEFER;
39
+ `},Un=(e,t,n)=>{const r=e.shape??{},a=kt(r,t,n);if(!a)return;const o=n.next(),i=`__obj${String(o)}`,s=`if (typeof ${t} !== "object" || ${t} === null || Array.isArray(${t})) return DEFER;
40
+ ${xt(t)}${a.pre}const ${i} = { ${a.entries} };
41
+ `;return{out:i,pre:s}},Kn=e=>{let t=0;const r=kt(e,"source",{next:()=>(t+=1,t)});if(r)return`(source) => {
42
+ if (typeof source !== "object" || source === null || Array.isArray(source)) return DEFER;
43
+ ${xt("source")}${r.pre}return { ${r.entries} };
44
+ }`},I=(e,t)=>`JSON.parse(${JSON.stringify(JSON.stringify(e))}) as ${t}`,S=`// GENERATED by @lunora/codegen — do not edit.
45
+ // Run \`lunora codegen\` to regenerate.
46
+
47
+ `,se=(e=!1)=>e?{client:"lunorash/client",do:"lunorash/do",flags:"lunorash/flags",server:"lunorash/server",serverDataModel:"lunorash/server/data-model",serverDrizzle:"lunorash/server/drizzle",serverTypes:"lunorash/server/types",values:"lunorash/values"}:{client:"@lunora/client",do:"@lunora/do",flags:"@lunora/flags",server:"@lunora/server",serverDataModel:"@lunora/server/data-model",serverDrizzle:"@lunora/server/drizzle",serverTypes:"@lunora/server/types",values:"@lunora/values"},vt=/^[A-Za-z_$][\w$]*$/u,Qn=/^[\w./-]+$/u,f=(e,t)=>{if(!vt.test(e))throw new Z("INTERNAL",`@lunora/codegen: ${t} is not a valid JS identifier: ${JSON.stringify(e)}`)},Ge=e=>e.charAt(0).toUpperCase()+e.slice(1),W=e=>vt.test(e)?e:JSON.stringify(e),X=e=>e==="__proto__"?`[${JSON.stringify(e)}]`:W(e),Vn={any:"unknown",bigint:"bigint",boolean:"boolean",bytes:"ArrayBuffer",date:"number",geoPoint:"{ lat: number; lng: number }",null:"null",number:"number",storage:"string",string:"string",timestamp:"number"},Hn=e=>{if(e===void 0)return"unknown";if(!yt.test(e))throw new Z("INTERNAL",`@lunora/codegen: v.literal() argument is not a parseable string/number/boolean/null literal: ${JSON.stringify(e)}`);return e},N=e=>e.column?.notNull===!1?`${ht(e)} | null`:ht(e),ht=e=>{const t=Vn[e.kind];if(t!==void 0)return t;switch(e.kind){case"array":return`Array<${e.inner?N(e.inner):"unknown"}>`;case"from":return e.tsType??"unknown";case"id":{const n=e.tableName??"_unknown_";return f(n,"v.id() table name"),`Id<"${n}">`}case"literal":return Hn(e.literalValue);case"object":return e.shape?`{ ${Object.entries(e.shape).map(([r,a])=>St(r,a)).join("; ")} }`:"Record<string, unknown>";case"optional":return`${e.inner?N(e.inner):"unknown"} | undefined`;case"record":{const n=e.keyType?N(e.keyType):"string",r=e.valueType?N(e.valueType):"unknown";return`Record<${n}, ${r}>`}case"union":return e.members&&e.members.length>0?e.members.map(n=>N(n)).join(" | "):"never";default:return"unknown"}},St=(e,t)=>{const n=W(e);if(t.kind==="optional"){const r=t.inner?N(t.inner):"unknown";return`${n}?: ${r}`}return Ln(t)?`${n}?: ${N(t)}`:`${n}: ${N(t)}`},ve=e=>{const t=Object.entries(e);return t.length===0?"{}":`{ ${t.map(([n,r])=>St(n,r)).join("; ")} }`},Rt=e=>e.kind==="optional"||!!e.column?.hasDefault,ie=e=>e.kind==="optional"&&e.inner?e.inner:e,Gn=e=>{f(e.name,"table name");const t=Object.entries(e.shape).map(([r,a])=>{const o=ie(a),i=N(o),s=W(r);return Rt(a)?` ${s}?: ${i};`:` ${s}: ${i};`}).join(`
48
+ `),n=t?`
49
+ ${t}
50
+ `:"";return`export interface Insert_${e.name} {
51
+ _id?: Id<"${e.name}">;
52
+ _creationTime?: number;${n}}`},Je="schema",fa=e=>{for(const u of e.tables)f(u.name,"table name");const t=e.tables.map(u=>`"${u.name}"`).join(" | ")||"never",n=e.tables.filter(u=>u.extensionKey===void 0).map(u=>`"${u.name}"`).join(" | ")||"never",r=e.tables.map(u=>{const l=Object.entries(u.shape).map(([x,v])=>{const $=W(x);if(v.kind==="optional"){const T=v.inner?N(v.inner):"unknown";return` ${$}?: ${T};`}return` ${$}: ${N(v)};`}).join(`
53
+ `),b=l?`
54
+ ${l}
55
+ `:"",R=u.commitOrdered===!0?`
56
+ _commitSeq: number;`:"";return`export interface Doc_${u.name} {
57
+ _id: Id<"${u.name}">;
58
+ _creationTime: number;${R}${b}}`}).join(`
59
+
60
+ `),a=e.tables.map(u=>` ${u.name}: Doc_${u.name};`).join(`
61
+ `),o=e.tables.map(u=>{const l=u.indexes.map(b=>JSON.stringify(b.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
62
+ `),i=e.tables.map(u=>{const l=u.searchIndexes.map(b=>JSON.stringify(b.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
63
+ `),s=e.tables.map(u=>{const l=u.rankIndexes.map(b=>JSON.stringify(b.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
64
+ `),c=e.tables.map(u=>{const l=u.shardMode==="global"?"":(u.geoIndexes??[]).map(b=>JSON.stringify(b.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
65
+ `),d=e.vectorIndexes.map(u=>JSON.stringify(u.name)).join(" | ")||"never",h=e.tables.map(u=>Gn(u)).join(`
66
+
67
+ `),g=e.tables.map(u=>` ${u.name}: Insert_${u.name};`).join(`
68
+ `),y=e.tables.map(u=>{const l=u.relations.map(b=>(f(b.table,"relation target table"),` ${W(b.name)}: ${b.kind==="one"?"OneRelation":"ManyRelation"}<"${b.table}">;`)).join(`
69
+ `);return l?` ${u.name}: {
70
+ ${l}
71
+ };`:` ${u.name}: {};`}).join(`
72
+ `);return E(`${S}export type TableName = ${t};
73
+
74
+ /**
75
+ * The tables **this app declared** — every {@link TableName} except those an add-on
76
+ * contributed through \`defineSchema(...).extend(...)\`.
77
+ *
78
+ * An add-on's tables are real tables and stay in \`TableName\` (they are queryable,
79
+ * they appear in \`DataModel\`), but an app enumerating "my tables" should not have to
80
+ * know they exist: an account-deletion sweep, an export, a migration allowlist, or a
81
+ * helper generic over a table union is about the app's own data, and every add-on
82
+ * added or removed would otherwise silently change the correct answer.
83
+ *
84
+ * \`\`\`ts
85
+ * // Doesn't need to mention \`ratelimit_buckets\`, and won't drift when an add-on lands.
86
+ * const EXPORTED: readonly AppTableName[] = ["nodes", "tagColors"];
87
+ * \`\`\`
88
+ */
89
+ export type AppTableName = ${n};
90
+
91
+ export type Id<TName extends string> = string & { readonly __table: TName };
92
+
93
+ ${r}
94
+
95
+ export interface DataModel {
96
+ ${a}
97
+ }
98
+
99
+ export type Doc<T extends keyof DataModel> = DataModel[T];
100
+
101
+ /**
102
+ * Per-table index name union. \`never\` for tables without secondary indexes.
103
+ * Used by \`TableReader.withIndex()\` to constrain callers to declared names.
104
+ */
105
+ export interface IndexNamesByTable {
106
+ ${o}
107
+ }
108
+
109
+ export type IndexName<T extends keyof DataModel> = IndexNamesByTable[T];
110
+
111
+ /** Per-table search-index name union. \`never\` for tables without searchIndex. */
112
+ export interface SearchIndexNamesByTable {
113
+ ${i}
114
+ }
115
+
116
+ export type SearchIndexName<T extends keyof DataModel> = SearchIndexNamesByTable[T];
117
+
118
+ /** Per-table rank-index name union. \`never\` for tables without a rankIndex. */
119
+ export interface RankIndexNamesByTable {
120
+ ${s}
121
+ }
122
+
123
+ export type RankIndexName<T extends keyof DataModel> = RankIndexNamesByTable[T];
124
+
125
+ /** Per-table geo-index name union. \`never\` for tables without a geoIndex. */
126
+ export interface GeoIndexNamesByTable {
127
+ ${c}
128
+ }
129
+
130
+ export type GeoIndexName<T extends keyof DataModel> = GeoIndexNamesByTable[T];
131
+
132
+ /** Union of declared vector index names. \`never\` when none are declared. */
133
+ export type VectorIndexName = ${d};
134
+
135
+ ${h}
136
+
137
+ /** Per-table insert shape, accepted by \`ctx.db.<table>.insert(...)\`. */
138
+ export interface InsertModel {
139
+ ${g}
140
+ }
141
+
142
+ export type Insert<T extends keyof DataModel> = InsertModel[T];
143
+
144
+ /**
145
+ * Phantom relation descriptors. They carry the relation kind and target table
146
+ * as type parameters only — there is no runtime value — so the \`with\` argument
147
+ * and its return type can be inferred from {@link Relations}.
148
+ */
149
+ export interface OneRelation<Target extends keyof DataModel> {
150
+ readonly __relationKind: "one";
151
+ readonly __target: Target;
152
+ }
153
+
154
+ export interface ManyRelation<Target extends keyof DataModel> {
155
+ readonly __relationKind: "many";
156
+ readonly __target: Target;
157
+ }
158
+
159
+ /** Per-table relation map keyed by accessor name. \`{}\` for tables with none. */
160
+ export interface Relations {
161
+ ${y}
162
+ }
163
+
164
+ `,Je)},zn=/import\("(?<spec>(?:\.\.?\/)*_generated\/[^"]+)"\)/gu,$t=/^(?:\.\.?\/)*_generated\//u,Tt=/\.[A-Za-z]\w*$/u,Jn=e=>e.replaceAll(zn,(t,n)=>{const r=n.replace($t,"./");return`import("${Tt.test(r)?r:`${r}.js`}")`}),Yn=/import\("(?<spec>[^"]+)"\)/gu,pt="node_modules/",Xn=/^@types\//u,Zn=e=>e.replaceAll(Yn,(t,n)=>{const r=n.lastIndexOf(pt);if(r===-1||r>0&&n[r-1]!=="/")return t;const a=n.slice(r+pt.length);return a===""||Xn.test(a)?t:`import("${a}")`}),er=/import\("(?<spec>\.\.?\/[^"]+)"\)/gu,tr=/^(?:\.\.?\/)+/u,nr=e=>{const t=[];for(const n of e.split("/"))if(!(n===""||n===".")){if(n===".."&&t.length>0&&t.at(-1)!==".."){t.pop();continue}t.push(n)}return t.join("/")},rr=(e,t)=>e.replaceAll(er,(n,r)=>{if($t.test(r.replace(tr,"")))return n;const a=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",i=`../${nr(`${a}/${r}`)}`;return`import("${Tt.test(i)?i:`${i}.js`}")`}),E=(e,t)=>Jn(rr(Zn(e),t)),ar=["client","do","errors","flags","observability","platform","ratelimit","runtime","server","values"],or=new RegExp(String.raw`import\("@lunora/(?<pkg>${ar.join("|")})(?<subpath>/[^"]*)?"\)`,"gu"),sr={flags:new Set(["/web"]),platform:new Set},Nt=(e,t)=>t?e.replaceAll(or,(n,r,a)=>{const o=sr[r];return a&&o&&!o.has(a)?n:`import("lunorash/${r}${a??""}")`}):e,Ye=e=>["Doc","Id"].filter(t=>new RegExp(String.raw`(?<![.$\w])${t}<`,"u").test(e)),At=e=>In(e)?N(e.output):e.returnType,Xe=e=>{const t=new Map;for(const n of e){const r=t.get(n.filePath)??[];r.push(n),t.set(n.filePath,r)}return[...t.entries()].toSorted(([n],[r])=>n.localeCompare(r))},mt=e=>{const t=([n,r])=>{const a=r.toSorted((o,i)=>o.exportName.localeCompare(i.exportName)).map(o=>{const i=E(ve(o.args),o.filePath),s=E(At(o),o.filePath);return` ${o.exportName}: FunctionReference<"${o.kind}", ${i}, ${s}>;`}).join(`
165
+ `);return` ${W(A(n))}: {
166
+ ${a}
167
+ };`};return Xe(e).map(n=>t(n)).join(`
168
+ `)},ir=(e,t)=>{if(e.length===0&&t.length===0)return{block:"",importLine:""};const n=[];let r="";if(e.length>0&&(r+="\n/** Params type carried by a `defineWorkflow` definition (its phantom `__params`). */\ntype WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params } ? (unknown extends Params ? Record<string, unknown> : Params) : Record<string, unknown>;\n"),r+=`
169
+ /** A typed reference to a durable workflow, addressable for \`cronJobs()\` targets. Mirrors \`@lunora/scheduler\`'s \`WorkflowReference\` structurally. */
170
+ export interface WorkflowReference<Params = Record<string, unknown>> {
171
+ readonly isLunoraWorkflow: true;
172
+ readonly __params?: Params;
173
+ readonly binding: string;
174
+ readonly name: string;
175
+ }
176
+ `,e.length>0){const a=[...e].toSorted((s,c)=>s.exportName.localeCompare(c.exportName)),o=a.map(s=>` ${s.exportName}: WorkflowReference<WorkflowParamsOf<typeof lunoraWorkflowDefinitions.${s.exportName}>>;`).join(`
177
+ `),i=a.map(s=>` ${X(s.exportName)}: { isLunoraWorkflow: true, binding: ${JSON.stringify(s.bindingName)}, name: ${JSON.stringify(s.exportName)} },`).join(`
178
+ `);r+=`
179
+ /** This project's durable workflows, addressable as typed references (e.g. \`crons.daily("digest", …, workflows.digestPipeline, params)\`). */
180
+ export interface WorkflowsRef {
181
+ ${o}
182
+ }
183
+
184
+ export const workflows: WorkflowsRef = {
185
+ ${i}
186
+ };
187
+ `,n.push(`import type * as lunoraWorkflowDefinitions from "../workflows.js";
188
+ `)}if(t.length>0){const a=[...t].toSorted((s,c)=>s.exportName.localeCompare(c.exportName)),o=a.map(s=>` ${s.exportName}: WorkflowReference<AgentRunInput>;`).join(`
189
+ `),i=a.map(s=>` ${X(s.exportName)}: { isLunoraWorkflow: true, binding: ${JSON.stringify(s.bindingName)}, name: ${JSON.stringify(s.name)} },`).join(`
190
+ `);r+=`
191
+ /** This project's durable agents, addressable as typed references for \`cronJobs()\` — each fire starts a fresh agent run (e.g. \`crons.daily("sweep", …, agents.support, { input, threadKey })\`). */
192
+ export interface AgentsRef {
193
+ ${o}
194
+ }
195
+
196
+ export const agents: AgentsRef = {
197
+ ${i}
198
+ };
199
+ `,n.push(`import type { AgentRunInput } from "@lunora/agent";
200
+ `)}return{block:r,importLine:n.join("")}},lr=()=>Object.keys(ft().functions).toSorted((e,t)=>e.localeCompare(t)),Ot=e=>new Set(e.filter(t=>A(t.filePath)==="agents").map(t=>t.exportName)),cr=()=>new Set(Object.entries(ft().functions).filter(([,e])=>e.visibility==="internal").map(([e])=>e)),dr=(e,t)=>{const n={importLine:"",lines:"",prelude:""};if(e.length===0)return n;const r=Ot(t),a=cr(),o=t.find(s=>A(s.filePath)==="agents"&&a.has(s.exportName));if(o)throw new Z("INTERNAL",`@lunora/codegen: "agents:${o.exportName}" is reserved for the durable agent loop's internal dispatch — rename the "${o.exportName}" export in ${o.filePath}`);const i=lr().filter(s=>!r.has(s));return i.length===0?n:{importLine:`import { agentComponent } from "@lunora/agent/component";
201
+ `,lines:i.map(s=>` "agents:${s}": lunoraAgentRuntimeFunctions.${s} as unknown as RegisteredLunoraFunction,`).join(`
202
+ `),prelude:`
203
+ /**
204
+ * The \`@lunora/agent\` runtime component — auto-registered because \`lunora/agents.ts\`
205
+ * declares agents. The durable loop dispatches the internal mutations; clients
206
+ * subscribe to the public queries (\`agents:agentMessages\` is the live thread view).
207
+ */
208
+ const lunoraAgentRuntimeFunctions = agentComponent().functions;
209
+ `}},ur=(e,t)=>{if(!e)return{importLine:"",lines:"",prelude:""};const n=t.find(r=>A(r.filePath)==="sandbox"&&r.exportName==="invoke");if(n)throw new Z("INTERNAL",`@lunora/codegen: "sandbox:invoke" is reserved for the batteries-included sandbox tool dispatcher (browserTool/containerTool) — rename the "invoke" export in ${n.filePath}`);return{importLine:`import { sandboxComponent } from "@lunora/agent/component";
210
+ `,lines:' "sandbox:invoke": lunoraSandbox.invoke as unknown as RegisteredLunoraFunction,',prelude:"\n/**\n * The `@lunora/agent` sandbox dispatcher — auto-registered because `lunora/`\n * imports a sandbox tool (`browserTool`/`containerTool`). The batteries-included\n * tools dispatch to this internal action, which runs on an action ctx carrying\n * `ctx.browser` + `ctx.containers` (the durable tool step itself has neither).\n */\nconst lunoraSandbox = sandboxComponent();\n"}},hr=(e,t)=>{if(e.length===0)return[];const n=Ot(t),r=[{args:{key:{kind:"string"},limit:{inner:{kind:"number"},kind:"optional"}},exportName:"agentMessages",filePath:"agents",kind:"query",returnType:"Record<string, unknown>[]"},{args:{decision:{kind:"union",members:[{kind:"literal",literalValue:'"approve"'},{kind:"literal",literalValue:'"reject"'}]},instanceId:{kind:"string"},note:{inner:{kind:"string"},kind:"optional"},threadKey:{kind:"string"},toolCallId:{kind:"string"}},exportName:"agentResolveApproval",filePath:"agents",kind:"mutation",returnType:"{ resolved: boolean }"},{args:{agent:{kind:"string"},input:{kind:"string"},threadKey:{kind:"string"},title:{inner:{kind:"string"},kind:"optional"}},exportName:"agentRun",filePath:"agents",kind:"mutation",returnType:"{ id: string; threadKey: string }"},{args:{key:{kind:"string"}},exportName:"agentState",filePath:"agents",kind:"query",returnType:"Record<string, unknown> | undefined"},{args:{key:{kind:"string"}},exportName:"agentThread",filePath:"agents",kind:"query",returnType:"Record<string, unknown> | undefined"}];for(const a of e)a.voice&&r.push({args:{threadKey:{kind:"string"}},exportName:`${a.exportName}Voice`,filePath:"agents",kind:"stream",returnType:"Record<string, unknown>"});return r.filter(a=>!n.has(a.exportName))},pr=(e,t)=>{if(e.length===0)return[];const n=new Set(t.filter(r=>A(r.filePath)==="mutators").map(r=>r.exportName));return e.filter(r=>!n.has(r.exportName)).map(r=>({args:r.args,exportName:r.exportName,filePath:r.filePath,kind:"mutation",returnType:r.returnType}))},mr=e=>{const t=e.filter(i=>i.stream);if(t.length===0)return{block:"",body:""};const n=Xe(t),r=n.map(([i,s])=>{const c=s.map(d=>{const h=E(d.chunkType??"unknown",d.filePath),g=E(ve(d.searchParams),d.filePath),y=E(ve(d.params),d.filePath);return` ${W(d.exportName)}: HttpStreamRef<${h}, ${g}, ${y}>;`}).join(`
211
+ `);return` ${W(A(i))}: {
212
+ ${c}
213
+ };`}).join(`
214
+ `),a=n.map(([i,s])=>{const c=s.map(d=>` ${X(d.exportName)}: { method: ${JSON.stringify(d.method)}, path: ${JSON.stringify(d.path)} },`).join(`
215
+ `);return` ${X(A(i))}: {
216
+ ${c}
217
+ },`}).join(`
218
+ `);return{block:`
219
+ /** This project's HTTP-SSE stream routes (\`httpRoute.<verb>(path).stream()\`), addressable as typed references for \`client.httpStream\` / \`useHttpStream\`. */
220
+ export interface HttpStreamsRef {
221
+ ${r}
222
+ }
223
+
224
+ export const httpStreams: HttpStreamsRef = {
225
+ ${a}
226
+ };
227
+ `,body:r}},ba=e=>{const{agents:t=[],functions:n,httpRoutes:r=[],mutators:a=[],useUmbrella:o=!1,workflows:i=[]}=e,s=se(o),c=[...n.filter(O=>O.visibility!=="internal"),...hr(t,n),...pr(a,n)],d=n.filter(O=>O.visibility==="internal"),h=mt(c),g=mt(d),y=mr(r),u=`${h}
228
+ ${g}
229
+ ${y.body}`,l=Ye(u),b=l.length>0?`
230
+ import type { ${l.join(", ")} } from "./dataModel.js";
231
+ `:"",R=h?`
232
+ ${h}
233
+ `:"",x=g?`
234
+ ${g}
235
+ `:"",v=ir(i,t),$=`export interface ApiTypes {${R}}
236
+
237
+ export const api = anyApi as unknown as ApiTypes;
238
+
239
+ /** Internal functions — callable only server-side via \`ctx.run*\`, never from a client. */
240
+ export interface InternalApiTypes {${x}}
241
+
242
+ export const internal = anyApi as unknown as InternalApiTypes;
243
+ ${v.block}${y.block}`,T=[...$.includes("FunctionReference<")?["FunctionReference"]:[],...y.block===""?[]:["HttpStreamRef"]],H=T.length>0?`import type { ${T.join(", ")} } from "${s.client}";
244
+ `:"";return Nt(`${S}import { anyApi } from "${s.client}";
245
+ ${H}${v.importLine}${b}
246
+ ${$}`,o)},ya=e=>e?`${S}import { createSeedClient as createSeedClientBase } from "@lunora/seed";
247
+ import type { SeedClient, SeedClientOptions } from "@lunora/seed";
248
+
249
+ import schema from "../schema.js";
250
+ import type { InsertModel } from "./dataModel.js";
251
+
252
+ /**
253
+ * Schema-aware seed client with this project's \`InsertModel\` and runtime schema
254
+ * pre-bound. Each table is a method; call it with a count, a range, explicit
255
+ * partial rows, or per-field overrides. Foreign keys connect to rows seeded
256
+ * earlier in the run, and FK-parent tables are seeded automatically.
257
+ * @example
258
+ * const seed = createSeedClient({ seed: 1 });
259
+ * const { users } = await seed.users(5);
260
+ * const { posts } = await seed.posts((x) => x([10, 20]));
261
+ */
262
+ export const createSeedClient = (options?: SeedClientOptions): SeedClient<InsertModel> => createSeedClientBase<InsertModel>(schema, options);
263
+ `:"",wa=(e,t,n=!1)=>{if(e.length===0||!t)return"";const r=se(n),a=c=>c.table===void 0?"Row":`Doc<${JSON.stringify(c.table)}> & Row`,o=e.map(c=>{f(c.exportName,"shape export name");const d=a(c),h=E(ve(c.args),c.filePath),g=Object.keys(c.args).length>0,y=g?` args: ${h};`:` args?: ${h};`,u=g?`(args: ${h}) => void`:`(args?: ${h}) => void`,l=`${c.exportName}CollectionOptions`;return`/** Options for the \`${c.exportName}\` shape binding. */
264
+ export interface ${Ge(l)} {
265
+ ${y}
266
+ /**
267
+ * Share the optimistic-overlay gate with other collections + mutators on this
268
+ * shard. Defaults to the shared per-shard registry, which is almost always what
269
+ * you want — pass one only to isolate this collection's gate.
270
+ */
271
+ checkpoints?: CheckpointRegistry;
272
+ /** The Lunora client to subscribe through. */
273
+ client: LunoraClient;
274
+ /** Row key extractor — defaults to \`row._id\`. Override when the app keys rows by a natural column. */
275
+ getKey?: (row: ${d}) => string;
276
+ /** \`"eager"\` starts syncing at creation; \`"lazy"\` (default) on the first subscriber. */
277
+ load?: "eager" | "lazy";
278
+ /** Notified when the underlying subscription errors; the collection always leaves \`loading\`. */
279
+ onError?: (error: SubscriptionError) => void;
280
+ /** Routes the subscription to a shard's DO. **Required for a \`.shardBy()\` table** — without it the watermark lands in the default bucket. */
281
+ shardKey?: string;
282
+ }
283
+
284
+ /**
285
+ * Collection options for the \`${c.exportName}\` replication shape: \`config\` for
286
+ * \`createCollection\`, plus the \`checkpoints\` registry to hand \`bindMutators\` and
287
+ * \`scope\` to re-point the subscription.
288
+ */
289
+ export const ${c.exportName}CollectionOptions = (options: ${Ge(l)}): LunoraCollectionOptions<${d}> =>
290
+ lunoraCollectionOptions<${d}>({
291
+ client: options.client,
292
+ ...(options.checkpoints === undefined ? {} : { checkpoints: options.checkpoints }),
293
+ ...(options.getKey === undefined ? {} : { getKey: options.getKey }),
294
+ ...(options.load === undefined ? {} : { load: options.load }),
295
+ ...(options.onError === undefined ? {} : { onError: options.onError }),
296
+ ...(options.shardKey === undefined ? {} : { shardKey: options.shardKey }),
297
+ shape: { args: options.args, name: ${JSON.stringify(c.exportName)}, ...(options.shardKey === undefined ? {} : { shardKey: options.shardKey }) },
298
+ });
299
+
300
+ /** Live collection for the \`${c.exportName}\` shape, with its sync controls. */
301
+ export const ${c.exportName}Collection = (
302
+ options: ${Ge(l)},
303
+ ): { checkpoints: CheckpointRegistry; collection: Collection<${d}, string>; scope: ${u} } => {
304
+ const { checkpoints, config, scope } = ${c.exportName}CollectionOptions(options);
305
+
306
+ return { checkpoints, collection: createCollection(config), scope };
307
+ };`}).join(`
308
+
309
+ `),i=Ye(o),s=i.length>0?`import type { ${i.join(", ")} } from "./dataModel.js";
310
+ `:"";return`${S}import type { LunoraClient, SubscriptionError } from "${r.client}";
311
+ import { lunoraCollectionOptions } from "@lunora/db/collections";
312
+ import type { CheckpointRegistry, LunoraCollectionOptions, Row } from "@lunora/db";
313
+ import type { Collection } from "@tanstack/db";
314
+ import { createCollection } from "@tanstack/db";
315
+ ${s}
316
+ ${o}
317
+ `},gr=(e,t)=>`lunora_${A(e)}_${String(t)}`,fr=`const callRegistered = async <R>(context: CallerCtx, functionPath: string, args: Record<string, unknown> | undefined): Promise<R> => {
318
+ const registered = LUNORA_FUNCTIONS[functionPath];
319
+
320
+ if (!registered) {
321
+ throw new LunoraError("FUNCTION_NOT_FOUND", \`function not registered: \${functionPath}\`);
322
+ }
323
+
324
+ // A mutation is routed through the caller's own \`ctx.runMutation\` rather than
325
+ // invoked directly, so \`createCaller(ctx).ns.someMutation()\` gets exactly what
326
+ // \`ctx.runMutation(api.ns.someMutation)\` gets: the BEGIN/COMMIT span (or the
327
+ // enclosing one, when the caller is already inside a transaction), the jobs it
328
+ // schedules held until that span commits, and the deferred object deletes
329
+ // flushed only once it has. Called straight, a mutation composed from an action
330
+ // or a stream had none of the three — its writes autocommitted one row at a
331
+ // time and its \`ctx.scheduler\` calls dispatched immediately, so a mid-handler
332
+ // throw left the earlier writes durable and the job already enqueued.
333
+ //
334
+ // The fallback covers a context that is not a shard dispatch (\`runMutation\` is
335
+ // installed by \`buildCtx\` on every kind but a query's TYPE omits it); there is
336
+ // no transaction to join in that case, so a direct call is all there is.
337
+ if (registered.kind === "mutation") {
338
+ const { runMutation } = context as { runMutation?: (reference: { __lunoraRef: string }, args: Record<string, unknown>) => Promise<unknown> };
339
+
340
+ if (typeof runMutation === "function") {
341
+ return (await runMutation.call(context, { __lunoraRef: functionPath }, args ?? {})) as R;
342
+ }
343
+ }
344
+
345
+ return (await registered.handler(context, args ?? {})) as R;
346
+ };`,br=(e,t,n=[],r=[])=>{const a=new Map,o=l=>{a.has(l)||a.set(l,gr(l,a.size))};for(const l of e)o(l.filePath);for(const l of t)o(l.filePath);for(const l of n)o(l.filePath);for(const l of r)o(l.filePath);const i=[...a.entries()].map(([l,b])=>{if(!Qn.test(l))throw new Z("INTERNAL",`@lunora/codegen: refusing to emit import for unsafe file path: ${JSON.stringify(l)}`);return`import * as ${b} from "../${l}.js";`}).join(`
347
+ `),s=e.map(l=>` "${A(l.filePath)}:${l.exportName}": ${a.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
348
+ `),c=t.map(l=>` ${JSON.stringify(l.id)}: ${a.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredDataMigration,`).join(`
349
+ `),d=n.map(l=>` "${A(l.filePath)}:${l.exportName}": ${a.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
350
+ `),h=r.map(l=>` "${l.exportName}": ${a.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredShape,`).join(`
351
+ `),g=n.map(l=>`${A(l.filePath)}:${l.exportName}`),y=[s,d].filter(l=>l.length>0).join(`
352
+ `),u=e.map(l=>{if(l.lifecycle||Object.keys(l.args).length===0)return;const b=Kn(l.args);return b===void 0?void 0:`installCompiledValidatorMap(${a.get(l.filePath)??""}.${l.exportName}.args, ${b});`}).filter(l=>l!==void 0).join(`
353
+ `);return{dispatchBody:y.length>0?`
354
+ ${y}
355
+ `:"",importBlock:i.length>0?`${i}
356
+
357
+ `:"",installBlock:u,migrationBody:c.length>0?`
358
+ ${c}
359
+ `:"",mutatorPaths:g,shapeBody:h.length>0?`
360
+ ${h}
361
+ `:""}},yr=e=>{const t=Xe(e),n=t.map(([a,o])=>{const i=o.map(s=>{const c=E(ve(s.args),s.filePath),d=c==="{}"?"?":"",h=E(At(s),s.filePath);return s.kind==="stream"?` ${s.exportName}: (args${d}: ${c}) => Promise<AsyncIterable<${h}>>;`:` ${s.exportName}: (args${d}: ${c}) => Promise<${h}>;`}).join(`
362
+ `);return` ${W(A(a))}: {
363
+ ${i}
364
+ };`}).join(`
365
+ `);return{implementation:t.map(([a,o])=>{const i=A(a),s=o.map(c=>` ${X(c.exportName)}: (args) => callRegistered(context, "${i}:${c.exportName}", args),`).join(`
366
+ `);return` ${X(i)}: {
367
+ ${s}
368
+ },`}).join(`
369
+ `),types:n}},wr=(e,t=[])=>{const n=new Set;for(const r of e.tables)for(const a of Object.values(r.shape)){const o=ie(a);o.kind==="storage"&&typeof o.bucket=="string"&&o.bucket!==""&&n.add(o.bucket)}for(const r of t)r!==""&&r!=="default"&&n.add(r);return["default",...[...n].toSorted((r,a)=>r.localeCompare(a))]},ka=({agents:e=[],containers:t=[],env:n,hasAccessFacade:r=!1,hasAi:a=!1,hasAnalytics:o=!1,hasBrowser:i=!1,hasVectors:s=!0,hasFlags:c=!1,hasHyperdrive:d=!1,hasImages:h=!1,hasKv:g=!1,hasNotify:y=!1,hasPayments:u=!1,hasPipelines:l=!1,hasR2sql:b=!1,hasX402:R=!1,identity:x,queues:v=[],schema:$,storageRuleBuckets:T=[],useUmbrella:H=!1,workflows:O=[]}={})=>{const D=se(H),k=wr($??{tables:[]},T).map(p=>JSON.stringify(p)).join(" | "),F=a?`import type { LunoraAi } from "@lunora/ai";
370
+ `:"",B=a?`
371
+ readonly ai: LunoraAi;`:"",le=u?`import type { LunoraPayment } from "@lunora/payment";
372
+ `:"",ce=u?`
373
+ readonly payments: LunoraPayment;`:"",ee=R?`import type { X402Pay } from "@lunora/x402/pay";
374
+ `:"",G=R?`
375
+ readonly x402: X402Pay;`:"",L=t.length>0?`import type { ContainerAccessor } from "@lunora/container";
376
+ `:"",te=t.length>0?`
377
+ readonly containers: {${t.map(p=>`
378
+ readonly ${p.exportName}: ContainerAccessor;`).join("")}
379
+ };`:"",w=[...a?[" /** Workers AI binding (the conventional `env.AI`), narrowing `ctx.ai`. */\n readonly AI?: unknown;"]:[],...t.map(p=>(f(p.bindingName,`container binding "${p.bindingName}"`),` /** Durable Object namespace for the \`${p.exportName}\` container. */
380
+ readonly ${p.bindingName}?: unknown;`)),...O.map(p=>(f(p.bindingName,`workflow binding "${p.bindingName}"`),` /** Workflow binding for the \`${p.exportName}\` workflow. */
381
+ readonly ${p.bindingName}?: unknown;`)),...v.map(p=>(f(p.bindingName,`queue binding "${p.bindingName}"`),` /** Queue producer binding for the \`${p.exportName}\` queue. */
382
+ readonly ${p.bindingName}?: unknown;`)),...e.map(p=>(f(p.bindingName,`agent binding "${p.bindingName}"`),` /** Workflow binding for the \`${p.exportName}\` agent. */
383
+ readonly ${p.bindingName}?: unknown;`))].join(`
384
+ `),Oe=`
385
+
386
+ /**
387
+ * This project's Cloudflare bindings, as far as codegen can discover them from
388
+ * \`lunora/\` source — the container/workflow Durable Object namespaces and the
389
+ * conventional Workers AI binding. Bindings the config layer reconciles from
390
+ * user-named wrangler config (R2/KV/D1/\`services\`/queues/…) aren't statically
391
+ * visible here, so the open index signature keeps them reachable (cast at the
392
+ * use site). Service bindings (\`env.<SERVICE>.fetch(...)\` / RPC stubs) live
393
+ * under this signature too — Lunora can't type a third-party worker's RPC
394
+ * surface, so treat them as \`Fetcher\`/\`Service<unknown>\` and cast.
395
+ */
396
+ export interface CloudflareBindings {
397
+ readonly [binding: string]: unknown;${w?`
398
+ ${w}`:""}
399
+ }
400
+
401
+ /** Alias for {@link CloudflareBindings} — the typed shape of \`env\`. */
402
+ export type Env = CloudflareBindings;`,q=(p,Ue,Ne)=>{const ae=_n.get(p);if(ae!==void 0&&ae.tier!==Ne)throw new Z("INTERNAL",`@lunora/codegen: capability "${p}" declares tier "${ae.tier}" in SERVER_CTX_FIELDS but is emitted onto the "${Ne}" context — update whichever is wrong.`);return Ue?ae?.field??"":""},de=q("kv",g,"every"),ue=q("analytics",o,"every"),Ie=q("hyperdrive",d,"action"),Ce=q("browser",i,"action"),De=q("images",h,"action"),he=q("pipelines",l,"action"),z=q("r2sql",b,"action"),P=s&&($?.vectorIndexes.length??0)>0,J=P?' | "vectors"':"",ne=P?`
403
+ readonly vectors: VectorSearch<VectorIndexName>;`:"",pe=P?`
404
+ readonly vectors: VectorSearchReader<VectorIndexName>;`:"",Y=P?`import type { VectorSearch, VectorSearchReader } from "${D.server}";
405
+ import type { VectorIndexName } from "./dataModel.js";
406
+ `:"",U=r?`
407
+ /** Verified Cloudflare Access identity — a synchronous facade over the resolved claims (email / groups / hasGroup / claims). Anonymous when no Access token is present. */
408
+ readonly access: import("@lunora/cloudflare-access/context").AccessFacade;`:"",K=c?`
409
+ /** Feature-flag evaluation (OpenFeature). Reads are memoized per request; evaluations never throw — a provider error resolves to the supplied default. */
410
+ readonly flags: import("${D.flags}").LunoraFlags;`:"",Q=y?`
411
+ /** Multi-channel notifications (@lunora/notify): send / chat / inApp / webhook plus the push device sub-facade. Sends are external I/O — confine them to action handlers. */
412
+ readonly notify: import("@lunora/notify").LunoraNotify;
413
+ /** Device push sub-facade — the same object as ctx.notify.push (register / send / broadcast). Sends belong in action handlers. */
414
+ readonly push: import("@lunora/notify").LunoraPush;`:"",j=O.length>0,re=j?`import type { WorkflowHandle } from "@lunora/workflow";
415
+ import type * as lunoraWorkflowDefinitions from "../workflows.js";
416
+ `:"",Le=j?`
417
+
418
+ /** Params type carried by a \`defineWorkflow\` definition (its phantom \`__params\`). */
419
+ type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params }
420
+ ? unknown extends Params
421
+ ? Record<string, unknown>
422
+ : NonNullable<Params>
423
+ : Record<string, unknown>;
424
+
425
+ /** This project's declared workflows, addressable from \`ctx.workflows\` by their \`lunora/workflows.ts\` export name. */
426
+ export interface LunoraWorkflows {
427
+ ${O.map(p=>` get(name: ${JSON.stringify(p.exportName)}): WorkflowHandle<WorkflowParamsOf<typeof lunoraWorkflowDefinitions.${p.exportName}>>;`).join(`
428
+ `)}
429
+ }`:"",Se=j?' | "workflows"':"",Re=j?`
430
+ readonly workflows: LunoraWorkflows;`:"",me=v.length>0,Ee=me?`import type { QueueProducer } from "@lunora/queue";
431
+ import type * as lunoraQueueDefinitions from "../queues.js";
432
+ `:"",_e=me?`
433
+
434
+ /** Message body type carried by a \`defineQueue\` definition (its phantom \`__lunoraBody\`). */
435
+ type QueueBodyOf<Definition> = Definition extends { __lunoraBody?: infer Body } ? (unknown extends Body ? unknown : NonNullable<Body>) : unknown;
436
+
437
+ /** This project's declared queues, addressable from \`ctx.queues\` by their \`lunora/queues.ts\` export name. */
438
+ export interface LunoraQueues {
439
+ ${v.map(p=>` readonly ${p.exportName}: QueueProducer<QueueBodyOf<typeof lunoraQueueDefinitions.${p.exportName}>>;`).join(`
440
+ `)}
441
+ }`:"",$e=me?`
442
+ readonly queues: LunoraQueues;`:"",ge=e.length>0,Pe=ge?`import type { AgentHandle } from "@lunora/agent";
443
+ `:"",Me=ge?`
444
+
445
+ /** This project's declared agents, addressable from \`ctx.agents\` by their \`lunora/agents.ts\` export name. */
446
+ export interface LunoraAgents {
447
+ ${e.map(p=>(f(p.exportName,`agent export "${p.exportName}"`),` readonly ${p.exportName}: AgentHandle;`)).join(`
448
+ `)}
449
+ }`:"",Te=ge?`
450
+ readonly agents: LunoraAgents;`:"",Fe=x?`import type { InferIdentity } from "${D.server}";
451
+ import type * as lunoraIdentityContract from "../identity.js";
452
+ `:"",Be=x?`
453
+
454
+ /** This app's declared identity claim contract (\`defineIdentity\` in \`lunora/identity.ts\`) — the typed shape of \`ctx.auth.getIdentity()\`, the RLS policy \`ctx.auth.identity\`, and the \`authorizeShard\`/\`authorizeFanOut\` identity argument. */
455
+ export type Identity = InferIdentity<typeof lunoraIdentityContract.${x.exportName}>;
456
+
457
+ /** \`ctx.auth\` narrowed so \`getIdentity()\` resolves the declared {@link Identity} contract instead of the untyped claim bag. */
458
+ type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: () => Promise<Identity | null> };`:"",fe=x?' | "auth"':"",be=x?`
459
+ readonly auth: NarrowedAuth;`:"",qe=x?", Identity":"",je=n?`import type * as lunoraEnvContract from "../env.js";
460
+ `:"",We=n?`
461
+
462
+ /** This app's declared env contract (\`defineEnv\` in \`lunora/env.ts\`) — the validated, coercion-aware shape of \`ctx.env\`. */
463
+ export type LunoraEnv = ReturnType<typeof lunoraEnvContract.${n.exportName}>;`:"",ye=n?' | "env"':"",we=n?"\n /** Validated, typed environment declared by `defineEnv` in `lunora/env.ts` — parsed & coercion-aware config values (`ctx.env.STRIPE_KEY`); a missing or invalid value throws at read time. */\n readonly env: LunoraEnv;":"";return`${S}import { createPolicyDsl, defineMutator as defineMutatorBase, initLunora, v as vBase } from "${D.server}";
464
+ import type {
465
+ ActionBuilder,
466
+ ActionCtx as ActionCtxBase,
467
+ ColumnValidator,
468
+ DatabaseReader,
469
+ DatabaseWriter,
470
+ EmptyArgs,
471
+ InternalActionBuilder,
472
+ InternalMutationBuilder,
473
+ InternalQueryBuilder,
474
+ MutationBuilder,
475
+ MutationCtx as MutationCtxBase,
476
+ MutationStorage,
477
+ MutatorDefinition,
478
+ QueryBuilder,
479
+ QueryCtx as QueryCtxBase,
480
+ ReadOnlyStorage,
481
+ RegisteredMutator,
482
+ Storage as StorageBase,
483
+ TableReader,
484
+ Validator,
485
+ } from "${D.server}";
486
+
487
+ import type {
488
+ DatabaseReaderFacade as DatabaseReaderFacadeOf,
489
+ DatabaseWriterFacade as DatabaseWriterFacadeOf,
490
+ LoadWith as LoadWithOf,
491
+ TableReaderFacade as TableReaderFacadeOf,
492
+ TableWriterFacade as TableWriterFacadeOf,
493
+ WithArg as WithArgOf,
494
+ } from "${D.serverDataModel}";
495
+
496
+ export type {
497
+ AggregateOp,
498
+ GroupByEntry,
499
+ OrderBy,
500
+ QueryArgs,
501
+ QueryPage,
502
+ RankPage,
503
+ RankResult,
504
+ RestrictableQueryOptions,
505
+ RestrictableQueryOptionsOf,
506
+ SearchFilterBuilder,
507
+ SearchReader,
508
+ TableAggregateOptions,
509
+ TableAggregateOptionsOf,
510
+ TableGroupByOptions,
511
+ TableGroupByOptionsOf,
512
+ TableRankOptions,
513
+ TableRankPageOptions,
514
+ Where,
515
+ WhereOf,
516
+ WhereOperators,
517
+ } from "${D.serverDataModel}";
518
+
519
+ import type { DataModel, Doc, GeoIndexNamesByTable, Id as IdOfTable, IndexNamesByTable, Insert, InsertModel, RankIndexNamesByTable, Relations, SearchIndexNamesByTable, TableName } from "./dataModel.js";
520
+ ${Y}${F}${le}${ee}${L}${re}${Ee}${Pe}${Fe}${je}
521
+ export type { AppTableName, DataModel, Doc, Id, TableName } from "./dataModel.js";
522
+
523
+ /**
524
+ * The query-DSL bindings: \`@lunora/server/data-model\`'s generics parameterized
525
+ * over this project's \`DataModel\` / \`InsertModel\` / \`Relations\` / index maps.
526
+ *
527
+ * They live here rather than in \`dataModel.ts\` because they are the half that
528
+ * needs the SERVER package. Keeping \`dataModel.ts\` free of it lets a sibling
529
+ * package — a web app, another Worker — compile \`api.ts\` for its \`Doc\`/\`Id\`
530
+ * types without installing a server dependency to do it.
531
+ */
532
+
533
+ /** The \`with\` argument for table \`T\` — see \`@lunora/server/data-model\`. */
534
+ export type WithArg<T extends keyof DataModel> = WithArgOf<DataModel, Relations, T>;
535
+
536
+ /** \`Doc<T>\` narrowed to exactly the relations requested in the with-arg \`W\`. */
537
+ export type LoadWith<T extends keyof DataModel, W> = LoadWithOf<DataModel, Relations, T, W>;
538
+
539
+ /** Read-only typed table accessor exposed on \`QueryCtx.db.<table>\`. */
540
+ export type TableReaderFacade<T extends keyof DataModel> = TableReaderFacadeOf<DataModel, Relations, RankIndexNamesByTable, SearchIndexNamesByTable, T, GeoIndexNamesByTable>;
541
+
542
+ /** Read-write typed table accessor exposed on \`MutationCtx.db.<table>\` / \`ActionCtx.db.<table>\`. */
543
+ export type TableWriterFacade<T extends keyof DataModel> = TableWriterFacadeOf<DataModel, InsertModel, Relations, RankIndexNamesByTable, SearchIndexNamesByTable, T, GeoIndexNamesByTable>;
544
+
545
+ /** Per-table read facade — \`ctx.db.<table>\` on a \`QueryCtx\`. */
546
+ export type DatabaseReaderFacade = DatabaseReaderFacadeOf<DataModel, Relations, RankIndexNamesByTable, SearchIndexNamesByTable, GeoIndexNamesByTable>;
547
+
548
+ /** Per-table read-write facade — \`ctx.db.<table>\` on a \`MutationCtx\` / \`ActionCtx\`. */
549
+ export type DatabaseWriterFacade = DatabaseWriterFacadeOf<DataModel, InsertModel, Relations, RankIndexNamesByTable, SearchIndexNamesByTable, GeoIndexNamesByTable>;
550
+
551
+ /** Insert builder returned by \`ctx.orm.insert(table)\`. */
552
+ export interface OrmInsertBuilder<T extends keyof DataModel> {
553
+ values: (values: Insert<T>) => Promise<IdOfTable<T>>;
554
+ }
555
+
556
+ /** Replace builder returned by \`ctx.orm.replace(table, id)\` — swaps the whole document. */
557
+ export interface OrmReplaceBuilder<T extends keyof DataModel> {
558
+ with: (values: Insert<T>) => Promise<void>;
559
+ }
560
+
561
+ /** Update builder returned by \`ctx.orm.update(table, id)\` — patches the named fields. */
562
+ export interface OrmUpdateBuilder<T extends keyof DataModel> {
563
+ set: (values: Partial<Insert<T>>) => Promise<void>;
564
+ }
565
+
566
+ /** Read-only ORM surface — \`ctx.orm\` on a \`QueryCtx\`. Mirrors \`ctx.db\` reads under a kitcn-style \`query\` namespace. */
567
+ export interface OrmReader {
568
+ query: DatabaseReaderFacade;
569
+ }
570
+
571
+ /** Read-write ORM surface — \`ctx.orm\` on a \`MutationCtx\` / \`ActionCtx\`. Writes are addressed by id, like \`ctx.db\`. */
572
+ export interface OrmWriter extends OrmReader {
573
+ delete: <T extends keyof DataModel>(table: T, id: IdOfTable<T>) => Promise<void>;
574
+ insert: <T extends keyof DataModel>(table: T) => OrmInsertBuilder<T>;
575
+ replace: <T extends keyof DataModel>(table: T, id: IdOfTable<T>) => OrmReplaceBuilder<T>;
576
+ update: <T extends keyof DataModel>(table: T, id: IdOfTable<T>) => OrmUpdateBuilder<T>;
577
+ }
578
+
579
+ /** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
580
+ export type StorageBucketName = ${k};${Oe}${Le}${_e}${Me}${Be}${We}
581
+
582
+ /**
583
+ * Project-typed contexts. The base contexts from \`@lunora/server\` are
584
+ * untyped against the schema; here \`db\` is widened to the generated per-table
585
+ * facade (\`ctx.db.<table>.findMany(...)\`) while keeping the legacy structural
586
+ * \`db.get\`/\`db.query\` surface for back-compat, and \`storage\` is narrowed so
587
+ * \`ctx.storage.bucket(name)\` autocompletes the declared buckets.
588
+ */
589
+ /**
590
+ * The Convex-style fluent reader \`ctx.db.query(table)\`, bound to this schema:
591
+ * a table-name literal narrows the row type, so \`.collect()\` / \`.first()\` /
592
+ * \`.take()\` (and the \`.withIndex()\` / \`.filter()\` / \`.order()\` chain) resolve
593
+ * as \`Doc<table>\` with no \`as unknown as Doc<...>\` casts. The per-table accessor
594
+ * \`ctx.db.<table>\` stays available alongside it via the facade.
595
+ *
596
+ * Intersected with the wide \`(string) => TableReader\` signature so the bound
597
+ * \`ctx.db\` is still structurally assignable to schema-agnostic consumers that
598
+ * call \`db.query(someString)\` (e.g. \`@lunora/ratelimit\`'s \`createDbStore\`).
599
+ *
600
+ * The three index-name unions are what make \`.withIndex("by_TYPO")\` a compile
601
+ * error: each resolves to the table's declared names, or \`never\` when it
602
+ * declares none of that kind. Without them a renamed or dropped index left
603
+ * every call site typechecking, and the query either threw at runtime or
604
+ * degraded silently to a full table scan.
605
+ */
606
+ type TypedTableQuery = (<T extends TableName>(
607
+ table: T,
608
+ ) => TableReader<Doc<T>, IndexNamesByTable[T], SearchIndexNamesByTable[T], GeoIndexNamesByTable[T]>) &
609
+ ((table: string) => TableReader);
610
+
611
+ /**
612
+ * The point read \`ctx.db.get(id)\`, bound to this schema: an \`Id<"table">\`
613
+ * carries its table name, so the resolved document is typed \`Doc<table>\` (or
614
+ * \`null\` when absent) with no \`as Doc<...>\` cast. Mirrors {@link TypedTableQuery}.
615
+ */
616
+ type TypedTableGet = <T extends TableName>(id: IdOfTable<T>) => Promise<Doc<T> | null>;
617
+
618
+ /**
619
+ * Resolves the \`asId\` table argument: a literal in {@link TableName} passes through, a
620
+ * genuinely-wide \`string\` (a computed name) passes through, and any OTHER literal
621
+ * collapses to \`never\` so it fails to typecheck.
622
+ *
623
+ * The \`string extends T\` arm is what distinguishes "the caller passed a computed
624
+ * string" from "the caller passed a misspelled literal" — only the wide \`string\` type
625
+ * is a supertype of \`string\`.
626
+ */
627
+ type AsIdTable<T extends string> = T extends TableName ? T : string extends T ? T : never;
628
+
629
+ /**
630
+ * The id parse boundary \`ctx.db.asId(table, id)\`, bound to this schema: a misspelled
631
+ * table literal is a compile error rather than a call that hands back a confidently-
632
+ * branded id for a table that does not exist. Mirrors {@link TypedTableQuery} /
633
+ * {@link TypedTableGet}.
634
+ *
635
+ * Deliberately NOT an intersection with a wide \`(string, string) => string\` overload:
636
+ * overload resolution would fall through to it for a bad literal, silently restoring
637
+ * the very typo hole this narrowing exists to close. {@link AsIdTable} instead keeps a
638
+ * computed table name working — and keeps \`ctx.db\` structurally assignable to
639
+ * schema-agnostic consumers — without accepting an invalid literal.
640
+ */
641
+ type TypedAsId = <T extends string>(tableName: AsIdTable<T>, id: string) => IdOfTable<T & TableName>;
642
+
643
+ export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${J}${fe}${ye}> {
644
+ readonly db: Omit<DatabaseReader, "asId" | "query" | "get"> & DatabaseReaderFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
645
+ readonly orm: OrmReader;
646
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${pe}${U}${de}${K}${Q}${ue}${we}${be}
647
+ }
648
+
649
+ export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${J}${Se}${fe}${ye}> {
650
+ readonly db: Omit<DatabaseWriter, "asId" | "query" | "get"> & DatabaseWriterFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
651
+ readonly orm: OrmWriter;
652
+ readonly storage: MutationStorage<StorageBucketName>;${ne}${U}${de}${K}${Q}${ue}${we}${Re}${$e}${Te}${be}
653
+ }
654
+
655
+ export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${J}${Se}${fe}${ye}> {
656
+ readonly db: Omit<DatabaseWriter, "asId" | "query" | "get"> & DatabaseWriterFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
657
+ readonly orm: OrmWriter;
658
+ readonly storage: StorageBase<StorageBucketName>;${ne}${U}${B}${ce}${G}${te}${de}${K}${Q}${Ie}${Ce}${De}${ue}${he}${z}${we}${Re}${$e}${Te}${be}
659
+ }
660
+
661
+ /**
662
+ * Procedure builders bound to this project's typed contexts. Chain
663
+ * \`.input({...})\` to declare args, \`.use(middleware)\` for rate limiting / RLS /
664
+ * masking (each middleware may refine \`ctx\`), then the terminal
665
+ * \`.query()\` / \`.mutation()\` / \`.action()\` with a \`({ ctx, args }) => ...\`
666
+ * handler. \`.output(validator)\` and (queries) \`.stream()\` are also available.
667
+ */
668
+ const lunoraBuilders = initLunora.dataModel<DataModel>().create();
669
+
670
+ /** \`query\` builder bound to this project's typed {@link QueryCtx}. */
671
+ export const query = lunoraBuilders.query as unknown as QueryBuilder<QueryCtx, EmptyArgs>;
672
+
673
+ /** \`mutation\` builder bound to this project's typed {@link MutationCtx}. */
674
+ export const mutation = lunoraBuilders.mutation as unknown as MutationBuilder<MutationCtx, EmptyArgs>;
675
+
676
+ /** \`action\` builder bound to this project's typed {@link ActionCtx}. */
677
+ export const action = lunoraBuilders.action as unknown as ActionBuilder<ActionCtx, EmptyArgs>;
678
+
679
+ /** \`internalQuery\` builder bound to this project's typed {@link QueryCtx} — never exposed on \`api\`. */
680
+ export const internalQuery = lunoraBuilders.internalQuery as unknown as InternalQueryBuilder<QueryCtx, EmptyArgs>;
681
+
682
+ /** \`internalMutation\` builder bound to this project's typed {@link MutationCtx} — never exposed on \`api\`. */
683
+ export const internalMutation = lunoraBuilders.internalMutation as unknown as InternalMutationBuilder<MutationCtx, EmptyArgs>;
684
+
685
+ /** \`internalAction\` builder bound to this project's typed {@link ActionCtx} — never exposed on \`api\`. */
686
+ export const internalAction = lunoraBuilders.internalAction as unknown as InternalActionBuilder<ActionCtx, EmptyArgs>;
687
+
688
+ /**
689
+ * \`defineMutator\` bound to this project's typed {@link MutationCtx} — declare
690
+ * custom mutators (\`lunora/mutators.ts\`) with it instead of importing from
691
+ * \`${D.server}\`, and the authoritative \`server\` impl's \`ctx\` carries the
692
+ * schema-typed \`ctx.db\` (\`ctx.db.query("nodes")\` resolves \`Doc<"nodes">\`,
693
+ * \`ctx.db.patch(id, …)\` takes an \`Id<"nodes">\`) with no hand-written ctx type and
694
+ * no \`as unknown as\` cast. Runtime-identical to the package export; only the
695
+ * context type narrows, so discovery finds a mutator authored either way.
696
+ * @example
697
+ * export const setText = defineMutator({
698
+ * args: { id: v.id("nodes"), text: v.string() },
699
+ * server: async (ctx, args) => { await ctx.db.patch(args.id, { text: args.text }); },
700
+ * });
701
+ */
702
+ export const defineMutator = defineMutatorBase as unknown as <Args extends Record<string, Validator> = Record<string, Validator>, ClientTx = unknown, R = unknown>(
703
+ definition: MutatorDefinition<Args, MutationCtx, ClientTx, R>,
704
+ ) => RegisteredMutator<Args, MutationCtx, ClientTx, R>;
705
+
706
+ /**
707
+ * \`definePolicy\` bound to THIS schema's {@link DataModel} + {@link Relations}.
708
+ * Import it from here (\`./_generated/server\`) instead of \`@lunora/server\` to get
709
+ * a relation-aware RLS authoring surface: \`table\` autocompletes to a real table
710
+ * name and the \`when\` predicate type-checks against the table's columns **and**
711
+ * its declared relations — \`{ author: { is: {...} } }\`, \`{ posts: { some: {...} } }\`,
712
+ * etc. (the \`@lunora/do\` pre-resolver resolves relation predicates on reads).
713
+ * Runtime-identical to \`@lunora/server\`'s \`definePolicy\`; only the types narrow,
714
+ * so the \`rls()\` chain discovers a policy authored either way the same.
715
+ */
716
+ export const definePolicy = createPolicyDsl<DataModel, Relations${qe}>();
717
+
718
+ /**
719
+ * The validator builder \`v\`, with \`v.id(...)\` constrained to THIS schema's
720
+ * table names — so the argument autocompletes and returns a precise
721
+ * \`Id<"table">\`. Identical to \`@lunora/server\`'s \`v\` at runtime; the only
722
+ * difference is the tightened \`id\` type.
723
+ *
724
+ * Import \`v\` from here (\`./_generated/server\`) in your queries/mutations/actions
725
+ * to get table-name autocomplete. \`lunora/schema.ts\` can't use it — it is the
726
+ * file that defines the table names, so they aren't known yet there; pass the
727
+ * names as string literals and the generated \`Id<"table">\` types still catch a
728
+ * typo wherever the id is used.
729
+ */
730
+ export const v = vBase as unknown as Omit<typeof vBase, "id"> & {
731
+ id: <T extends TableName>(table: T) => ColumnValidator<IdOfTable<T>, IdOfTable<T>>;
732
+ };
733
+ `},kr=e=>{const t={connect:[],disconnect:[],init:[],reactor:[],whisper:[]};for(const n of e)n.lifecycle&&t[n.lifecycle].push(`${A(n.filePath)}:${n.exportName}`);return t},xa=e=>{const{agents:t=[],functions:n,migrations:r=[],mutators:a=[],shapes:o=[],useUmbrella:i=!1,usesSandbox:s=!1}=e,c=n.length>0,d=se(i),{dispatchBody:h,importBlock:g,installBlock:y,migrationBody:u,mutatorPaths:l,shapeBody:b}=br(n,r,a,o),R=dr(t,n),x=ur(s,n),v=[R.lines,x.lines].filter(w=>w.length>0).join(`
734
+ `);let $=h;v.length>0&&($=h.length>0?`${h.trimEnd()}
735
+ ${v}
736
+ `:`
737
+ ${v}
738
+ `);const T=kr(n),H=o.length>0?`import type { RegisteredShape } from "${d.server}";
739
+ `:"",O=o.length>0?`
740
+ /**
741
+ * Replication-shape registry — one entry per \`defineShape\` in \`lunora/shapes.ts\`.
742
+ * The generated ShardDO's \`resolveShape\` override looks a shape up by name and
743
+ * evaluates its trusted \`compileWhere(ctx, args)\` to authorize + scope a
744
+ * \`shape_subscribe\` (reads-as-permissions).
745
+ */
746
+ export const LUNORA_SHAPES: Record<string, RegisteredShape> = {${b}};
747
+ `:"",D=l.length>0?`
748
+ /**
749
+ * Custom-mutator function paths — the \`LUNORA_FUNCTIONS\` keys the generated
750
+ * ShardDO's \`isCustomMutator\` override routes through the client-watermark push
751
+ * protocol (\`x-lunora-client-id\`/\`x-lunora-client-seq\` ordering).
752
+ */
753
+ export const LUNORA_MUTATOR_PATHS: ReadonlySet<string> = new Set([${l.map(w=>JSON.stringify(w)).join(", ")}]);
754
+ `:"",k=y.length>0?`import { DEFER_VALIDATION as DEFER, installCompiledValidatorMap } from "${d.values}";
755
+ `:"",F=y.length>0?`
756
+ /**
757
+ * AOT-compiled argument validators (Worker-safe, no \`eval\`). Each is installed
758
+ * onto its function's live \`.args\` object and consulted by the interpreted
759
+ * parser as a zero-allocation fast path; anything it can't model is deferred.
760
+ */
761
+ ${y}
762
+ `:"",B=yr(n),le=B.types?`
763
+ ${B.types}
764
+ `:"",ce=B.implementation?`
765
+ ${B.implementation}
766
+ `:"",ee=Ye(B.types),G=ee.length>0?`import type { ${ee.join(", ")} } from "./dataModel.js";
767
+ `:"",L=c?"context":"_context",te=c?`${fr}
768
+
769
+ `:"";return Nt(`${S}${g}${R.importLine}${x.importLine}${k}${H}import { LunoraError } from "${d.server}";
770
+ import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
771
+ ${G}
772
+ /**
773
+ * Single registered function, narrowed to the shape \`handleRpc\` needs.
774
+ * The real argument validators / return types are checked elsewhere — at
775
+ * the dispatch site we erase to \`unknown\` so a single map can host every
776
+ * registered function.
777
+ */
778
+ export interface RegisteredLunoraFunction {
779
+ kind: "action" | "mutation" | "query" | "stream";
780
+ args: Record<string, unknown>;
781
+ /**
782
+ * Present on a \`"stream"\` declared \`durable\`: its run is persisted and
783
+ * survives the socket that opened it. Absent on every other kind, and on an
784
+ * ephemeral stream.
785
+ */
786
+ durable?: { ttlMs?: number };
787
+ /**
788
+ * For \`"action" | "mutation" | "query"\` the handler is awaited and its result returned.
789
+ * For \`"stream"\` the handler returns an \`AsyncIterable\` synchronously and takes an
790
+ * \`AbortSignal\` as a third argument — the runtime drives it frame-by-frame.
791
+ */
792
+ handler: ((context: unknown, args: Record<string, unknown>) => Promise<unknown> | unknown) | ((context: unknown, args: Record<string, unknown>, signal?: AbortSignal) => AsyncIterable<unknown>);
793
+ /**
794
+ * The lifecycle moment a hook fires on, when this registration came from
795
+ * \`onConnect\`/\`onDisconnect\`/\`onShardInit\`/\`onQueryChange\`/\`onWhisper\`.
796
+ * Read at dispatch to decide whether the function runs system-trusted:
797
+ * \`init\` and \`reactor\` have no caller identity, so RLS has no user to scope
798
+ * to. \`whisper\` does have one — it runs under the asking socket's identity.
799
+ */
800
+ lifecycle?: "connect" | "disconnect" | "init" | "reactor" | "whisper";
801
+ /**
802
+ * Hoisted by the builder when the \`.use()\` chain carries a step with a
803
+ * per-dispatch effect — \`rateLimit(...)\` consuming budget, a single-use
804
+ * captcha token being burned. Read by \`isCacheableQuery\`: the chain runs
805
+ * inside the dispatch callback, and a reactive-cache HIT skips that
806
+ * callback, so such a query must never be memoized.
807
+ */
808
+ perDispatch?: boolean;
809
+ /** \`"internal"\` functions are rejected on the external RPC path; absence === public. */
810
+ visibility?: "internal" | "public";
811
+ /**
812
+ * \`.x402({ price })\` tag on a paid public procedure. The origin worker
813
+ * paywalls it; the shard refuses to subscribe it (\`isPaidFunction\`).
814
+ */
815
+ x402?: { readonly price: number | string };
816
+ }
817
+ ${R.prelude}${x.prelude}
818
+ /**
819
+ * Static dispatch table. The key matches the \`__lunoraRef\` the client
820
+ * emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
821
+ */
822
+ export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${$}};
823
+ ${F}${O}${D}
824
+ /**
825
+ * Lifecycle manifest: the function paths the generated ShardDO dispatches when a
826
+ * client's WebSocket connects (\`connect\`) or disconnects (\`disconnect\`), once
827
+ * per Durable Object instance before any handler runs (\`init\`), after a
828
+ * write flush when a watched read's result changed (\`reactor\`), and before a
829
+ * socket joins or broadcasts to a whisper topic (\`whisper\`). Each path also
830
+ * resolves through {@link LUNORA_FUNCTIONS}. The socket-scoped moments run under
831
+ * the socket's verified identity; \`init\` and \`reactor\` have no caller, so they
832
+ * run anonymous — all via system dispatch.
833
+ */
834
+ export const LUNORA_LIFECYCLE_HOOKS: {
835
+ connect: readonly string[];
836
+ disconnect: readonly string[];
837
+ init: readonly string[];
838
+ reactor: readonly string[];
839
+ whisper: readonly string[];
840
+ } = {
841
+ connect: [${T.connect.map(w=>JSON.stringify(w)).join(", ")}],
842
+ disconnect: [${T.disconnect.map(w=>JSON.stringify(w)).join(", ")}],
843
+ init: [${T.init.map(w=>JSON.stringify(w)).join(", ")}],
844
+ reactor: [${T.reactor.map(w=>JSON.stringify(w)).join(", ")}],
845
+ whisper: [${T.whisper.map(w=>JSON.stringify(w)).join(", ")}],
846
+ };
847
+
848
+ /**
849
+ * Resolve and invoke a registered function from an external caller. Throws a
850
+ * LunoraError-shaped object (404) when the path is unknown — the runtime's
851
+ * structural error mapper turns that into the right HTTP status. Internal
852
+ * functions are treated as not-found so their existence never leaks to clients.
853
+ */
854
+ export const dispatchLunoraFunction = async (functionPath: string, context: unknown, args: Record<string, unknown>): Promise<unknown> => {
855
+ const registered = LUNORA_FUNCTIONS[functionPath];
856
+
857
+ if (!registered || registered.visibility === "internal") {
858
+ throw new LunoraError("FUNCTION_NOT_FOUND", \`function not registered: \${functionPath}\`);
859
+ }
860
+
861
+ return registered.handler(context, args);
862
+ };
863
+
864
+ /**
865
+ * A handler context accepted by {@link createCaller}. The runtime context the
866
+ * shard DO builds is uniform across kinds, so any handler's \`ctx\` works here.
867
+ */
868
+ export type CallerCtx = ActionCtx | MutationCtx | QueryCtx;
869
+
870
+ /**
871
+ * Typed, in-process server-to-server caller. Every registered function — public
872
+ * *and* internal — is reachable; each call dispatches against the same shard
873
+ * with the supplied \`context\`, exactly like \`ctx.runQuery\`/\`runMutation\`/
874
+ * \`runAction\` but without re-stating the function path.
875
+ */
876
+ export interface Caller {${le}}
877
+
878
+ ${te}/** Build a {@link Caller} bound to \`context\` (typically a handler's \`ctx\`). */
879
+ export const createCaller = (${L}: CallerCtx): Caller => ({${ce}});
880
+
881
+ /**
882
+ * Single registered data migration, narrowed to the shape the per-shard runner
883
+ * consumes. \`up\`/\`down\` are erased to a structural transform; the authoring
884
+ * validation lives in \`defineMigration\`.
885
+ */
886
+ export interface RegisteredDataMigration {
887
+ batchSize?: number;
888
+ down?: (document: Record<string, unknown>) => Record<string, unknown> | undefined;
889
+ id: string;
890
+ table: string;
891
+ up: (document: Record<string, unknown>) => Record<string, unknown> | undefined;
892
+ }
893
+
894
+ /**
895
+ * Registry of online data migrations keyed by \`defineMigration\`'s \`id\`. The
896
+ * shard DO's admin RPC and the CLI resolve migrations to run through this map.
897
+ */
898
+ export const LUNORA_MIGRATIONS: Record<string, RegisteredDataMigration> = {${u}};
899
+ `,i)},xr=e=>{const t={};for(const n of e.tables){const r={};for(const[a,o]of Object.entries(n.shape)){const i=ie(o);i.kind==="id"&&i.tableName!==void 0&&(r[a]=i.tableName)}Object.keys(r).length>0&&(t[n.name]=r)}return t},vr=e=>{const t={};for(const n of e.tables){const r=[];for(const[a,o]of Object.entries(n.shape))ie(o).kind==="storage"&&r.push(a);r.length>0&&(t[n.name]=r)}return t},Sr=e=>{const t={};for(const n of e.tables){const r=[...n.indexes.map(a=>({fields:[...a.fields],name:a.name,type:"index",...a.unique===!0?{unique:!0}:{}})),...n.searchIndexes.map(a=>({fields:[a.field,...a.filterFields??[]],name:a.name,type:"search"})),...(n.geoIndexes??[]).map(a=>({fields:[a.field],name:a.name,type:"geo"})),...n.rankIndexes.map(a=>({fields:a.sortBy.map(o=>o.field),name:a.name,type:"rank"})),...n.vectorIndexes.map(a=>({fields:a.field===void 0?[]:[a.field],name:a.name,type:"vector"}))];r.length>0&&(t[n.name]=r)}return t},Rr=e=>{const t=[];for(const n of e.tables)!n.ttl||n.shardMode==="global"||t.push({...n.ttl.after===void 0?{}:{after:n.ttl.after},field:n.ttl.field,...n.softDelete?{softDeleteField:n.softDelete.field}:{},table:n.name});return t},$r=e=>{if(e.kind!=="union"||e.members===void 0||e.members.length===0)return;const t=[];for(const n of e.members){if(n.kind!=="literal"||n.literalValue===void 0)return;try{const r=JSON.parse(n.literalValue);if(typeof r!="string")return;t.push(r)}catch{return}}return t},Tr=(e,t,n)=>{const r=ie(t),a={name:e,optional:Rt(t),type:r.kind};if(r.kind==="id"&&r.tableName!==void 0){a.ref=r.tableName;const i=n.get(e);i!==void 0&&(a.onDelete=i)}r.kind==="storage"&&(a.isStorage=!0,r.bucket!==void 0&&(a.bucket=r.bucket));const o=$r(r);return o!==void 0&&(a.enumValues=o),r.column?.notNull===!1&&(a.nullable=!0),a},Nr=e=>{const t={};for(const n of e.tables){const r=[{name:"_id",optional:!1,pk:!0,type:"id"},{name:"_creationTime",optional:!1,type:"number"},...n.commitOrdered===!0?[{name:"_commitSeq",optional:!1,type:"number"}]:[]],a=new Map(n.relations.filter(o=>o.kind==="one").map(o=>[o.field,o.onDelete]));for(const[o,i]of Object.entries(n.shape))r.push(Tr(o,i,a));t[n.name]=r}return t},Ar=e=>e?{importFragment:"serveRelationFanout, ",override:`
900
+ protected override async runRelationFanoutRead(functionPath: string, args: Record<string, unknown>): Promise<unknown> {
901
+ this.ensureMigrated();
902
+
903
+ const { db } = this.buildCtx({ functionPath }) as { db: DatabaseWriterLike };
904
+
905
+ return serveRelationFanout(schema as unknown as SchemaLike, db, functionPath, args);
906
+ }
907
+ `}:{importFragment:"",override:""},Or=e=>{if(!e)return{build:"",configField:"",contextField:"",stub:""};const t='throw new Error("ctx.ai: no AI binding found. Add an \\`ai\\` binding (env.AI) to wrangler.jsonc, or pass \\`ai\\` to createShardDO().");';return{build:`
908
+ const aiBinding = config.ai?.(env) ?? (env as Record<string, unknown>).AI;
909
+ // Correlate AI-Gateway-routed calls with the Lunora trace: thread the
910
+ // function path + trace id into createAi, which folds them into the
911
+ // gateway's native \`metadata\` only when a gateway is configured (absent
912
+ // otherwise). Mirror the tracer's anchor guard — a deferred subscription
913
+ // re-run must not borrow a concurrent dispatch's trace, so read
914
+ // \`getCurrentTrace()\` only on the synchronous (non-threaded-identity) path.
915
+ const aiTrace = options.identity ? undefined : this.getCurrentTrace();
916
+ const ai: LunoraAi = aiBinding
917
+ ? createAi({ binding: aiBinding as AiBindingLike, env: env as Record<string, unknown>, metadata: { functionPath: options.functionPath, traceId: aiTrace?.traceId } })
918
+ : aiStub;
919
+ `,configField:`
920
+ ai?: (env: Record<string, unknown>) => AiBindingLike;`,contextField:`
921
+ ai,`,stub:`
922
+ const aiStub: LunoraAi = {
923
+ embeddingModel: () => {
924
+ ${t}
925
+ },
926
+ model: () => {
927
+ ${t}
928
+ },
929
+ run: async () => {
930
+ ${t}
931
+ },
932
+ // workersai is a callable-with-properties; a bare throwing arrow isn't
933
+ // structurally assignable, so cast it. Never invoked (the stub throws first).
934
+ workersai: (() => {
935
+ ${t}
936
+ }) as unknown as LunoraAi["workersai"],
937
+ };
938
+ `}},C=(e,t,n,{cast:r="",sync:a=[]}={})=>{const o=n.map(i=>` ${i}: ${a.includes(i)?"":"async "}() => {
939
+ ${t}
940
+ },`).join(`
941
+ `);return`
942
+ const ${e} = {
943
+ ${o}
944
+ }${r};
945
+ `},_={build:"",configField:"",contextField:"",importLines:[],stub:""},Ir=e=>e?{build:`
946
+ const kvBinding = config.kv?.(env) ?? (env as Record<string, unknown>).KV;
947
+ // KV is not this shard's SQLite, so nothing appends a \`__cdc_log\` entry
948
+ // when a value changes — a subscription that read one can never be proven
949
+ // current on reconnect and must re-snapshot. Reads only; \`put\`/\`delete\`
950
+ // are writes and stay unstamped.
951
+ const kv: Kv = markUnvouchableReads(kvBinding ? createKv({ namespace: kvBinding as KVNamespaceLike }) : kvStub, options.onRead, [
952
+ "get",
953
+ "getRaw",
954
+ "getWithMetadata",
955
+ "list",
956
+ ]);
957
+ `,configField:`
958
+ kv?: (env: Record<string, unknown>) => KVNamespaceLike;`,contextField:`
959
+ kv,`,importLines:['import type { Kv, KVNamespaceLike } from "@lunora/bindings/kv";','import { createKv } from "@lunora/bindings/kv";'],stub:C("kvStub: Kv",'throw new Error("ctx.kv: no KV binding found. Add a \\`kv_namespaces\\` binding (env.KV) to wrangler.jsonc, or pass \\`kv\\` to createShardDO().");',["delete","get","getRaw","getWithMetadata","list","put"])}:_,Cr=(e,t)=>e?{build:`
960
+ const flags: import("${t}").LunoraFlags = createFlags(flagsConfig, env, {
961
+ provider: () => config.flags?.(env),
962
+ targetingKey: () => flagsConfig.identify?.({ identity: identity ?? null, userId: userId ?? null }),
963
+ });
964
+ `,configField:`
965
+ flags?: (env: Record<string, unknown>) => import("${t}").Provider;`,contextField:`
966
+ flags,`,importLines:[`import { createFlags } from "${t}";`,'import flagsConfig from "../flags.js";'],stub:""}:_,Dr=e=>e?{build:`
967
+ const { notify, push } = createNotify(notifyConfig, env, { log, metrics });
968
+ `,configField:"",contextField:`
969
+ notify,
970
+ push,`,importLines:['import { createNotify } from "@lunora/notify";','import notifyConfig from "../notify.js";'],stub:""}:_,Lr=e=>e?{build:`
971
+ const envConfig = lunoraEnvContract.${e.exportName}(env);
972
+ `,configField:"",contextField:`
973
+ env: envConfig,`,importLines:['import * as lunoraEnvContract from "../env.js";'],stub:""}:_,Er=e=>e?{build:`
974
+ const access = accessFacade(identity, userId);
975
+ `,configField:"",contextField:`
976
+ access,`,importLines:['import { accessFacade } from "@lunora/cloudflare-access/context";'],stub:""}:_,_r=(e,t,n)=>{if(!t)return{constant:"",evaluateOverride:"",subscriptionOverride:""};const r=s=>`
977
+ const env = (this.env ?? {}) as Record<string, unknown>;
978
+ const flags: import("${n}").LunoraFlags = createFlags(flagsConfig, env, {
979
+ provider: () => config.flags?.(env),
980
+ targetingKey: ${s},
981
+ });`,a=`
982
+ /** Statically-discovered feature flags (\`ctx.flags.<type>("key")\` reads) served via \`__lunora_admin__:listFlags\` + the reactive \`__lunora_flags__:\` channel. */
983
+ const LUNORA_FLAG_KEYS = ${I(e,'ReadonlyArray<{ key: string; type: "boolean" | "number" | "object" | "string" }>')};
984
+ `,o=`
985
+ protected override async evaluateFlags(context?: Record<string, unknown>): Promise<FlagsResult> {${r("undefined")}
986
+ const evalContext = context as import("${n}").EvaluationContext | undefined;
987
+ const evaluations: FlagsResult["flags"] = [];
988
+
989
+ for (const entry of LUNORA_FLAG_KEYS) {
990
+ // eslint-disable-next-line no-await-in-loop -- flags evaluate sequentially; each shares the single memoized provider client
991
+ const details =
992
+ entry.type === "boolean"
993
+ ? await flags.details.boolean(entry.key, false, evalContext)
994
+ : entry.type === "number"
995
+ ? await flags.details.number(entry.key, 0, evalContext)
996
+ : entry.type === "string"
997
+ ? await flags.details.string(entry.key, "", evalContext)
998
+ : await flags.details.object(entry.key, {}, evalContext);
999
+
1000
+ evaluations.push({ errorCode: details.errorCode, key: entry.key, reason: details.reason, type: entry.type, value: details.value, variant: details.variant });
1001
+ }
1002
+
1003
+ return { configured: true, flags: evaluations };
1004
+ }
1005
+ `,i=`
1006
+ protected override runFlagSubscriptionRead(_functionPath: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): Promise<unknown> {${r("() => flagsConfig.identify?.({ identity: identity?.identity ?? null, userId: identity?.userId ?? null })")}
1007
+ const key = typeof args.key === "string" ? args.key : "";
1008
+
1009
+ // SECURITY: the reactive channel is public (any socket, no auth). Serve
1010
+ // ONLY statically-discovered flag keys — an arbitrary client-supplied key
1011
+ // would let a subscriber probe the value of internal/unreleased flags the
1012
+ // app never exposes. Unknown key ⇒ the "nothing to deliver" sentinel.
1013
+ // eslint-disable-next-line unicorn/no-null -- the base hook's "nothing to deliver" sentinel
1014
+ if (key.length === 0 || !LUNORA_FLAG_KEYS.some((entry) => entry.key === key)) {
1015
+ return Promise.resolve(null);
1016
+ }
1017
+
1018
+ // SECURITY: evaluate under the socket's server-verified identity ONLY
1019
+ // (the targetingKey resolved above). Client-supplied targeting context is
1020
+ // NOT honored on this public channel — otherwise a subscriber could spoof
1021
+ // targeting attributes (e.g. plan/role) to unlock a flag gated on them.
1022
+ const context = undefined;
1023
+
1024
+ if (args.type === "number") {
1025
+ return flags.number(key, typeof args.default === "number" ? args.default : 0, context);
1026
+ }
1027
+
1028
+ if (args.type === "string") {
1029
+ return flags.string(key, typeof args.default === "string" ? args.default : "", context);
1030
+ }
1031
+
1032
+ if (args.type === "object") {
1033
+ return flags.object(key, (args.default ?? {}) as import("${n}").JsonValue, context);
1034
+ }
1035
+
1036
+ return flags.boolean(key, typeof args.default === "boolean" ? args.default : false, context);
1037
+ }
1038
+ `;return{constant:a,evaluateOverride:o,subscriptionOverride:i}},Pr=e=>e?{build:`
1039
+ const analyticsBinding = config.analytics?.(env) ?? (env as Record<string, unknown>).ANALYTICS;
1040
+ const analytics: AnalyticsClient = analyticsBinding ? createAnalytics(analyticsBinding as AnalyticsEngineDatasetLike) : analyticsStub;
1041
+ `,configField:`
1042
+ analytics?: (env: Record<string, unknown>) => AnalyticsEngineDatasetLike;`,contextField:`
1043
+ analytics,`,importLines:['import type { AnalyticsClient, AnalyticsEngineDatasetLike } from "@lunora/bindings/analytics";','import { createAnalytics } from "@lunora/bindings/analytics";'],stub:C("analyticsStub: AnalyticsClient",'throw new Error("ctx.analytics: no Analytics Engine binding found. Add an \\`analytics_engine_datasets\\` binding (env.ANALYTICS) to wrangler.jsonc, or pass \\`analytics\\` to createShardDO().");',["track","writeDataPoint"],{sync:["track","writeDataPoint"]})}:_,Mr=e=>e?{build:`
1044
+ const imagesBinding = config.images?.(env) ?? (env as Record<string, unknown>).IMAGES;
1045
+ const images: Images = imagesBinding ? createImages({ binding: imagesBinding as ImagesBindingLike }) : imagesStub;
1046
+ `,configField:`
1047
+ images?: (env: Record<string, unknown>) => ImagesBindingLike;`,contextField:`
1048
+ images,`,importLines:['import type { Images, ImagesBindingLike } from "@lunora/bindings/images";','import { createImages } from "@lunora/bindings/images";'],stub:C("imagesStub: Images",'throw new Error("ctx.images: no Images binding found. Add an \\`images\\` binding (env.IMAGES) to wrangler.jsonc, or pass \\`images\\` to createShardDO().");',["info","transform"])}:_,Fr=e=>e?{build:`
1049
+ const sql: SqlClient = config.sql ? config.sql(env) : sqlStub;
1050
+ `,configField:`
1051
+ sql?: (env: Record<string, unknown>) => SqlClient;`,contextField:`
1052
+ sql,`,importLines:['import type { SqlClient } from "@lunora/hyperdrive";'],stub:C("sqlStub: SqlClient",'throw new Error("ctx.sql: provide a \\`sql\\` config thunk that builds a SqlClient from your driver, e.g. \\`sql: (env) => fromPostgresJs(postgres(env.HYPERDRIVE.connectionString))\\`.");',["query"])}:_,Br=e=>e?{build:`
1053
+ const browser: Browser = config.browser ? config.browser(env) : browserStub;
1054
+ `,configField:`
1055
+ browser?: (env: Record<string, unknown>) => Browser;`,contextField:`
1056
+ browser,`,importLines:['import type { Browser } from "@lunora/browser";'],stub:C("browserStub: Browser","throw new Error(\"ctx.browser: provide a \\`browser\\` config thunk, e.g. \\`browser: (env) => createBrowser({ binding: env.BROWSER, launch })\\` with \\`import { launch } from '@cloudflare/playwright'\\`. Session reuse (connect/sessions) additionally needs those two exports passed the same way.\");",["connect","content","launch","pdf","scrape","screenshot","sessions"])}:_,qr=e=>e?{build:`
1057
+ const r2sqlEnv = env as Record<string, unknown>;
1058
+ const r2sqlAccountId = (r2sqlEnv.R2_SQL_ACCOUNT_ID ?? r2sqlEnv.CLOUDFLARE_ACCOUNT_ID) as string | undefined;
1059
+ const r2sqlToken = r2sqlEnv.R2_SQL_TOKEN as string | undefined;
1060
+ const r2sqlBucket = r2sqlEnv.R2_SQL_BUCKET as string | undefined;
1061
+ const r2sql: R2SqlClient = config.r2sql
1062
+ ? config.r2sql(env)
1063
+ : r2sqlAccountId && r2sqlToken && r2sqlBucket
1064
+ ? createR2Sql({ accountId: r2sqlAccountId, apiToken: r2sqlToken, bucket: r2sqlBucket })
1065
+ : r2sqlStub;
1066
+ `,configField:`
1067
+ r2sql?: (env: Record<string, unknown>) => R2SqlClient;`,contextField:"",importLines:['import type { R2SqlClient } from "@lunora/bindings/r2sql";','import { createR2Sql } from "@lunora/bindings/r2sql";'],stub:C("r2sqlStub: R2SqlClient",'throw new Error("ctx.r2sql: no R2 SQL credentials found. Set \\`R2_SQL_TOKEN\\`, \\`R2_SQL_ACCOUNT_ID\\` (or \\`CLOUDFLARE_ACCOUNT_ID\\`), and \\`R2_SQL_BUCKET\\` in your env/.dev.vars, or pass an \\`r2sql\\` config thunk to createShardDO().");',["describe","explain","from","query","showDatabases","showTables"],{sync:["from"]})}:_,jr=e=>e?{build:`
1068
+ const pipelinesBinding = config.pipelines?.(env) ?? (env as Record<string, unknown>).PIPELINES;
1069
+ const pipelines: PipelineClient = pipelinesBinding ? createPipelines({ binding: pipelinesBinding as PipelineBindingLike }) : pipelinesStub;
1070
+ `,configField:`
1071
+ pipelines?: (env: Record<string, unknown>) => PipelineBindingLike;`,contextField:"",importLines:['import type { PipelineBindingLike, PipelineClient } from "@lunora/bindings/pipelines";','import { createPipelines } from "@lunora/bindings/pipelines";'],stub:C("pipelinesStub: PipelineClient",'throw new Error("ctx.pipelines: no Pipelines binding found. Add a \\`pipelines\\` binding (env.PIPELINES) to wrangler.jsonc, or pass \\`pipelines\\` to createShardDO().");',["send"])}:_,va=(e,t)=>{if(e.length===0)return"";const n=t?`, ${JSON.stringify(t)}`:"",r=e.map(o=>(f(o.exportName,`container export "${o.exportName}"`),f(o.className,`container class "${o.className}"`),`/** Container DO for the \`${o.exportName}\` definition (binding \`${o.bindingName}\`). */
1072
+ export class ${o.className} extends LunoraContainer {
1073
+ public constructor(ctx: ConstructorParameters<typeof LunoraContainer>[0], env: Record<string, unknown>) {
1074
+ super(ctx, env, ${o.exportName}, "${o.exportName}"${n});
1075
+ }
1076
+ }
1077
+ `)).join(`
1078
+ `),a=e.map(o=>o.exportName).join(", ");return`${S}/**
1079
+ * Container-enabled Durable Object classes for the containers declared in
1080
+ * \`lunora/containers.ts\`. Re-export them from your worker entry — wrangler
1081
+ * requires each \`containers[].class_name\` to be exported by the worker:
1082
+ *
1083
+ * \`export * from "./lunora/_generated/containers.js";\`
1084
+ *
1085
+ * \`ContainerProxy\` is re-exported alongside them: the egress-interception path
1086
+ * (\`allowedHosts\`/\`deniedHosts\`/\`interceptHttps\` and the runtime
1087
+ * \`handle.egress\` controls) routes container outbound traffic through this
1088
+ * WorkerEntrypoint, so it too must be exported by the deployed worker.
1089
+ */
1090
+ import { LunoraContainer } from "@lunora/container/do";
1091
+
1092
+ import { ${a} } from "../containers.js";
1093
+
1094
+ export { ContainerProxy } from "@lunora/container/do";
1095
+
1096
+ ${r}`},Wr=(e,t)=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const r of e)f(r.exportName,`container export "${r.exportName}"`),f(r.bindingName,`container binding "${r.bindingName}"`);const n=e.map(r=>{const a=r.maxInstances===void 0?"":`, maxInstances: ${String(r.maxInstances)}`;return` { binding: "${r.bindingName}", exportName: "${r.exportName}"${a} },`}).join(`
1097
+ `);return{build:`
1098
+ const containers = createContainerContext(env, LUNORA_CONTAINERS, ${t?JSON.stringify(t):"undefined"}, this.getCurrentTraceparent(), this.getCurrentSampleErrors());
1099
+ `,contextField:`
1100
+ containers,`,importLines:['import type { ContainerBindingSpec } from "@lunora/container";','import { createContainerContext } from "@lunora/container";'],specs:`
1101
+ /** Wiring specs for \`ctx.containers\` (codegen-derived from \`lunora/containers.ts\`). */
1102
+ const LUNORA_CONTAINERS: ReadonlyArray<ContainerBindingSpec> = [
1103
+ ${n}
1104
+ ];
1105
+ `}},Sa=e=>{if(e.length===0)return"";const t=e.map(r=>(f(r.exportName,`workflow export "${r.exportName}"`),f(r.className,`workflow class "${r.className}"`),`/** WorkflowEntrypoint for the \`${r.exportName}\` definition (binding \`${r.bindingName}\`). */
1106
+ export class ${r.className} extends LunoraWorkflow<WorkflowParamsOf<typeof ${r.exportName}>, WorkflowOutputOf<typeof ${r.exportName}>> {
1107
+ public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
1108
+ super(ctx, env, ${r.exportName}, "${r.exportName}");
1109
+ }
1110
+ }
1111
+ `)).join(`
1112
+ `),n=e.map(r=>r.exportName).join(", ");return`${S}/**
1113
+ * WorkflowEntrypoint classes for the workflows declared in
1114
+ * \`lunora/workflows.ts\`. Re-export them from your worker entry — wrangler
1115
+ * requires each \`workflows[].class_name\` to be exported by the worker:
1116
+ *
1117
+ * \`export * from "./lunora/_generated/workflows.js";\`
1118
+ */
1119
+ import LunoraWorkflow from "@lunora/workflow/do";
1120
+
1121
+ import { ${n} } from "../workflows.js";
1122
+
1123
+ /** Params type carried by a \`defineWorkflow\` definition (its phantom \`__params\`), so each entrypoint keeps the authored \`ctx.params\` type. */
1124
+ type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params }
1125
+ ? unknown extends Params
1126
+ ? Record<string, unknown>
1127
+ : NonNullable<Params>
1128
+ : Record<string, unknown>;
1129
+
1130
+ /** Output type carried by a \`defineWorkflow\` definition (its phantom \`__output\`), so each entrypoint keeps the authored return type. */
1131
+ type WorkflowOutputOf<Definition> = Definition extends { __output?: infer Output } ? Output : unknown;
1132
+
1133
+ ${t}`},Ra=e=>{if(e.length===0)return"";const n=e.filter(s=>s.voice).length>0,r=e.map(s=>{f(s.exportName,`agent export "${s.exportName}"`),f(s.className,`agent class "${s.className}"`);const c=`/** WorkflowEntrypoint for the \`${s.exportName}\` agent (binding \`${s.bindingName}\`). */
1134
+ export class ${s.className} extends LunoraWorkflow<AgentRunInput, AgentRunResult> {
1135
+ public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
1136
+ super(ctx, env, compileAgentWorkflow(${s.exportName}, "${s.exportName}"), "${s.exportName}");
1137
+ }
1138
+ }
1139
+ `;if(!s.voice)return c;const d=s.voiceClassName??"",h=s.voiceBindingName??"";f(d,`agent voice class "${d}"`);const g=`
1140
+ /** Voice-session Durable Object for the \`${s.exportName}\` agent (binding \`${h}\`). */
1141
+ export class ${d} extends VoiceSessionDO {
1142
+ public constructor(ctx: ConstructorParameters<typeof VoiceSessionDO>[0], env: Record<string, unknown>) {
1143
+ super(ctx, env, ${s.exportName}, "${s.exportName}");
1144
+ }
1145
+ }
1146
+ `;return`${c}${g}`}).join(`
1147
+ `),a=e.map(s=>s.exportName).join(", ");return`${S}/**
1148
+ * WorkflowEntrypoint classes for the agents declared in \`lunora/agents.ts\`.
1149
+ * Each \`defineAgent\` compiles a replay-safe tool-loop onto a Cloudflare
1150
+ * Workflow. Re-export them from your worker entry — wrangler requires each
1151
+ * \`workflows[].class_name\` to be exported by the worker:
1152
+ *
1153
+ * \`export * from "./lunora/_generated/agents.js";\`${n?"\n *\n * Voice-enabled agents ALSO get a `VoiceSessionDO` subclass here — a real\n * Durable Object (unlike the Workflow), so wrangler needs both its\n * `durable_objects` binding and its `class_name` export (the same re-export\n * line covers it).":""}
1154
+ */
1155
+ import LunoraWorkflow from "@lunora/workflow/do";
1156
+ ${n?'import { compileAgentWorkflow, VoiceSessionDO } from "@lunora/agent";':'import { compileAgentWorkflow } from "@lunora/agent";'}
1157
+ import type { AgentRunInput, AgentRunResult } from "@lunora/agent";
1158
+
1159
+ import { ${a} } from "../agents.js";
1160
+
1161
+ ${r}`},$a=e=>{const t=e.filter(a=>a.mode==="push");if(t.length===0)return"";for(const a of t)f(a.exportName,`queue export "${a.exportName}"`);const n=t.map(a=>a.exportName).join(", "),r=t.map(a=>` ${JSON.stringify(a.name)}: { binding: ${JSON.stringify(a.bindingName)}, definition: ${a.exportName}, exportName: ${JSON.stringify(a.exportName)} },`).join(`
1162
+ `);return`${S}/**
1163
+ * Push-consumer registry for the queues declared in \`lunora/queues.ts\`. The
1164
+ * composed worker's \`queue(batch, env, ctx)\` entry routes each delivered batch
1165
+ * by \`batch.queue\` to the matching \`defineQueue\` handler. Wired automatically
1166
+ * by \`defineApp\` — you don't import this directly.
1167
+ */
1168
+ import type { QueueRegistry } from "@lunora/queue";
1169
+
1170
+ import { ${n} } from "../queues.js";
1171
+
1172
+ /** Stable wrangler queue name → { binding, definition, exportName } for batch routing. */
1173
+ export const LUNORA_QUEUE_REGISTRY: QueueRegistry = {
1174
+ ${r}
1175
+ };
1176
+ `},Ur=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)f(n.exportName,`workflow export "${n.exportName}"`),f(n.bindingName,`workflow binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}" },`).join(`
1177
+ `);return{build:`
1178
+ const workflows = createWorkflowContext(env, LUNORA_WORKFLOWS);
1179
+ `,contextField:`
1180
+ workflows,`,importLines:['import type { WorkflowBindingSpec } from "@lunora/workflow";','import { createWorkflowContext } from "@lunora/workflow";'],specs:`
1181
+ /** Wiring specs for \`ctx.workflows\` (codegen-derived from \`lunora/workflows.ts\`). */
1182
+ const LUNORA_WORKFLOWS: ReadonlyArray<WorkflowBindingSpec> = [
1183
+ ${t}
1184
+ ];
1185
+ `}},Kr=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)f(n.exportName,`queue export "${n.exportName}"`),f(n.bindingName,`queue binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}", name: ${JSON.stringify(n.name)} },`).join(`
1186
+ `);return{build:`
1187
+ const queues = createQueueContext(env, LUNORA_QUEUES);
1188
+ `,contextField:`
1189
+ queues,`,importLines:['import type { QueueBindingSpec } from "@lunora/queue";','import { createQueueContext } from "@lunora/queue";'],specs:`
1190
+ /** Wiring specs for \`ctx.queues\` (codegen-derived from \`lunora/queues.ts\`). */
1191
+ const LUNORA_QUEUES: ReadonlyArray<QueueBindingSpec> = [
1192
+ ${t}
1193
+ ];
1194
+ `}},Qr=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)f(n.exportName,`agent export "${n.exportName}"`),f(n.bindingName,`agent binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}"${n.publicRun===!0?", publicRun: true":""} },`).join(`
1195
+ `);return{build:`
1196
+ const agents = createAgentContext(env, LUNORA_AGENTS);
1197
+ `,contextField:`
1198
+ agents,`,importLines:['import { createAgentContext } from "@lunora/agent";','import type { AgentBindingSpec } from "@lunora/agent";'],specs:`
1199
+ /** Wiring specs for \`ctx.agents\` (codegen-derived from \`lunora/agents.ts\`). */
1200
+ const LUNORA_AGENTS: ReadonlyArray<AgentBindingSpec> = [
1201
+ ${t}
1202
+ ];
1203
+ `}},It=(e,t,n,r,a)=>({constant:`
1204
+ ${a}
1205
+ const ${e} = ${I(r,t)};
1206
+ `,override:`
1207
+ protected override ${n}(): ${t} {
1208
+ return ${e};
1209
+ }
1210
+ `}),Vr=e=>{if(e.length===0)return{constant:"",override:""};const t={workflows:e.map(n=>({binding:n.bindingName,className:n.className,exportName:n.exportName,name:n.name}))};return It("LUNORA_WORKFLOWS_INFO","WorkflowsResult","workflowsMetadata",t,"/** Read-only declared-workflow metadata (discovered from `lunora/workflows.ts`) served via `__lunora_admin__:listWorkflows` for the studio's workflows view. */")},Hr=e=>{if(e.length===0)return{constant:"",override:""};const t={queues:e.map(n=>({binding:n.bindingName,...n.tuning.deadLetterQueue===void 0?{}:{deadLetterQueue:n.tuning.deadLetterQueue},exportName:n.exportName,mode:n.mode,name:n.name}))};return It("LUNORA_QUEUES_INFO","QueuesResult","queuesMetadata",t,"/** Read-only declared-queue metadata (discovered from `lunora/queues.ts`) served via `__lunora_admin__:listQueues` for the studio's queues view. */")},Gr=e=>e?{imports:['import type { LunoraDatabaseLike as LunoraPaymentDbLike, LunoraPayment, PaymentsFromContextOptions } from "@lunora/payment";','import { paymentsFromContext } from "@lunora/payment";'],build:`
1211
+ const payments: LunoraPayment = config.payment
1212
+ ? paymentsFromContext({ auth: { userId: userId ?? null }, db: db as unknown as LunoraPaymentDbLike }, config.payment(env))
1213
+ : paymentStub;
1214
+ `,configField:`
1215
+ payment?: (env: Record<string, unknown>) => PaymentsFromContextOptions;`,contextField:`
1216
+ payments,`,stub:C("paymentStub: LunoraPayment",'throw new Error("ctx.payments: no payment configured. Pass \\`payment\\` to createShardDO().");',["attach","cancelSubscription","check","createCheckout","createPortalSession","handleWebhook","listBalances","listSubscriptions","track"],{cast:" as unknown as LunoraPayment",sync:["attach","cancelSubscription","check","createCheckout","createPortalSession","handleWebhook","listBalances","listSubscriptions","track"]})}:{build:"",configField:"",contextField:"",imports:[],stub:""},zr=e=>e?{imports:['import type { X402Pay, X402PayConfig } from "@lunora/x402/pay";','import { lazyX402Pay } from "@lunora/x402/pay";'],build:`
1217
+ const x402: X402Pay = config.x402
1218
+ ? lazyX402Pay(config.x402(env), { getSecret: (name: string) => secrets.get(name) })
1219
+ : x402Stub;
1220
+ `,configField:`
1221
+ x402?: (env: Record<string, unknown>) => X402PayConfig;`,stub:C("x402Stub: X402Pay",'throw new Error("ctx.x402: no pay rail configured. Pass \\`x402\\` to createShardDO().");',["fetch"],{cast:" as unknown as X402Pay",sync:["fetch"]})}:{build:"",configField:"",imports:[],stub:""},Jr=(e,t,n,r,a)=>["AdvisorProcedure","AdvisoryFinding","DatabaseWriterLike","DataMigrationLike","DispatchBookmark","ExportRow",...r?["FlagsResult"]:[],"ImportShardResult","KeyRange","MaskPoliciesResult","MigrationRunResult","QueryReadScope",...n?["QueuesResult"]:[],"RelatedPage","RunShardApplyCdcArgs","RunShardExportArgs","RunShardFindRelatedArgs","RunShardImportArgs","RunShardMigrationArgs","RlsPoliciesResult","RunShardRankBeforeArgs","RunShardRankPageArgs","RunShardWriteArgs","RunShardWriteResult","SchedulerLike",...a?["SearchBackfillProgress"]:[],"TransactionHeadroomTracker","SchemaLike","ShardDOState","ShardRankPageResult","SqlExec","StorageRulesResult","StudioFeaturesResult","SubscriptionIdentity","SystemReaderStorageLike","TelemetrySink",...e?["VectorBackfillProgress"]:[],...t?["WorkflowsResult"]:[],...e?["WriteHook"]:[]],Ta=({advisories:e=[],advisorProcedures:t=[],agents:n=[],containers:r=[],env:a,flagKeys:o=[],hasAccessFacade:i=!1,hasAi:s=!1,hasAnalytics:c=!1,hasBrowser:d=!1,hasFlags:h=!1,hasHyperdrive:g=!1,hasImages:y=!1,hasKv:u=!1,hasNotify:l=!1,hasPayments:b=!1,hasPipelines:R=!1,hasR2sql:x=!1,hasVectors:v=!0,hasX402:$=!1,maskMetadata:T,mutators:H=[],queues:O=[],rlsMetadata:D,schema:k,schemaSnapshot:F,shapes:B=[],storageRules:le,studioFeatures:ce,useUmbrella:ee=!1,workflows:G=[]})=>{const L=se(ee),te=H.length>0,w=B.length>0,Oe=F===void 0?"":An(F),q=F===void 0?"":`
1222
+ /** 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. */
1223
+ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(On(F))}, json: ${JSON.stringify(Oe)} };
1224
+ `,de=F===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",{build:ue,configField:Ie,contextField:Ce,stub:De}=Or(s),he=Er(i),z=Ir(u),P=Cr(h,L.flags),J=_r(o,h,L.flags),ne=Dr(l),pe=Lr(a),Y=Pr(c),U=Mr(y),K=Fr(g),Q=Br(d),j=qr(x),re=jr(R),{build:Le,contextField:Se,importLines:Re,specs:me}=Kr(O),{build:Ee,contextField:_e,importLines:$e,specs:ge}=Wr(r,k.jurisdiction),{build:Pe,contextField:Me,importLines:Te,specs:Fe}=Ur(G),{build:Be,contextField:fe,importLines:be,specs:qe}=Qr(n),{build:je,configField:We,contextField:ye,imports:we,stub:et}=Gr(b),{build:p,configField:Ue,imports:Ne,stub:ae}=zr($),Dt=e,Lt=t,tt=D??{policies:[],roles:[]},Et=T??{columns:[]},_t=le??{rules:[]},Pt=ce??{analytics:!1,auth:!1,containers:!1,flags:!1,kv:!1,mail:!1,notifications:!1,payments:!1,queues:!1,scheduler:!1,storage:!1,vectors:!1,workflows:!1},{constant:Mt,override:Ft}=Vr(G),{constant:Bt,override:qt}=Hr(O),M=v&&k.vectorIndexes.length>0,nt=new Set(k.tables.filter(m=>typeof m.shardMode=="object"&&m.shardMode.kind==="shardBy").map(m=>m.name)),Ae=M&&k.vectorIndexes.some(m=>nt.has(m.table)),ke=k.tables.some(m=>m.shardMode==="global"),Ke=k.tables.some(m=>Cn(m)),rt=k.tables.some(m=>Dn(m)),oe=k.tables.some(m=>m.externalSource!==void 0),at=k.tables.some(m=>m.memory===!0),Qe=k.tables.some(m=>m.shardMode!=="global"&&m.searchIndexes.length>0);if(rt&&Ke)throw new Z("INTERNAL",'lunora codegen: mixing `.global()` (D1) and `.global({ backend: "hyperdrive" })` tables in one app is not supported yet — use a single global backend.');const Ve=k.tables.length>0,jt=xr(k),Wt=Sr(k),Ut=Nr(k),Kt=vr(k),ot=Rr(k),st=Jr(M,G.length>0,O.length>0,h,Qe);w&&st.push("WhereInput");const it=Ar(ke),Qt=w?`
1225
+ protected override resolveShape(name: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string } | undefined {
1226
+ const shape = LUNORA_SHAPES[name];
1227
+
1228
+ if (!shape) {
1229
+ return undefined;
1230
+ }
1231
+
1232
+ this.ensureMigrated();
1233
+
1234
+ // Trusted ctx from the socket's OWN verified identity — the client
1235
+ // supplies only the shape name + args; \`compileWhere\` validates the
1236
+ // args then runs the shape's \`where(ctx, args)\` under that identity,
1237
+ // so which rows replicate is a server decision (reads-as-permissions).
1238
+ const ctx = this.buildCtx({ functionPath: \`__shape__:\${name}\`, identity });
1239
+ // \`ownerField\` resolves an \`owner: true\` shape: only here is the table's
1240
+ // \`.ownedBy(field)\` column in scope, so the DO hands it to the shape
1241
+ // rather than every shape restating the ownership check in its \`where\`.
1242
+ const shapeWhere = shape.compileWhere(ctx, args, {
1243
+ ownerField: (schema as unknown as { tables: Record<string, { ownerField?: string }> }).tables[shape.table]?.ownerField,
1244
+ }) as unknown as WhereInput;
1245
+
1246
+ // AND-compose with the read policies this SHAPE declares
1247
+ // (\`defineShape({ use: [guard] })\`, pre-indexed into
1248
+ // \`shape.rlsRegistry\`). A shape runs no procedure, so the
1249
+ // \`.use(rls(...))\` middleware never fires; without this merge its
1250
+ // reads would bypass the read policies it opted into.
1251
+ // \`composeShapeReadWhere\` evaluates them under this same trusted ctx
1252
+ // and fails closed under a \`.rls("required")\` schema for a
1253
+ // non-\`.public()\` table the shape declared no read policy for.
1254
+ //
1255
+ // The registry is the shape's OWN — never one folded from every
1256
+ // registered function. A project-wide union let one admin-only
1257
+ // procedure's allow-all read policy unrestrict every shape on the
1258
+ // table; see the SCOPE note in \`shape-read-base.ts\`.
1259
+ //
1260
+ // It is NOT identical to the request-time path, and must not be
1261
+ // described as such: roles come from the identity's \`roles\` claim
1262
+ // only, because no middleware runs here to contribute
1263
+ // \`ctx.auth.roles\`. Derive roles at the identity if a policy gates
1264
+ // on them — see \`shape-read-base.ts\`.
1265
+ const effectiveWhere = composeShapeReadWhere(shape.rlsRegistry, {
1266
+ ctx,
1267
+ identity: identity?.identity ?? null,
1268
+ rlsRequired: (schema as unknown as { rlsMode?: string }).rlsMode === "required",
1269
+ shapeWhere,
1270
+ table: shape.table,
1271
+ tablePublic: (schema as unknown as { tables: Record<string, { isPublic?: boolean }> }).tables[shape.table]?.isPublic === true,
1272
+ userId: identity?.userId ?? null,
1273
+ });
1274
+
1275
+ // A live shape pokes only from its OWN shard's op-log, so a \`where()\`
1276
+ // that joins to a \`.shardBy()\` table (rows in another DO) is rejected
1277
+ // here — the first point the compiled predicate + shard modes are both
1278
+ // known. Remedy: denormalize, or move the joined table to \`.global()\`.
1279
+ assertShapeShardable(effectiveWhere, schema as unknown as SchemaLike, shape.table);
1280
+
1281
+ // A \`.global()\` table lives in D1 (no per-DO op-log): flag it so the
1282
+ // base serves it through the latency-tiered poll path (\`readGlobalShapeRows\`)
1283
+ // instead of the CDC poke path.
1284
+ const isGlobal = (schema as unknown as SchemaLike).tables[shape.table]?.shardMode?.kind === "global";
1285
+
1286
+ return { columns: shape.columns, effectiveWhere, global: isGlobal, table: shape.table };
1287
+ }
1288
+ `:"",lt=w?[...new Set(tt.policies.filter(m=>m.on==="read"&&m.table!=="").map(m=>m.table))].toSorted((m,Nn)=>m.localeCompare(Nn)):[],ct=lt.length>0?`
1289
+ /** 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). */
1290
+ assertShapesDeclareReadPolicies(LUNORA_SHAPES, ${JSON.stringify(lt)}, (schema as unknown as { rlsMode?: string }).rlsMode === "required");
1291
+ `:"",Vt=te?`
1292
+ protected override isCustomMutator(functionPath: string): boolean {
1293
+ return LUNORA_MUTATOR_PATHS.has(functionPath);
1294
+ }
1295
+ `:"",Ht=w?"assertShapeShardable, ":"",Gt=ct===""?"":"assertShapesDeclareReadPolicies, ",xe=[`import type { ${st.join(", ")} } from "${L.do}";`,`import { applyCdcChanges, ${Qe?"backfillSearchIndexes, ":""}${M?"backfillVectorIndexes, ":""}buildReprojectionMigration, ${at?"clearMemoryTables, ":""}${Ht}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${oe?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}markUnvouchableReads, ${Ae?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${it.importFragment}ShardDO as ShardDOBase } from "${L.do}";`,...oe?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${L.do}";`]:[],w?`import { asBucketStorage, ${Gt}beginDeferredDeletes, beginDeferredSchedules, composeShapeReadWhere, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${L.server}";`:`import { asBucketStorage, beginDeferredDeletes, beginDeferredSchedules, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${L.server}";`];Ve&&xe.push(`import { bindOrm, bindTableFacade } from "${L.server}";`),M&&xe.push('import type { SchemaLike as VectorSchemaLike, VectorBackfillSync, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectorBackfillSync, createVectors, createVectorSyncHook, vectorBackfillTargets } from "@lunora/bindings/vectors";'),s&&xe.push('import type { AiBindingLike, LunoraAi } from "@lunora/ai";','import { createAi } from "@lunora/ai";'),xe.push(...he.importLines,...z.importLines,...P.importLines,...ne.importLines,...pe.importLines,...Y.importLines,...U.importLines,...K.importLines,...Q.importLines,...j.importLines,...re.importLines,...$e,...Te,...Re,...be,...we,...Ne,"",'import schema from "../schema.js";',`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...te?["LUNORA_MUTATOR_PATHS"]:[],...w?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const zt=M?`
1296
+ vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",Jt=rt?`
1297
+ 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;`:"",Yt=Ke?`
1298
+ hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",Xt=oe?`
1299
+ sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",Zt='throw new Error("ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().");',en='throw new Error("ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().");',tn='throw new Error("ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().");',nn='throw new Error("ctx.storage: no storage configured. Pass `storage` to createShardDO().");',rn=ke?C("globalDbStub: DatabaseWriterLike",Zt,["aggregate","count","delete","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",an=M?C("vectorsStub: VectorSearchLike",en,["deleteByIds","getByIds","query","upsert","upsertNow"]):"",on=Ae?` const vectorShardKey = this.currentShardKey();
1300
+ `:"",dt=Ae?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",sn=`, { ${["deferAfterCommit: (work) => this.deferAfterCommit(work)",...Ae?["namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey",`shardedIndexNames: [${k.vectorIndexes.filter(m=>nt.has(m.table)).map(m=>JSON.stringify(m.name)).join(", ")}]`]:[]].join(", ")} }`,ln=M?`
1301
+ const vectorSync = this.vectorSync(env);
1302
+ // Vectorize lives outside this shard's SQLite, so a query whose result
1303
+ // depends on a similarity search cannot be proven current on reconnect —
1304
+ // an unrelated upsert (or a re-index) moves the matches without touching
1305
+ // \`__cdc_log\`. Only \`ctx.vectors\` is wrapped: the write-through
1306
+ // vector-sync hook keeps calling the bare facade.
1307
+ const bareVectors = vectorSync?.vectors ?? vectorsStub;
1308
+ const vectors = markUnvouchableReads(bareVectors, options.onRead, ["getByIds", "query"]);
1309
+ const onWrite = vectorSync?.onWrite;
1310
+ `:"",cn=M?`
1311
+ private vectorSync(env: Record<string, unknown>): { backfill: VectorBackfillSync; onWrite: WriteHook; vectors: VectorSearchLike } | undefined {
1312
+ if (!config.vectors) {
1313
+ return undefined;
1314
+ }
1315
+
1316
+ const lunora = createVectors({ indexes: config.vectors(env) });
1317
+ ${on} const vectors = createContextVectors(lunora${sn});
1318
+
1319
+ return {
1320
+ backfill: createVectorBackfillSync({ ${dt}schema: schema as unknown as VectorSchemaLike, upsertMany: lunora.upsertMany, vectors }),
1321
+ onWrite: createVectorSyncHook({ ${dt}schema: schema as unknown as VectorSchemaLike, vectors }),
1322
+ vectors,
1323
+ };
1324
+ }
1325
+
1326
+ // \`__lunora_admin__:backfillVectors\`: the rows that predate a vector index,
1327
+ // a page at a time, each page ordered on the after-commit chain so it
1328
+ // never lands over a newer write's vector.
1329
+ protected override async runShardVectorBackfill(options: { maxPages?: number; restart?: boolean }): Promise<VectorBackfillProgress> {
1330
+ this.ensureMigrated();
1331
+
1332
+ const vectorSync = this.vectorSync((this.env ?? {}) as Record<string, unknown>);
1333
+
1334
+ if (!vectorSync) {
1335
+ throw new LunoraError("NOT_IMPLEMENTED", "vector backfill is unavailable: no vectors configured. Pass \`vectors\` to createShardDO().");
1336
+ }
1337
+
1338
+ return backfillVectorIndexes(this.sql as SqlExec, vectorBackfillTargets(schema as unknown as VectorSchemaLike), vectorSync.backfill, {
1339
+ ...options,
1340
+ ordered: async (read, work) => this.runOrderedAfterWrites(read, work),
1341
+ });
1342
+ }
1343
+ `:"",dn=`{
1344
+ ...this.ctxDbTuning(),
1345
+ auth: { identity: identity ?? null, userId: userId ?? null },
1346
+ broadcast: (delta) => {
1347
+ this.recordChangedTable(delta.table, delta.indexKeys);
1348
+ },
1349
+ // Read at call time, not captured: the baseline belongs to the
1350
+ // dispatch in flight, and a queued mutation admitted after a
1351
+ // sibling's prologue must be judged against ITS caller's cursor.
1352
+ baselineSeq: () => this.getCurrentBaselineSeq(),
1353
+ cdc: config.cdc ?? false,
1354
+ // A dispatch with NO caller identity runs system-trusted: RLS scopes
1355
+ // rows to a user, and these have no user to scope to. This is the tier
1356
+ // migrations, imports and the external-source poll loop already run in
1357
+ // (see \`adminWriterPrelude\`). Only \`onShardInit\` and \`onQueryChange\`
1358
+ // reach it; \`onConnect\`/\`onDisconnect\` carry a verified identity and
1359
+ // stay guarded like any request.
1360
+ enforceRls: options.trusted !== true,
1361
+ headroom: options.headroom ?? this.transactionHeadroom(),
1362
+ // A live predicate, not a flag: the transaction opens AFTER this ctx
1363
+ // is built (a mutation dispatch wraps the handler), so only a call-time
1364
+ // read reports the truth. \`_commitSeq\` reuses one sequence across
1365
+ // writes only while they commit together — an action is not wrapped, so
1366
+ // each of its writes allocates its own.
1367
+ inTransaction: () => this.isInTransaction(),
1368
+ onIndexUse: this.getCtxDbIndexUseHook(),
1369
+ onStalePatchDropped: (event) => {
1370
+ this.recordStalePatchDropped(event);
1371
+ },
1372
+ // Bound to THIS dispatch's reactive-cache read scope (\`handleRpc\`
1373
+ // threads it in), so two concurrent queries never stamp each
1374
+ // other's dep sets. \`executeSubscription\` overrides both with its
1375
+ // own \`ReadFootprint\` pair instead, and a dispatch with neither
1376
+ // gets an unbound hook that stamps no deps.
1377
+ onRead: options.onRead ?? this.getCtxDbReadHook(options.scope),
1378
+ // Left \`undefined\` when there is no scope so \`createShardCtxDb\`
1379
+ // keeps its own "degrade a provable slice to a whole-table dep"
1380
+ // fallback rather than reporting into a no-op.
1381
+ onReadRange: options.onReadRange ?? (options.scope === undefined ? undefined : this.getCtxDbReadRangeHook(options.scope)),${M?`
1382
+ // Vectorize is outside this shard's SQLite and cannot roll back, so
1383
+ // the sync is held until the mutation's transaction has COMMITTED
1384
+ // (\`deferAfterCommit\`) instead of running inline. Inline, a write
1385
+ // that later aborted left a vector pointing at a row that does not
1386
+ // exist — which a search then surfaces — and a rolled-back delete
1387
+ // left the row with its vector already purged. Outside a
1388
+ // transaction (an action) nothing is open and it runs at once.
1389
+ onWrite: onWrite === undefined ? undefined : (event) => this.deferAfterCommit(() => onWrite(event)),`:""}
1390
+ scheduler,
1391
+ schema: schema as unknown as SchemaLike,
1392
+ sql: this.sql as SqlExec,
1393
+ storage,${ke?`
1394
+ globalDb,`:""}
1395
+ }`,un=` /**
1396
+ * The bare writer every admin/maintenance entry point writes through.
1397
+ *
1398
+ * Admin and maintenance writes go through the SAME reactive-cache hooks as a
1399
+ * user mutation. Without this, a studio row edit, a TTL sweep, an admin
1400
+ * import, a CDC apply or a data-migration backfill writes without
1401
+ * invalidating, and the next query answers from the pre-write snapshot.
1402
+ *
1403
+ * \`headroom\` meters the transaction. An admin \`/rpc\` caller passes nothing
1404
+ * and falls back to \`this.transactionHeadroom()\`, which for an admin RPC is
1405
+ * \`undefined\` — \`handleAdminRpc\` answers before \`beginDispatch\`, so no
1406
+ * per-dispatch meter is in flight and a bulk loop is bounded by its per-call
1407
+ * row cap alone. The TTL sweep (an alarm work item, no dispatch) passes its
1408
+ * own by-value tracker explicitly instead.
1409
+ */
1410
+ private adminWriter(headroom?: TransactionHeadroomTracker): DatabaseWriterLike {
1411
+ const env = (this.env ?? {}) as Record<string, unknown>;
1412
+ const scheduler = (config.scheduler?.(env) ?? schedulerStub) as SchedulerLike;
1413
+
1414
+ return createShardCtxDb({
1415
+ ...this.ctxDbTuning(),
1416
+ broadcast: (delta) => {
1417
+ this.recordChangedTable(delta.table, delta.indexKeys);
1418
+ },
1419
+ cdc: config.cdc ?? false,
1420
+ headroom,
1421
+ // Live predicate, same as the user-facing ctx — see \`databaseOptions\`.
1422
+ inTransaction: () => this.isInTransaction(),
1423
+ scheduler,
1424
+ schema: schema as unknown as SchemaLike,
1425
+ sql: this.sql as SqlExec,
1426
+ });
1427
+ }`,V=(m="")=>` const writer = this.adminWriter(${m});`,hn=M?`
1428
+ vectors,`:"",pn=Ve?`
1429
+ orm: bindOrm(facade),`:"",He=Ke?"config.hyperdriveGlobal":"config.d1",mn=ke?` const globalRequest = { ...this.globalCdcOptions(config.cdc ?? false), bookmark: this.getInboundBookmark(), identity, onBookmark: (bookmarkValue: string | undefined) => { if (options.bookmarks !== undefined) { options.bookmarks.value = bookmarkValue; } }, userId };
1430
+ const globalDb: DatabaseWriterLike = ${He}?.(env, globalRequest) ?? globalDbStub;
1431
+ `:"",gn=w&&ke?`
1432
+ protected override async readGlobalShapeRows(resolved: { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string }, identity?: SubscriptionIdentity): Promise<Array<{ doc: Record<string, unknown>; id: string }>> {
1433
+ const env = this.env as Record<string, unknown>;
1434
+ // A shape read performs no writes, so the widened request only needs
1435
+ // the inbound bookmark (pin the membership drain to the caller's own
1436
+ // prior writes) — no \`onBookmark\` here.
1437
+ //
1438
+ // The two identity members are named rather than spread, matching the
1439
+ // dispatch-path request built in \`buildCtx\`. A spread would also hand
1440
+ // the global writer \`ip\`, and \`globalShapeReadKey\` — the per-flush
1441
+ // cache in front of this read — keys on identity + userId only. Equal
1442
+ // keys must mean equal rows, so a field the writer can scope by has to
1443
+ // be either in the key or out of the request; it is out.
1444
+ const globalRequest = { ...this.globalCdcOptions(config.cdc ?? false), bookmark: this.getInboundBookmark(), identity: identity?.identity, userId: identity?.userId };
1445
+ const globalDb: DatabaseWriterLike = ${He}?.(env, globalRequest) ?? globalDbStub;
1446
+ const rows: Array<{ doc: Record<string, unknown>; id: string }> = [];
1447
+
1448
+ let cursor: null | string = null;
1449
+
1450
+ // Drain every page of the global membership so the seed/diff sees the
1451
+ // full rowset (D1 \`findMany\` is paginated). Stop one row past the cap:
1452
+ // a broad \`.global()\` shape would otherwise materialize an unbounded
1453
+ // array before the caller's \`withinGlobalShapeBound\` check rejects it,
1454
+ // so bail early and let the caller fail it closed.
1455
+ do {
1456
+ // eslint-disable-next-line no-await-in-loop -- sequential page drain to assemble the full membership
1457
+ const page = await globalDb.findMany(resolved.table, { cursor, where: resolved.effectiveWhere });
1458
+
1459
+ for (const doc of page.page) {
1460
+ rows.push({ doc, id: String((doc as { _id?: unknown })._id) });
1461
+
1462
+ if (rows.length > ShardDOBase.GLOBAL_SHAPE_MAX_ROWS) {
1463
+ return rows;
1464
+ }
1465
+ }
1466
+
1467
+ cursor = page.isDone ? null : page.continueCursor;
1468
+ } while (cursor !== null);
1469
+
1470
+ return rows;
1471
+ }
1472
+
1473
+ protected override async readGlobalChangedTables(sinceSeq: number, cursorOnly?: boolean): Promise<{ cursor: number; floor?: number; tables: string[] } | undefined> {
1474
+ const env = this.env as Record<string, unknown>;
1475
+ // Metadata-only: this asks the global changelog which TABLES moved,
1476
+ // never what changed in them, so it costs one small read per poll tick
1477
+ // for the whole shard — and a tick whose answer omits a shape's table
1478
+ // skips that shape's membership drain entirely.
1479
+ // Named local, not an inline object literal — same reason as the
1480
+ // dispatch path and \`readGlobalShapeRows\`: \`bookmark\` is not declared
1481
+ // on the narrower Hyperdrive thunk's \`request\` type, and an inline
1482
+ // literal trips an excess-property error (TS2353) that makes the
1483
+ // emitted \`shard.ts\` uncompilable for every Hyperdrive-global app with
1484
+ // a \`defineShape\`. The Hyperdrive factory simply never reads it.
1485
+ const globalRequest = { ...this.globalCdcOptions(config.cdc ?? false), bookmark: this.getInboundBookmark() };
1486
+ const globalDb: DatabaseWriterLike = ${He}?.(env, globalRequest) ?? globalDbStub;
1487
+
1488
+ return globalDb.cdcChangedTables?.(sinceSeq, { cursorOnly });
1489
+ }
1490
+ `:"",fn=oe?`
1491
+ const sourceClientCache = new WeakMap<object, Map<string, SourceClientLike>>();
1492
+ const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
1493
+ `:"",bn=`
1494
+ protected override async runShardInit(): Promise<void> {
1495
+ this.ensureMigrated();
1496
+ ${at?` clearMemoryTables(this.sql as SqlExec, schema as unknown as SchemaLike);
1497
+ `:""}
1498
+ await this.dispatchShardInit();
1499
+ }
1500
+ `,yn=oe?`
1501
+ protected override async pollExternalSources(trace?: TraceRefLike): Promise<number | undefined> {
1502
+ const env = (this.env ?? {}) as Record<string, unknown>;
1503
+ const sourced = Object.entries((schema as unknown as SchemaLike).tables)
1504
+ .map(([table, definition]) => [table, (definition as { externalSource?: ExternalSourceLike }).externalSource] as const)
1505
+ .filter((entry): entry is [string, ExternalSourceLike] => entry[1] !== undefined);
1506
+
1507
+ if (sourced.length === 0) {
1508
+ return undefined;
1509
+ }
1510
+
1511
+ const shardKey = this.currentShardKey();
1512
+ const scheduler = (config.scheduler?.(env) ?? schedulerStub) as SchedulerLike;
1513
+
1514
+ let clients = sourceClientCache.get(this);
1515
+
1516
+ if (clients === undefined) {
1517
+ clients = new Map();
1518
+ sourceClientCache.set(this, clients);
1519
+ }
1520
+
1521
+ let polledAt = sourcePollAtCache.get(this);
1522
+
1523
+ if (polledAt === undefined) {
1524
+ polledAt = new Map();
1525
+ sourcePollAtCache.set(this, polledAt);
1526
+ }
1527
+
1528
+ const now = Date.now();
1529
+ // \`nextDueAt\` tracks the EARLIEST next-due timestamp across every
1530
+ // non-manual source; the shared alarm re-arms there instead of the
1531
+ // fixed 2 s global-shape floor, so a large \`refresh.everyMs\` actually
1532
+ // sleeps until it's due. Stays \`undefined\` when every source is
1533
+ // \`refresh: "manual"\`, so the shared alarm goes idle for this tier.
1534
+ let nextDueAt: number | undefined;
1535
+
1536
+ for (const [table, source] of sourced) {
1537
+ if (source.refresh === "manual") {
1538
+ continue;
1539
+ }
1540
+
1541
+ if (isSourceDue(source.refresh, polledAt.get(table), now)) {
1542
+ try {
1543
+ let client = clients.get(source.binding);
1544
+
1545
+ if (client === undefined) {
1546
+ client = config.sourceClient?.(env, source.binding);
1547
+
1548
+ if (client !== undefined) {
1549
+ clients.set(source.binding, client);
1550
+ }
1551
+ }
1552
+
1553
+ if (client === undefined) {
1554
+ // No SqlClient resolved for this binding (host never wired
1555
+ // \`config.sourceClient\`, or wired it wrong). Surface it in the
1556
+ // Logs panel and stamp \`polledAt\` so a persistent misconfig backs
1557
+ // off to \`refresh.everyMs\` instead of retrying every alarm tick.
1558
+ this.recordExternalSourceError(table, new Error(\`external-source: no sourceClient resolved for binding "\${source.binding}"\`), trace);
1559
+ } else {
1560
+ // A FRESH tracker per TABLE, not one shared across the whole
1561
+ // loop: one table's runaway pull must not spend a budget a
1562
+ // sibling table needs, and a limit hit here must not block
1563
+ // that sibling from getting its own full budget this tick.
1564
+ const writer = createShardCtxDb({
1565
+ // Admin and maintenance writes go through the SAME reactive-cache hooks as
1566
+ // a user mutation. Without this, a studio row edit, a TTL sweep, an admin
1567
+ // import, a CDC apply or a data-migration backfill writes without
1568
+ // invalidating, and the next query answers from the pre-write snapshot.
1569
+ ...this.ctxDbTuning(),
1570
+ broadcast: (delta) => {
1571
+ this.recordChangedTable(delta.table, delta.indexKeys);
1572
+ },
1573
+ cdc: config.cdc ?? false,
1574
+ headroom: this.alarmHeadroom(),
1575
+ scheduler,
1576
+ schema: schema as unknown as SchemaLike,
1577
+ sql: this.sql as SqlExec,
1578
+ });
1579
+
1580
+ if (source.mode === "incremental") {
1581
+ // Incremental (plan 136): pull only rows past the durable
1582
+ // watermark (or a full-pull seed/reconcile), upsert-only.
1583
+ // eslint-disable-next-line no-await-in-loop -- one sourced table at a time; sequential keeps the writer transaction simple
1584
+ await pullExternalSourceIncrementalTick(this.sql as SqlExec, writer, client, table, source, shardKey, now);
1585
+ } else {
1586
+ // eslint-disable-next-line no-await-in-loop -- one sourced table at a time; slices are independent but small and sequential keeps the writer transaction simple
1587
+ await pullExternalSourceTick(this.sql as SqlExec, writer, client, table, source, shardKey);
1588
+ }
1589
+ }
1590
+
1591
+ // Timestamp AFTER the poll finishes, not the batch-start \`now\` — a
1592
+ // poll that outruns \`everyMs\` must not make \`nextDueAt\` (below)
1593
+ // stale-immediate and re-arm the alarm in a hammering loop.
1594
+ polledAt.set(table, Date.now());
1595
+ } catch (error) {
1596
+ if (error instanceof LunoraError && error.code === "TRANSACTION_LIMIT_EXCEEDED") {
1597
+ // Batch full, not a genuine failure. Incremental mode only
1598
+ // persists its watermark AFTER a full apply (see
1599
+ // \`pullExternalSourceIncrementalTick\`), so this throw left it
1600
+ // untouched — the next tick safely re-pulls/re-applies the
1601
+ // SAME slice (idempotent upsert). Full-pull mode (and an
1602
+ // incremental source's own occasional full-pull/reconcile
1603
+ // sweep) has no resumable cursor at all — deliberately not
1604
+ // inventing one here; a retry just redoes the whole pull,
1605
+ // safe if wasteful. Either way: warn instead of
1606
+ // \`recordExternalSourceError\`, and leave \`polledAt\`
1607
+ // UNCHANGED (skip the stamp below via \`continue\`) so this
1608
+ // table stays "due" and \`nextPollAlarmTarget\`'s existing
1609
+ // due-now floor re-arms the shared alarm promptly — not a
1610
+ // fresh \`setAlarm(now)\`.
1611
+ this.recordExternalSourceWarning(
1612
+ table,
1613
+ \`external-source poll for "\${table}" hit the transaction limit mid-batch; resuming next tick: \${error.message}\`,
1614
+ trace,
1615
+ );
1616
+
1617
+ nextDueAt = nextDueAt === undefined ? now : Math.min(nextDueAt, now);
1618
+
1619
+ continue;
1620
+ }
1621
+
1622
+ this.recordExternalSourceError(table, error, trace);
1623
+ // Stamp on failure too, so a persistently failing source throttles
1624
+ // to \`refresh.everyMs\` rather than being hammered every tick.
1625
+ polledAt.set(table, Date.now());
1626
+ }
1627
+ }
1628
+
1629
+ // This source's own next-due time, read AFTER the poll-or-skip above
1630
+ // so a just-polled source reports \`now + everyMs\` (its FRESH due
1631
+ // time), not the stale pre-poll one. An omitted \`refresh\` (poll
1632
+ // every tick) is due again immediately.
1633
+ const sourceNextDueAt = source.refresh === undefined ? now : (polledAt.get(table) ?? now) + source.refresh.everyMs;
1634
+
1635
+ nextDueAt = nextDueAt === undefined ? sourceNextDueAt : Math.min(nextDueAt, sourceNextDueAt);
1636
+ }
1637
+
1638
+ return nextDueAt;
1639
+ }
1640
+ `:"",wn=ot.length>0,kn=`
1641
+ public constructor(state: ShardDOState, env: unknown) {
1642
+ super(state, env, {
1643
+ // Every writer this file builds spreads the ctxDbTuning() slice —
1644
+ // the user-facing ctx and all three admin/maintenance writers — so
1645
+ // the base class can drop its coarse invalidation backstop. Only the
1646
+ // emitter can know that; a hand-written subclass leaves this unset
1647
+ // and keeps the backstop.
1648
+ ctxDbCacheWired: true,
1649
+ ...(config.maxRelationKeys === undefined ? {} : { maxRelationKeys: config.maxRelationKeys }),
1650
+ ...(config.reactiveCache ? { reactiveCache: config.reactiveCache === true ? {} : config.reactiveCache } : {}),
1651
+ ...(config.relationExistsPushDown === undefined ? {} : { relationExistsPushDown: config.relationExistsPushDown }),
1652
+ });
1653
+ ${oe?`
1654
+ const autoSourced = Object.values((schema as unknown as SchemaLike).tables).some((definition) => {
1655
+ const source = (definition as { externalSource?: ExternalSourceLike }).externalSource;
1656
+
1657
+ return source !== undefined && source.refresh !== "manual";
1658
+ });
1659
+
1660
+ if (autoSourced) {
1661
+ void this.scheduleSourcePoll();
1662
+ }
1663
+ `:""}${wn?`
1664
+ if (LUNORA_TTL_SWEEPS.length > 0) {
1665
+ void this.scheduleTtlSweep();
1666
+ }
1667
+ `:""} }
1668
+
1669
+ protected override isCacheableQuery(functionPath: string): boolean {
1670
+ // NOT \`kind === "query"\` alone. A cache hit answers without running
1671
+ // \`handleRpc\`, so the \`internal\` refusal at the top of it is skipped —
1672
+ // and an \`internalQuery\` primed by a trusted system dispatch was then
1673
+ // served to an anonymous client that the refusal would have 404'd.
1674
+ //
1675
+ // \`perDispatch\` is the same argument one layer out: the procedure's
1676
+ // own \`.use()\` chain runs INSIDE \`handleRpc\`, so a hit skips it too.
1677
+ // A \`.use(rateLimit(...))\` query was therefore charged once and then
1678
+ // served from the memo to every later request — each of which still
1679
+ // reached this Durable Object and still cost it a dispatch, so only
1680
+ // the accounting was skipped. Metering and memoizing are mutually
1681
+ // exclusive; the author asking for the first is choosing against the
1682
+ // second.
1683
+ const registered = LUNORA_FUNCTIONS[functionPath];
1684
+
1685
+ return registered?.kind === "query" && registered.visibility !== "internal" && registered.perDispatch !== true;
1686
+ }
1687
+
1688
+ protected override isPaidFunction(functionPath: string): boolean {
1689
+ return LUNORA_FUNCTIONS[functionPath]?.x402 !== undefined;
1690
+ }
1691
+ `,xn=Ve?`
1692
+ const facade = db as unknown as Record<string, ReturnType<typeof bindTableFacade>>;
1693
+ ${k.tables.map(m=>` facade[${JSON.stringify(m.name)}] = bindTableFacade(db, ${JSON.stringify(m.name)});`).join(`
1694
+ `)}
1695
+ `:"",vn=`${he.build}${z.build}${P.build}${Y.build}${pe.build}
1696
+ const secrets = createSecrets(env);
1697
+ `,Sn=`${he.contextField}${z.contextField}${P.contextField}${ne.contextField}${Y.contextField}${pe.contextField}
1698
+ secrets,`,Rn=ne.build,ut=y||g||d||x||R||$,$n=ut?`
1699
+ // ActionCtx-only helpers (external, non-deterministic I/O): constructed
1700
+ // and attached only for an \`action\` so query/mutation ctx never carry them.
1701
+ if (isAction) {
1702
+ ${U.build}${K.build}${Q.build}${j.build}${re.build}${p}${[...y?[" ctx.images = images;"]:[],...g?[" ctx.sql = sql;"]:[],...d?[" ctx.browser = browser;"]:[],...x?[" ctx.r2sql = r2sql;"]:[],...R?[" ctx.pipelines = pipelines;"]:[],...$?[" ctx.x402 = x402;"]:[]].join(`
1703
+ `)}
1704
+ }
1705
+ `:"",Tn=ut?` const isAction = LUNORA_FUNCTIONS[options.functionPath ?? ""]?.kind === "action";
1706
+ `:"";return`${S}${xe.join(`
1707
+ `)}
1708
+
1709
+ type FunctionKind = "action" | "mutation" | "query";
1710
+
1711
+ interface FunctionReference {
1712
+ __lunoraRef: string;
1713
+ }
1714
+
1715
+ /** Foreign-key columns per table (\`v.id("target")\` fields) for the data browser. */
1716
+ const LUNORA_TABLE_REFS = ${I(jt,"Record<string, Record<string, string>>")};
1717
+
1718
+ /** Declared indexes per table (secondary, search, geo, rank, vector) for the schema viewer. */
1719
+ const LUNORA_TABLE_INDEXES = ${I(Wt,'Record<string, Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }>>')};
1720
+
1721
+ /** Columns per table (typed, with PK/FK markers) for the studio's schema diagram, served via \`__lunora_admin__:describeTable\`. */
1722
+ const LUNORA_TABLE_COLUMNS = ${I(Ut,`Record<
1723
+ string,
1724
+ Array<{
1725
+ bucket?: string;
1726
+ enumValues?: string[];
1727
+ isStorage?: boolean;
1728
+ name: string;
1729
+ nullable?: boolean;
1730
+ onDelete?: "cascade" | "restrict" | "set null";
1731
+ optional: boolean;
1732
+ pk?: boolean;
1733
+ ref?: string;
1734
+ type: string;
1735
+ }>
1736
+ >`)};
1737
+
1738
+ /** Storage-key columns per table (\`v.storage(...)\` fields) for the file browser's records↔files join. */
1739
+ const LUNORA_STORAGE_COLUMNS = ${I(Kt,"Record<string, string[]>")};
1740
+
1741
+ /** Declarative TTL policies (\`.ttl(field, { after? })\`) the DO alarm sweep auto-expires rows for. */
1742
+ const LUNORA_TTL_SWEEPS = ${I(ot,"Array<{ after?: number; field: string; softDeleteField?: string; table: string }>")};
1743
+
1744
+ /** Static schema advisories (computed by @lunora/advisor at codegen time) served via \`__lunora_admin__:getAdvisories\`. */
1745
+ const LUNORA_ADVISORIES = ${I(Dt,"AdvisoryFinding[]")};
1746
+
1747
+ /** Every declared procedure (discovered by @lunora/codegen) served via \`__lunora_admin__:getAdvisorProcedures\` — the health map's denominator. */
1748
+ const LUNORA_ADVISOR_PROCEDURES = ${I(Lt,"AdvisorProcedure[]")};
1749
+
1750
+ /** Read-only RLS metadata (policies + roles discovered from \`.use(rls(...))\` chains) served via \`__lunora_admin__:rlsPolicies\` for the studio's RLS inspector. */
1751
+ const LUNORA_RLS_METADATA = ${I(tt,"RlsPoliciesResult")};
1752
+ ${ct}
1753
+ /** 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. */
1754
+ const LUNORA_MASK_METADATA = ${I(Et,"MaskPoliciesResult")};
1755
+
1756
+ /** Read-only storage access-rule metadata (discovered from \`.use(storageRules(...))\` chains) served via \`__lunora_admin__:storageRules\` for the studio's access-rules view. */
1757
+ const LUNORA_STORAGE_RULES = ${I(_t,"StorageRulesResult")};
1758
+
1759
+ /** 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. */
1760
+ const LUNORA_STUDIO_FEATURES = ${I(Pt,"StudioFeaturesResult")};
1761
+ ${q}${J.constant}${Mt}${Bt}${ge}${Fe}${me}${qe}
1762
+ export interface ShardDOConfig {
1763
+ /** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
1764
+ cdc?: boolean;
1765
+ /** Ceiling on the join keys one relation-crossing \`where\` predicate may pre-resolve via semijoin before failing closed. Omit for the engine default. */
1766
+ maxRelationKeys?: number;
1767
+ /** Enable the per-shard reactive query cache: \`true\` for the defaults, or an options object to tune the caps. Query results are memoized by \`(functionPath, args, identity)\` and invalidated by the ctx-db write hooks before the subscription broadcast, so subscribers never observe a pre-write value. Omitted (or \`false\`) keeps every dispatch re-running its handler. */
1768
+ reactiveCache?: boolean | { maxBytes?: number; maxEntries?: number };
1769
+ /** Resolution policy for a relation-crossing \`where\` whose child is co-located in this shard: \`"auto"\` (cost-based, the engine default), \`"always"\` (inline correlated EXISTS) or \`"never"\` (universal semijoin). All three return identical rows. */
1770
+ relationExistsPushDown?: "always" | "auto" | "never";
1771
+ /** Optional telemetry sink. When supplied, each \`ctx.log.*\` call is forwarded to \`sink.onLog\`. Pass the SAME sink you give \`createWorker({ observability })\` (which drives \`onRpc\`) to route both RPC and log events. */
1772
+ observability?: (env: Record<string, unknown>) => TelemetrySink | undefined;
1773
+ /** \`unknown\` because \`@lunora/scheduler\`'s \`Scheduler\` is not assignable to \`SchedulerLike\`; the shard casts it. */
1774
+ scheduler?: (env: Record<string, unknown>) => unknown;
1775
+ /** \`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. */
1776
+ storage?: (env: Record<string, unknown>, origin?: string) => unknown;${zt}${Ie}${z.configField}${P.configField}${Y.configField}${U.configField}${K.configField}${Q.configField}${j.configField}${re.configField}${We}${Ue}${Jt}${Yt}${Xt}
1777
+ }
1778
+ ${C("schedulerStub",tn,["cancel","runAfter","runAt"])}${C("storageStub",nn,["delete","download","getMetadata","getSignedUrl","getUrl","head","list","upload"],{sync:["getUrl"]})}${rn}${fn}${an}${De}${z.stub}${P.stub}${Y.stub}${U.stub}${K.stub}${Q.stub}${j.stub}${re.stub}${et}${ae}
1779
+ // Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
1780
+ // referencing call fails loudly with a clear error instead of overflowing the
1781
+ // stack. Tracked across the awaited handler chain (one DO invocation is
1782
+ // single-threaded), so the counter reflects true nesting depth.
1783
+ const MAX_RUN_DEPTH = 32;
1784
+ let runDepth = 0;
1785
+
1786
+ const dispatchRun = async (
1787
+ expected: FunctionKind,
1788
+ functionPath: string,
1789
+ args: Record<string, unknown>,
1790
+ ctx: unknown,
1791
+ // The kind of the context making the call, and — for a mutation target — the
1792
+ // transaction wrapper to run it under. Both are supplied by \`buildCtx\`, which
1793
+ // is the only place that knows which dispatch this \`ctx\` belongs to. Widened
1794
+ // past \`FunctionKind\` because the registry also carries \`"stream"\`, and
1795
+ // \`undefined\` for a ctx built outside a registered function (admin, migration).
1796
+ callerKind?: "stream" | FunctionKind,
1797
+ runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,
1798
+ ): Promise<unknown> => {
1799
+ // A query is read-only. The ctx object installs \`run*\` on every kind (one
1800
+ // shape, built once), so the TYPE is all that stops a query from writing —
1801
+ // and a cast walks straight past it, inside a subscription re-run that can
1802
+ // execute many times per write. Checked on the CALLER, before the registry
1803
+ // lookup, so \`ctx.runMutation\` from a query fails identically whatever it
1804
+ // aimed at.
1805
+ if (callerKind === "query" && expected !== "query") {
1806
+ throw new LunoraError(
1807
+ "RUN_KIND_FORBIDDEN",
1808
+ \`ctx.run\${expected[0]!.toUpperCase()}\${expected.slice(1)}: a query may only compose other queries (tried to run "\${functionPath}")\`,
1809
+ );
1810
+ }
1811
+
1812
+ const registered = LUNORA_FUNCTIONS[functionPath];
1813
+
1814
+ if (!registered) {
1815
+ throw new Error(\`unknown function: \${functionPath}\`);
1816
+ }
1817
+
1818
+ if (registered.kind !== expected) {
1819
+ throw new Error(\`ctx.run\${expected[0]!.toUpperCase()}\${expected.slice(1)}: "\${functionPath}" is registered as a \${registered.kind}, not a \${expected}\`);
1820
+ }
1821
+
1822
+ if (runDepth >= MAX_RUN_DEPTH) {
1823
+ throw new LunoraError("RUN_DEPTH_EXCEEDED", \`ctx.run*: composition depth limit (\${MAX_RUN_DEPTH}) exceeded — likely a cyclic runQuery/runMutation\`);
1824
+ }
1825
+
1826
+ runDepth += 1;
1827
+
1828
+ try {
1829
+ // A mutation composed from an action (or an http action) is the shape the
1830
+ // docs prescribe for atomicity — "do the transactional reads and writes in
1831
+ // a mutation and call it from the action". It only holds if the composed
1832
+ // dispatch gets the same BEGIN/COMMIT span and single-writer gate the
1833
+ // top-level mutation path gets; without it every write autocommits and a
1834
+ // mid-handler throw leaves the earlier ones durable. A \`runMutation\` from
1835
+ // inside a mutation rides the enclosing span instead of opening a second
1836
+ // one (see \`runMutationTransaction\`).
1837
+ if (runTransactional) {
1838
+ return await runTransactional(async () => registered.handler(ctx, args));
1839
+ }
1840
+
1841
+ return await registered.handler(ctx, args);
1842
+ } finally {
1843
+ runDepth -= 1;
1844
+ }
1845
+ };
1846
+
1847
+ /**
1848
+ * Build the project's shard Durable Object. Export the result as \`ShardDO\`
1849
+ * from the worker entry so wrangler binds it by name.
1850
+ */
1851
+ export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOState, env: unknown) => ShardDOBase =>
1852
+ class extends ShardDOBase {${kn}
1853
+ private migrated = false;
1854
+
1855
+ public override async handleRpc(functionPath: string, args: Record<string, unknown>, headroom?: TransactionHeadroomTracker, scope?: QueryReadScope, bookmarks?: DispatchBookmark): Promise<unknown> {
1856
+ const registered = LUNORA_FUNCTIONS[functionPath];
1857
+
1858
+ // Internal functions are reachable server-side only: via \`ctx.run*\`
1859
+ // composition, or a trusted system dispatch (scheduler/cron, marked by
1860
+ // \`isSystemDispatch()\`). A client RPC never carries that flag, so its
1861
+ // internals stay not-found and never leak across the external boundary.
1862
+ if (!registered || (registered.visibility === "internal" && !this.isSystemDispatch())) {
1863
+ throw new LunoraError("FUNCTION_NOT_FOUND", \`function not registered: \${functionPath}\`);
1864
+ }
1865
+
1866
+ this.ensureMigrated();
1867
+
1868
+ // \`headroom\`, when the caller supplied one, is threaded straight
1869
+ // through to \`buildCtx\` — bypassing its \`this.transactionHeadroom()\`
1870
+ // fallback (the racy shared-field read) entirely for THIS dispatch.
1871
+ // The main \`/rpc\` path always supplies one; \`dispatchLifecycle\` /
1872
+ // \`handleRunAs\` don't mint their own and omit it, so they keep the
1873
+ // prior fallback behavior unchanged.
1874
+ //
1875
+ // \`scope\` is the reactive cache's per-dispatch read capture, threaded
1876
+ // the same way and for the same reason: the ctx-db read hooks resolve
1877
+ // their tracker at READ time, so binding them to the scope THIS
1878
+ // dispatch was handed is what keeps a concurrent query's reads out of
1879
+ // our dep set — and ours out of theirs. A dispatch with no scope (a
1880
+ // mutation, an action, a cache-less shard) builds unbound hooks that
1881
+ // stamp no deps.
1882
+ const ctx = this.buildCtx({ bookmarks, functionPath, headroom, scope, trusted: registered.lifecycle === "init" });
1883
+
1884
+ // A mutation's writes must commit all-or-nothing, so its dispatch runs
1885
+ // under \`runMutationTransaction\` — the ONE place that opens the span,
1886
+ // settles the deferred schedules and flushes the deferred deletes. The
1887
+ // same helper backs \`ctx.runMutation\` and \`runReactor\`, because a
1888
+ // mutation reached by composition is promised exactly these guarantees
1889
+ // and used to get none of them.
1890
+ //
1891
+ // The replay bookkeeping (idempotency dedup row + custom-mutator
1892
+ // watermark advance) commits INSIDE the span via
1893
+ // \`commitMutationBookkeeping\`, so the writes, the dedup row, and the
1894
+ // watermark are atomic — a crash can't leave the writes durable without
1895
+ // the replay guard. It is deliberately NOT in the shared helper: it is
1896
+ // per-REQUEST bookkeeping, and a composed sub-mutation has no request of
1897
+ // its own to record.
1898
+ if (registered.kind === "mutation") {
1899
+ return await this.runMutationTransaction(ctx, async () => {
1900
+ const value = await registered.handler(ctx, args);
1901
+
1902
+ this.commitMutationBookkeeping(value);
1903
+
1904
+ return value;
1905
+ });
1906
+ }
1907
+
1908
+ const result = await registered.handler(ctx, args);
1909
+
1910
+ // An action is not itself transactional, but \`ctx.runMutation\` runs its
1911
+ // submutations on THIS ctx, so anything they queued after their own
1912
+ // transaction settled lands here. A dispatch with nothing queued no-ops.
1913
+ await this.deferPastResponse(flushDeferredDeletes(ctx));
1914
+
1915
+ return result;
1916
+ }
1917
+
1918
+ /**
1919
+ * Run one mutation handler with the atomicity every caller of a mutation is
1920
+ * promised: a BEGIN/COMMIT span (with the single-writer gate
1921
+ * \`ShardRunner.runInTransaction\` composes in front of it), scheduled jobs
1922
+ * held until that span commits, and the deferred object deletes flushed
1923
+ * only once it has.
1924
+ *
1925
+ * Shared by the three entry points that dispatch a mutation handler — the
1926
+ * top-level RPC, \`ctx.runMutation\` from an action or http action, and a
1927
+ * reactor — because the guarantee is a property of the mutation, not of how
1928
+ * it was reached. The docs tell users to put the transactional part in a
1929
+ * mutation and call it from the action precisely so that composition is
1930
+ * atomic.
1931
+ *
1932
+ * A \`ctx.runMutation\` issued from inside a mutation is NESTED: SQLite-in-DO
1933
+ * has no savepoints and \`runInTransaction\` rejects a second open, so it
1934
+ * rides the enclosing span, which also owns its commit and therefore its
1935
+ * scheduler flush.
1936
+ *
1937
+ * Both deferral windows are opened here and settled against the SAME
1938
+ * outcome. The delete window is what keeps a composed mutation's queued
1939
+ * object deletes off the caller's flush when it rolls back: \`ctx\` is
1940
+ * shared, so without a window the keys are indistinguishable from the
1941
+ * action's own and the action's flush destroys objects whose rows the
1942
+ * rollback put back.
1943
+ */
1944
+ private async runMutationTransaction<T>(ctx: unknown, work: () => Promise<T>): Promise<T> {
1945
+ const settleSchedules = beginDeferredSchedules(ctx as { scheduler?: unknown });
1946
+ // Synchronous and non-throwing (it only moves queue entries), so it can
1947
+ // settle first on every path and cannot mask the outcome.
1948
+ const settleDeletes = beginDeferredDeletes(ctx);
1949
+
1950
+ if (this.isInTransaction()) {
1951
+ try {
1952
+ const nested = await work();
1953
+
1954
+ settleDeletes(true);
1955
+ await settleSchedules(true);
1956
+
1957
+ return nested;
1958
+ } catch (error) {
1959
+ settleDeletes(false);
1960
+ await settleSchedules(false);
1961
+
1962
+ throw error;
1963
+ }
1964
+ }
1965
+
1966
+ let result: T;
1967
+
1968
+ try {
1969
+ result = await this.runInTransaction(work);
1970
+ } catch (error) {
1971
+ // The span rolled back, so the rows are gone and the jobs that were
1972
+ // to run "after I commit" must go with them. Dropping them is the
1973
+ // whole point of buffering: a persisted job for a write that never
1974
+ // landed fires against state that does not exist.
1975
+ //
1976
+ // The queued object deletes go the same way, and for a sharper
1977
+ // reason: the rows they were to clean up after are still there, and
1978
+ // an R2 delete cannot be undone.
1979
+ settleDeletes(false);
1980
+ await settleSchedules(false);
1981
+
1982
+ throw error;
1983
+ }
1984
+
1985
+ // Committed, so this span's queued keys join whatever the flush below
1986
+ // (or an enclosing span's) will drain.
1987
+ settleDeletes(true);
1988
+
1989
+ // Committed. Schedules first and AWAITED: \`runAfter(0, ...)\` is
1990
+ // documented as the deterministic equivalent of an \`afterCommit\` hook,
1991
+ // so the job must be enqueued after the commit, and a failure to enqueue
1992
+ // it must be visible rather than swallowed.
1993
+ //
1994
+ // In a \`finally\`, because both halves of this are post-commit cleanup
1995
+ // and neither is allowed to cancel the other. \`settleSchedules\` drains
1996
+ // its whole queue and then rethrows what failed, so a SchedulerDO that
1997
+ // refused one job used to take the object cleanup down with it — the
1998
+ // rows were durably gone and their objects leaked with nothing logged.
1999
+ try {
2000
+ await settleSchedules(true);
2001
+ } finally {
2002
+ // Then the objects whose rows are now durably gone. Deliberately AFTER
2003
+ // the span, never inside it: an R2 delete cannot roll back, so a delete
2004
+ // issued from within a transaction that later aborts destroys data the
2005
+ // surviving row still points at.
2006
+ //
2007
+ // \`flushDeferredDeletes\` never rejects — the mutation has already
2008
+ // succeeded, so a failed cleanup must not turn into a failed response.
2009
+ // A leaked object is reported through \`ctx.log\` instead, with its key.
2010
+ await this.deferPastResponse(flushDeferredDeletes(ctx));
2011
+ }
2012
+
2013
+ return result;
2014
+ }
2015
+
2016
+ // Only a \`mutation\` may enter the base class's single-writer gate for
2017
+ // mutation-replay dedup — it is \`blockConcurrencyWhile\`, so gating an
2018
+ // action would stall every other dispatch on the shard for the length of
2019
+ // its outbound I/O, on nothing but a caller-supplied header. Unregistered
2020
+ // paths answer \`false\`; \`handleRpc\` above rejects them anyway.
2021
+ protected override isMutationFunction(functionPath: string): boolean {
2022
+ return LUNORA_FUNCTIONS[functionPath]?.kind === "mutation";
2023
+ }
2024
+
2025
+ // The scheduler the deferred-schedule outbox retries through. Only the
2026
+ // CONFIGURED one: falling back to \`schedulerStub\` would have an app with no
2027
+ // scheduler retry every entry against a thrower until the attempt ceiling
2028
+ // parked it, and there is nothing to park — an app with no scheduler has no
2029
+ // \`ctx.scheduler\` call that reached the buffer in the first place.
2030
+ protected override scheduleOutboxScheduler(): SchedulerLike | undefined {
2031
+ return config.scheduler?.((this.env ?? {}) as Record<string, unknown>) as SchedulerLike | undefined;
2032
+ }
2033
+ ${it.override}
2034
+ protected override async executeSubscription(functionPath: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): Promise<{ ranges?: Map<string, KeyRange[]>; result: unknown; tables: Set<string> } | null> {
2035
+ const registered = LUNORA_FUNCTIONS[functionPath];
2036
+
2037
+ if (!registered || registered.kind !== "query" || registered.visibility === "internal") {
2038
+ return null;
2039
+ }
2040
+
2041
+ this.ensureMigrated();
2042
+
2043
+ const footprint = createReadFootprint();
2044
+ // Identity is threaded EXPLICITLY from the (deferred/interleaved)
2045
+ // subscription caller — never read from the shared per-request field
2046
+ // here — so a concurrent RPC can't leak its identity into this re-run.
2047
+ const ctx = this.buildCtx({ functionPath, headroom: this.subscriptionHeadroom(), identity, onRead: footprint.onRead, onReadRange: footprint.onReadRange });
2048
+ const result = await registered.handler(ctx, args);
2049
+
2050
+ // Ranges come from THIS run's reads, never a shared field on the DO:
2051
+ // a deferred re-run interleaves with unrelated dispatches.
2052
+ return { ranges: footprint.ranges(), result, tables: footprint.tables };
2053
+ }
2054
+
2055
+ protected override executeStream(functionPath: string, args: Record<string, unknown>, identity?: SubscriptionIdentity): null | { durable?: { ttlMs?: number }; iterator: (signal: AbortSignal) => AsyncIterable<unknown> } {
2056
+ const registered = LUNORA_FUNCTIONS[functionPath];
2057
+
2058
+ if (!registered || registered.kind !== "stream" || registered.visibility === "internal") {
2059
+ return null;
2060
+ }
2061
+
2062
+ this.ensureMigrated();
2063
+
2064
+ return {
2065
+ ...(registered.durable ? { durable: registered.durable as { ttlMs?: number } } : {}),
2066
+ // Identity threaded EXPLICITLY from the socket, exactly as
2067
+ // \`executeSubscription\` above: the iterator is pulled after this
2068
+ // frame returned, interleaved with unrelated dispatches, so
2069
+ // \`buildCtx\`'s per-request fallback would run an \`rls()\` /
2070
+ // \`ctx.auth\` stream as nobody — or as a concurrent RPC's caller.
2071
+ iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath, identity }), args, signal),
2072
+ };
2073
+ }
2074
+ ${Vt}${Qt}${gn}${yn}
2075
+ protected override lifecycleHookPaths(event: "connect" | "disconnect" | "init" | "reactor" | "whisper"): readonly string[] {
2076
+ return LUNORA_LIFECYCLE_HOOKS[event];
2077
+ }
2078
+ ${bn}
2079
+ // One \`onQueryChange\` dispatch. Mirrors \`executeSubscription\` — the
2080
+ // socket-terminated half of the same reactivity — and differs only in who
2081
+ // consumes the result: the base's \`dispatchReactors\` stores the digest as
2082
+ // the new baseline and the footprint as the "should I even re-run this"
2083
+ // gate, instead of pushing a frame down a socket.
2084
+ //
2085
+ // Dispatched through \`runMutationTransaction\` because a reactor handler is
2086
+ // a mutation: its writes must commit all-or-nothing, the jobs it schedules
2087
+ // must wait for that commit, and the objects its row deletes orphaned must
2088
+ // be flushed once it lands. Safe to open a span here: the refresh drain
2089
+ // runs OUTSIDE any dispatch transaction (it is post-flush background work),
2090
+ // so this never nests.
2091
+ //
2092
+ // NO identity is threaded, and the ctx is built \`trusted\`. A reactor fires
2093
+ // because data moved, not because anyone asked, so there is no user for RLS
2094
+ // to scope to — it runs in the same system tier as migrations and the
2095
+ // external-source poll loop. Inheriting the shared per-request identity
2096
+ // would instead run an app's reactor as whichever user happened to write
2097
+ // last, which is worse than running it as nobody.
2098
+ protected override async runReactor(functionPath: string, previousDigest?: string): Promise<{ digest: string; ran: boolean; tables: readonly string[] } | undefined> {
2099
+ const registered = LUNORA_FUNCTIONS[functionPath];
2100
+
2101
+ if (!registered || registered.kind !== "mutation") {
2102
+ return undefined;
2103
+ }
2104
+
2105
+ this.ensureMigrated();
2106
+
2107
+ const footprint = createReadFootprint();
2108
+ const ctx = this.buildCtx({ functionPath, headroom: this.subscriptionHeadroom(), onRead: footprint.onRead, onReadRange: footprint.onReadRange, trusted: true });
2109
+ const outcome = (await this.runMutationTransaction(ctx, async () =>
2110
+ registered.handler(ctx, { previousDigest } as unknown as Record<string, unknown>),
2111
+ )) as {
2112
+ digest: string;
2113
+ ran: boolean;
2114
+ };
2115
+
2116
+ return { digest: outcome.digest, ran: outcome.ran, tables: [...footprint.tables] };
2117
+ }
2118
+
2119
+
2120
+ protected override tableRefs(table: string): Record<string, string> | undefined {
2121
+ return LUNORA_TABLE_REFS[table];
2122
+ }
2123
+
2124
+ protected override tableIndexes(table: string): Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }> {
2125
+ return LUNORA_TABLE_INDEXES[table] ?? [];
2126
+ }
2127
+
2128
+ protected override ttlSweeps(): ReadonlyArray<{ after?: number; field: string; softDeleteField?: string; table: string }> {
2129
+ return LUNORA_TTL_SWEEPS;
2130
+ }
2131
+
2132
+ protected override tableColumns(table: string): Array<{
2133
+ bucket?: string;
2134
+ enumValues?: string[];
2135
+ isStorage?: boolean;
2136
+ name: string;
2137
+ nullable?: boolean;
2138
+ onDelete?: "cascade" | "restrict" | "set null";
2139
+ optional: boolean;
2140
+ pk?: boolean;
2141
+ ref?: string;
2142
+ type: string;
2143
+ }> {
2144
+ return LUNORA_TABLE_COLUMNS[table] ?? [];
2145
+ }
2146
+
2147
+ protected override storageColumns(): Record<string, string[]> {
2148
+ return LUNORA_STORAGE_COLUMNS;
2149
+ }
2150
+
2151
+ protected override rlsMetadata(): RlsPoliciesResult {
2152
+ return LUNORA_RLS_METADATA;
2153
+ }
2154
+
2155
+ protected override maskMetadata(): MaskPoliciesResult {
2156
+ return LUNORA_MASK_METADATA;
2157
+ }
2158
+
2159
+ protected override storageRulesMetadata(): StorageRulesResult {
2160
+ return LUNORA_STORAGE_RULES;
2161
+ }
2162
+
2163
+ protected override studioFeatures(): StudioFeaturesResult {
2164
+ return LUNORA_STUDIO_FEATURES;
2165
+ }
2166
+ ${J.evaluateOverride}${J.subscriptionOverride}${Ft}${qt}
2167
+ protected override advisories(): AdvisoryFinding[] {
2168
+ return LUNORA_ADVISORIES;
2169
+ }
2170
+
2171
+ protected override advisorProcedures(): AdvisorProcedure[] {
2172
+ return LUNORA_ADVISOR_PROCEDURES;
2173
+ }
2174
+
2175
+ ${un}
2176
+
2177
+ protected override async runShardDataMigration(args: RunShardMigrationArgs): Promise<MigrationRunResult> {
2178
+ this.ensureMigrated();
2179
+
2180
+ // Falls back to the framework's reserved re-projection backfill
2181
+ // (\`__lunora_reproject__<table>\`), which rewrites rows whose
2182
+ // v.bigint()/v.bytes() columns are still stored in the pre-projection
2183
+ // tagged form. Built here rather than at module scope because it
2184
+ // needs this shard's \`sql\` handle to tell a legacy row from a
2185
+ // current one.
2186
+ const migration = LUNORA_MIGRATIONS[args.id] ?? buildReprojectionMigration(args.id, schema as unknown as SchemaLike, this.sql as SqlExec);
2187
+
2188
+ if (!migration) {
2189
+ throw new LunoraError("MIGRATION_NOT_FOUND", \`data migration "\${args.id}" is not registered\`, { status: 404 });
2190
+ }
2191
+
2192
+ ${V()}
2193
+
2194
+ return runDataMigration({
2195
+ batchSize: args.batchSize,
2196
+ direction: args.direction,
2197
+ dryRun: args.dryRun,
2198
+ maxBatches: args.maxBatches,
2199
+ migration: migration as unknown as DataMigrationLike,
2200
+ // Flush after each batch so live migrationStatus subscribers see
2201
+ // progress mid-run (the base method records the reserved state
2202
+ // table the raw-SQL progress write is invisible to the tracker).
2203
+ onBatch: () => this.flushMigrationProgress(),
2204
+ sql: this.sql as SqlExec,
2205
+ writer,
2206
+ });
2207
+ }
2208
+
2209
+ protected override async runShardWrite(args: RunShardWriteArgs, headroom?: TransactionHeadroomTracker): Promise<RunShardWriteResult> {
2210
+ const definition = (schema as unknown as SchemaLike).tables[args.table];
2211
+
2212
+ if (!definition) {
2213
+ throw new LunoraError("UNKNOWN_TABLE", \`unknown table: \${args.table}\`, { status: 404 });
2214
+ }
2215
+
2216
+ // \`.global()\` tables live in D1, not this DO's SQLite — editing them
2217
+ // here would corrupt nothing but would fail confusingly, so reject up
2218
+ // front with a clear code the studio can surface.
2219
+ if (definition.shardMode?.kind === "global") {
2220
+ throw new LunoraError("GLOBAL_TABLE_NOT_EDITABLE", \`table "\${args.table}" is global; edit it through D1, not the shard\`, { status: 400 });
2221
+ }
2222
+
2223
+ this.ensureMigrated();
2224
+
2225
+ ${V("headroom ?? this.transactionHeadroom()")}
2226
+
2227
+ if (args.op === "insert") {
2228
+ const id = await writer.insert(args.table, args.doc ?? {});
2229
+
2230
+ return { id, op: "insert" };
2231
+ }
2232
+
2233
+ // Every by-id op PINS \`args.table\`. Unpinned, \`locateRowById\` probes
2234
+ // every non-global table for the id, so a request naming table A with an
2235
+ // id belonging to table B locates and mutates B's row — the by-id IDOR
2236
+ // the per-table \`ctx.db.<table>\` facade pins against. Pinning also stops
2237
+ // an absent row falling through to the \`.global()\` D1 twin, though that
2238
+ // branch is already unreachable here: \`adminWriter\` is built without a
2239
+ // \`globalDb\`, so a miss throws \`NOT_FOUND\` either way.
2240
+ // \`hard\` is set only by the BULK delete arm, which needs the row gone
2241
+ // from the physical table for its next batch's scan to make progress.
2242
+ // A single-row \`writeRow\` delete never carries it, so it keeps the
2243
+ // table's declared \`.softDelete()\` behaviour.
2244
+ if (args.op === "delete") {
2245
+ await writer.delete(args.id ?? "", args.table, { hard: args.hard === true });
2246
+
2247
+ return { id: args.id ?? null, op: "delete" };
2248
+ }
2249
+
2250
+ if (args.op === "replace") {
2251
+ await writer.replace(args.id ?? "", args.doc ?? {}, args.table);
2252
+
2253
+ return { id: args.id ?? null, op: "replace" };
2254
+ }
2255
+
2256
+ await writer.patch(args.id ?? "", args.doc ?? {}, args.table);
2257
+
2258
+ return { id: args.id ?? null, op: "patch" };
2259
+ }
2260
+
2261
+ // The base \`ShardDO\` cannot build a schema-aware writer, so its
2262
+ // \`runShardExport\`/\`runShardImport\` return \`[]\` and \`{inserted:{}}\`.
2263
+ // Those are success-shaped: an empty export is what a correct export of
2264
+ // an empty shard looks like, so a backup of a populated sharded schema
2265
+ // reported success and wrote nothing, and an import reported success and
2266
+ // dropped every row. Overriding both here is what makes the two admin
2267
+ // RPCs real for a \`.shardBy()\` table.
2268
+ //
2269
+ // NOTE: the admin export RPC is not paginated — \`parseExportShardArgs\`
2270
+ // takes \`tables\`/\`batchSize\` but no cursor, and the coordinator issues
2271
+ // one call per shard — so the rows are collected into a single response.
2272
+ // \`batchSize\` bounds the SQLite scan page, not the reply. A shard holding
2273
+ // more rows than fit in the DO's memory budget will therefore fail the
2274
+ // export rather than stream it. That is a loud failure where the bug this
2275
+ // replaces was a silent empty one, but a cursor on the RPC is what makes
2276
+ // this a complete backup path for large shards.
2277
+ protected override async runShardExport(args: RunShardExportArgs): Promise<ExportRow[]> {
2278
+ this.ensureMigrated();
2279
+
2280
+ ${V()}
2281
+
2282
+ const rows: ExportRow[] = [];
2283
+
2284
+ // \`exportShardRows\` walks in keyset batches so a large table does not
2285
+ // materialize at once inside the generator; the admin RPC's response
2286
+ // is a single array, so it is collected here rather than streamed.
2287
+ for await (const row of exportShardRows(writer, schema as unknown as SchemaLike, { batchSize: args.batchSize, tables: args.tables })) {
2288
+ rows.push(row);
2289
+ }
2290
+
2291
+ return rows;
2292
+ }
2293
+
2294
+ protected override async runShardImport(args: RunShardImportArgs): Promise<ImportShardResult> {
2295
+ this.ensureMigrated();
2296
+
2297
+ ${V()}
2298
+
2299
+ // \`importShardRows\` inserts with \`allowExplicitId\`, so a source
2300
+ // database's \`_id\`s carry across verbatim and every foreign key
2301
+ // referencing them stays valid without a second remapping pass.
2302
+ return importShardRows(writer, schema as unknown as SchemaLike, { rows: args.rows, startLine: args.startLine });
2303
+ }
2304
+
2305
+ protected override async runShardRankBefore(args: RunShardRankBeforeArgs): Promise<{ before: number; total: number }> {
2306
+ this.ensureMigrated();
2307
+
2308
+ ${V()}
2309
+
2310
+ // \`rankBefore\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
2311
+ // but the shard writer from \`createShardCtxDb\` always defines it.
2312
+ if (!writer.rankBefore) {
2313
+ throw new LunoraError("NOT_IMPLEMENTED", "rankBefore is unavailable on the shard writer", { status: 500 });
2314
+ }
2315
+
2316
+ return writer.rankBefore(args.table, args.index, {
2317
+ partitionKey: args.partitionKey,
2318
+ rowId: args.rowId,
2319
+ sortValues: args.sortValues,
2320
+ });
2321
+ }
2322
+
2323
+ protected override async runShardFindRelated(args: RunShardFindRelatedArgs): Promise<RelatedPage> {
2324
+ this.ensureMigrated();
2325
+
2326
+ ${V()}
2327
+
2328
+ // \`related\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
2329
+ // but the shard writer from \`createShardCtxDb\` always defines it — it
2330
+ // derives the edge set from this app's schema.
2331
+ if (!writer.related) {
2332
+ throw new LunoraError("NOT_IMPLEMENTED", "findRelated is unavailable on the shard writer", { status: 500 });
2333
+ }
2334
+
2335
+ return writer.related(
2336
+ { id: args.id, table: args.table },
2337
+ { cursor: args.cursor, depth: args.depth, direction: args.direction, edges: args.edges, limit: args.limit },
2338
+ );
2339
+ }
2340
+
2341
+ protected override async runShardRankPage(args: RunShardRankPageArgs): Promise<ShardRankPageResult> {
2342
+ this.ensureMigrated();
2343
+
2344
+ ${V()}
2345
+
2346
+ // \`rankPageRows\` is optional on \`DatabaseWriterLike\` (the D1 twin omits it),
2347
+ // but the shard writer from \`createShardCtxDb\` always defines it. The sort
2348
+ // directions live in the schema's rankIndex, so the shard reads them itself;
2349
+ // \`args.directions\` is only the coordinator's comparator hint and isn't forwarded.
2350
+ if (!writer.rankPageRows) {
2351
+ throw new LunoraError("NOT_IMPLEMENTED", "rankPage is unavailable on the shard writer", { status: 500 });
2352
+ }
2353
+
2354
+ return writer.rankPageRows(args.table, args.index, {
2355
+ after: args.after,
2356
+ cursor: args.cursor,
2357
+ partitionKey: args.partitionKey,
2358
+ take: args.take,
2359
+ });
2360
+ }
2361
+
2362
+ protected override async runShardApplyCdc(args: RunShardApplyCdcArgs): Promise<{ applied: number }> {
2363
+ this.ensureMigrated();
2364
+
2365
+ ${V()}
2366
+
2367
+ await applyCdcChanges(writer, args.changes);
2368
+
2369
+ return { applied: args.changes.length };
2370
+ }
2371
+
2372
+ protected override ensureMigrated(): void {
2373
+ if (this.migrated) {
2374
+ return;
2375
+ }
2376
+
2377
+ runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false${de} });
2378
+ this.migrated = true;
2379
+ }
2380
+ ${Qe?`
2381
+ protected override runShardSearchBackfill(options: { maxPages?: number }): SearchBackfillProgress {
2382
+ // Migrations create the fts5 companions; without them every backfill
2383
+ // statement would raise "no such table" on a shard that has never
2384
+ // served a request.
2385
+ this.ensureMigrated();
2386
+
2387
+ return backfillSearchIndexes(this.sql as SqlExec, schema as unknown as SchemaLike, options);
2388
+ }
2389
+ `:""}${cn}
2390
+ private buildCtx(options: { bookmarks?: DispatchBookmark; functionPath?: string; headroom?: TransactionHeadroomTracker; identity?: SubscriptionIdentity; onRead?: (table: string, idOrScan?: string) => void; onReadRange?: (range: KeyRange) => void; scope?: QueryReadScope; trusted?: boolean } = {}): unknown {
2391
+ const env = (this.env ?? {}) as Record<string, unknown>;
2392
+ // The caller context this ctx runs under, resolved ONCE on one
2393
+ // discriminant. When the caller threads an explicit identity
2394
+ // (subscription seed / refresh / stream pull / shape resolve — all
2395
+ // deferred or interleaved), it is used by value and the shared
2396
+ // per-request fields are never read, because a concurrent RPC owns
2397
+ // them: a refresh in particular runs inside the writing dispatch's
2398
+ // \`flushChangedTables\`, BEFORE its \`endDispatch\`, so \`getCurrentIp()\`
2399
+ // there is the MUTATING caller's address. Otherwise (the synchronous
2400
+ // RPC dispatch path) it falls back to those fields as before.
2401
+ //
2402
+ // One expression, not one per field: three parallel ternaries on the
2403
+ // same discriminant is how \`ip\` came to be the only one still reading
2404
+ // the shared field.
2405
+ const caller: SubscriptionIdentity = options.identity ?? {
2406
+ identity: this.getCurrentIdentity(),
2407
+ ip: this.getCurrentIp(),
2408
+ userId: this.getCurrentUserId(),
2409
+ };
2410
+ const { identity, ip, userId } = caller;
2411
+ ${ln}${ue}${vn}${Ee}${Pe}${Le}${Be}
2412
+ // Which dispatch this ctx belongs to. Drives the two deferral facades
2413
+ // below and the \`ctx.run*\` caller guard; a ctx built for an
2414
+ // admin/lifecycle path has no registered function, and so no kind.
2415
+ const contextKind = LUNORA_FUNCTIONS[options.functionPath ?? ""]?.kind;
2416
+ // \`list\`/\`get\` are the two methods \`ctx.db.system.query("_scheduled_functions")\`
2417
+ // reaches through, and pending jobs live in the SchedulerDO — nothing the
2418
+ // CDC changelog records — so reading them must forfeit a delta resume.
2419
+ // The scheduler's own \`runAfter\`/\`runAt\`/\`cancel\` are writes and stay unstamped.
2420
+ const schedulerBase = markUnvouchableReads((config.scheduler?.(env) ?? schedulerStub) as SchedulerLike, options.onRead, ["get", "list"]);
2421
+ // \`ctx.scheduler.runAfter(0, ...)\` is documented as the deterministic
2422
+ // equivalent of an \`afterCommit\` hook, and the SchedulerDO persists the
2423
+ // job the moment it is called — so inside a mutation the call is BUFFERED
2424
+ // and replayed after the transaction commits (a rollback drops it).
2425
+ // Wrapped on the same dispatches as the deferred-delete queue and for the
2426
+ // same reason: \`ctx.runMutation\` hands the CALLER's ctx to the callee, so
2427
+ // a mutation reached from an action schedules through the action's ctx.
2428
+ // An action's own schedules stay immediate — the window is only open
2429
+ // while a transaction is (see \`runMutationTransaction\`).
2430
+ //
2431
+ // Every kind but \`query\` is wrapped, because \`runMutationTransaction\`
2432
+ // is installed on \`ctx.runMutation\` for every kind but \`query\`: a
2433
+ // STREAM ctx, and an admin/lifecycle ctx with no registered kind at all,
2434
+ // both get the BEGIN/COMMIT span for a mutation they compose, and used
2435
+ // to get it with the deferral missing — so that mutation's job reached
2436
+ // the SchedulerDO while its transaction was still open, and survived the
2437
+ // rollback. A query cannot host a mutation handler (\`dispatchRun\`
2438
+ // refuses it) and its ctx is built on the hot subscription path, so it
2439
+ // stays unwrapped.
2440
+ //
2441
+ // Wrapped OUTSIDE the read-stamping facade so \`get\`/\`list\` stay stamped.
2442
+ //
2443
+ // \`this.scheduleOutbox()\` is what keeps the buffer honest. Everything
2444
+ // between the COMMIT and the scheduler's acknowledgement is outside the
2445
+ // transaction, so a failure there leaves writes that are durable and a
2446
+ // job that exists nowhere — and the mutation's replay-dedup row committed
2447
+ // inside the span, so the client's retry is answered from cache and told
2448
+ // it succeeded. The outbox takes custody of each buffered call inside the
2449
+ // transaction and releases it once the scheduler has the job; what
2450
+ // survives is retried by \`pollScheduleOutbox\` on the shared poll alarm.
2451
+ const scheduler = contextKind === "query" ? schedulerBase : withDeferredSchedules(schedulerBase, this.scheduleOutbox());
2452
+ // Build the storage adapter once and share it between \`ctx.storage\`
2453
+ // and \`ctx.db.system._storage\` so both read the same R2 binding. The
2454
+ // \`storageStub\` fallback satisfies SystemReaderStorageLike structurally
2455
+ // (its \`list\`/\`getMetadata\` throw the "no storage configured" error).
2456
+ //
2457
+ // Wrapped BEFORE the split so both consumers share one stamping facade.
2458
+ // Only the methods that actually reach R2 are stamped: \`getUrl\` and
2459
+ // \`getSignedUrl\` build a URL from the configured base (and an HMAC) and
2460
+ // read nothing, and they are what handlers overwhelmingly call — stamping
2461
+ // them would forfeit resumes for a dependency that cannot move the result.
2462
+ // \`bucket\` IS stamped: it hands back a sub-facade this wrapper does not
2463
+ // reach, so the selection is the last point at which the read can be seen.
2464
+ //
2465
+ // The request origin is the storage base fallback for mutations and
2466
+ // actions on the synchronous dispatch only. A deferred caller
2467
+ // (\`options.identity\` set) runs outside the dispatch that owns the
2468
+ // field, the same hazard \`getCurrentIp\` has. A query never gets it: a
2469
+ // live query re-runs with no request behind it, and the reactive
2470
+ // cache does not key on the host, so a query that signs URLs needs a
2471
+ // declared \`publicBaseUrl\` either way.
2472
+ const requestOrigin = contextKind !== "query" && options.identity === undefined ? this.getCurrentOrigin() : undefined;
2473
+ const storage = markUnvouchableReads(asBucketStorage(config.storage?.(env, requestOrigin) ?? storageStub) as SystemReaderStorageLike, options.onRead, [
2474
+ "bucket",
2475
+ "download",
2476
+ "getMetadata",
2477
+ "list",
2478
+ ]);
2479
+ // \`ctx.storage.deleteAfterCommit(key)\`, on every dispatch that can host
2480
+ // a MUTATION handler — which is not only a mutation dispatch:
2481
+ // \`ctx.runMutation\` hands the CALLER's ctx to the callee, so a mutation
2482
+ // reached from an action runs on the action's ctx. Wrapping mutations
2483
+ // alone made that composition throw a bare TypeError on a method the
2484
+ // handler's own type promises. Queries stay unwrapped: they cannot host
2485
+ // one, and their ctx is built on the hot subscription path.
2486
+ //
2487
+ // Every dispatch wrapped here also flushes (see \`handleRpc\` and
2488
+ // \`runReactor\`) — a queue nothing drains leaks silently, which is worse
2489
+ // than not having the method at all.
2490
+ //
2491
+ // Wrapped OUTSIDE the read-stamping facade so \`bucket(name)\` still
2492
+ // resolves through it and stays stamped, and applied only to
2493
+ // \`ctx.storage\` so \`ctx.db.system._storage\` (which shares the adapter
2494
+ // above) is untouched.
2495
+ const contextStorage = contextKind === "mutation" || contextKind === "action" ? withDeferredDeletes(storage) : storage;
2496
+ // \`ctx.log\`: the DO base builds the attributed logger (structured
2497
+ // fields + \`.with(...)\` child + trace correlation) and routes each call
2498
+ // to the optional \`observability\` sink. It also buffers the line (studio
2499
+ // Logs panel) and emits a structured console event the dev-server formats.
2500
+ //
2501
+ // Resolved BEFORE \`ctx.db\` below, because the database is wrapped in
2502
+ // auto-instrumentation that needs the sink, the trace anchor, and the
2503
+ // dispatch's wide-event handle.
2504
+ const observability = config.observability?.(env);
2505
+ const logFunctionPath = options.functionPath ?? "";
2506
+ const log = this.makeLogger(logFunctionPath, observability);
2507
+ const traceAnchor = this.resolveDispatchAnchor(Boolean(options.identity));
2508
+
2509
+ // \`ctx.span\`: the handle onto the DISPATCH's own span — the wide-event
2510
+ // surface. Attributes attached here accumulate for the whole request
2511
+ // and are folded into the one span it already emits, instead of
2512
+ // becoming N separate log lines. Shares \`traceAnchor\` with \`ctx.trace\`
2513
+ // so both write to the same trace.
2514
+ const span = this.makeDispatchSpan(traceAnchor, observability);
2515
+
2516
+ ${mn} // \`ctx.db\`, wrapped in automatic instrumentation: by default this
2517
+ // adds aggregate counters (call count, total time, per-operation
2518
+ // breakdown) to the wide event rather than a span per call, so a
2519
+ // handler making hundreds of queries stays readable. See
2520
+ // \`instrumentDatabase\` for the \`"spans"\` / \`"off"\` levels.
2521
+ const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${dn}), logFunctionPath, traceAnchor, observability);
2522
+ ${xn}${je}
2523
+
2524
+ // \`ctx.trace\` / \`ctx.metrics\`: spans and measurements to the same sink.
2525
+ // The trace anchor is threaded explicitly for the same reason \`identity\`
2526
+ // is — a deferred caller (a subscription re-run) must not inherit the
2527
+ // writing mutation's trace from the shared per-request field.
2528
+ const trace = this.makeTracer(logFunctionPath, observability, traceAnchor);
2529
+ const metrics = this.makeMetrics(logFunctionPath, observability);
2530
+ ${Rn}
2531
+ // \`ctx.now\`: the wall-clock instant (epoch ms) this function began,
2532
+ // captured ONCE so the whole handler body sees a single stable value.
2533
+ // A \`query\` handler is re-run by every live subscription that reads it,
2534
+ // so \`Date.now()\` there flickers between re-evaluations. A \`mutation\`
2535
+ // handler does not replay under ordinary dispatch (an OCC conflict throws
2536
+ // to the caller rather than retrying internally), but one called from a
2537
+ // workflow step or queue consumer runs again when that step replays. Both
2538
+ // read time through \`ctx.now\` — the \`nondeterministic_query_mutation\`
2539
+ // advisor flags \`Date.now()\`. Actions may still use ambient \`Date.now()\`.
2540
+ const now = Date.now();
2541
+
2542
+ const ctx: Record<string, unknown> = {
2543
+ auth: {
2544
+ getIdentity: async () => identity ?? null,
2545
+ userId: userId ?? null,
2546
+ },
2547
+ db,
2548
+ // Instrumented \`fetch\`: a CLIENT span per outbound call plus W3C
2549
+ // \`traceparent\` propagation, so time spent in a downstream service
2550
+ // is visible and its spans join this trace. Degrades to the bare
2551
+ // global when no sink is configured.
2552
+ fetch: this.makeFetch(logFunctionPath, traceAnchor, observability),
2553
+ // A GETTER, not a plain field, so the reactive cache can tell a
2554
+ // handler that reads the caller's address from the majority that
2555
+ // never do. \`ctx.ip\` is per-request state the memo must be keyed
2556
+ // by — otherwise two anonymous callers share one entry and the
2557
+ // second is served the first's address — but keying every entry
2558
+ // by address would shard the cache per client for every query.
2559
+ // Reading it here marks the dispatch's scope; \`runCachedQuery\`
2560
+ // folds the address into the key for this function from then on.
2561
+ get ip() {
2562
+ options.scope?.markIpRead();
2563
+
2564
+ return ip;
2565
+ },
2566
+ log,
2567
+ metrics,
2568
+ now,${pn}
2569
+ scheduler,
2570
+ span,
2571
+ storage: contextStorage,
2572
+ trace,${hn}${Ce}${Sn}${ye}${_e}${Me}${Se}${fe}
2573
+ };
2574
+ ${Tn}${$n}
2575
+ ctx.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, ctx, contextKind);
2576
+ // The composed mutation runs under the SAME wrapper the top-level RPC
2577
+ // uses, so "do the transactional work in a mutation and call it from the
2578
+ // action" — the recipe the docs give for atomicity — actually is atomic.
2579
+ ctx.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) =>
2580
+ dispatchRun("mutation", reference.__lunoraRef, fnArgs, ctx, contextKind, async (work) => this.runMutationTransaction(ctx, work));
2581
+ // \`ctx.runQuery(ref, args, { untracked: true })\` runs the sub-query on
2582
+ // its OWN context, built without the subscription's read-footprint
2583
+ // hooks — so its reads never enter this subscription's footprint and a
2584
+ // write to the tables it touched does not re-run us. Everything else is
2585
+ // inherited: \`functionPath\` (log/metric attribution), \`headroom\` (the
2586
+ // sub-query must not escape this dispatch's resource ceiling),
2587
+ // \`scope\` (the reactive-cache capture — an untracked sub-query's reads
2588
+ // must still be deps of the entry the OUTER query is memoized as, or
2589
+ // the memo goes stale), and — load-bearing — the resolved \`caller\` BY VALUE
2590
+ // (identity, userId AND ip). Omitting it would let \`buildCtx\` fall back
2591
+ // to the shared per-request fields, which a concurrent RPC may have
2592
+ // re-set, and an RLS-scoped sub-query would then read as the wrong user
2593
+ // from the wrong address. A tracked call keeps sharing \`ctx\` exactly as
2594
+ // before.
2595
+ ctx.runQuery = (reference: FunctionReference, fnArgs: Record<string, unknown>, runOptions?: { untracked?: boolean }) =>
2596
+ dispatchRun(
2597
+ "query",
2598
+ reference.__lunoraRef,
2599
+ fnArgs,
2600
+ runOptions?.untracked === true
2601
+ ? this.buildCtx({ bookmarks: options.bookmarks, functionPath: options.functionPath, headroom: options.headroom, identity: caller, scope: options.scope })
2602
+ : ctx,
2603
+ contextKind,
2604
+ );
2605
+
2606
+ return ctx;
2607
+ }
2608
+ };
2609
+ `},Ze=e=>{switch(e.kind){case"array":case"object":case"record":case"union":return{builder:"text",mode:"json",notNull:!0,typeAnnotation:N(e)};case"bigint":return{builder:"blob",mode:"bigint",notNull:!0};case"boolean":return{builder:"integer",mode:"boolean",notNull:!0};case"bytes":return{builder:"blob",mode:"buffer",notNull:!0};case"date":case"timestamp":return{builder:"integer",notNull:!0};case"from":return{builder:"text",notNull:!0,typeAnnotation:N(e)};case"id":return{builder:"text",notNull:!0};case"literal":{const t=e.literalValue??"";return t.startsWith('"')||t.startsWith("'")?{builder:"text",notNull:!0}:t==="true"||t==="false"?{builder:"integer",mode:"boolean",notNull:!0}:t==="null"?{builder:"text",notNull:!0}:{builder:"real",notNull:!0}}case"null":return{builder:"text",notNull:!0};case"number":return{builder:"real",notNull:!0};case"optional":return{...e.inner?Ze(e.inner):{builder:"text",notNull:!1},notNull:!1};case"storage":case"string":return{builder:"text",notNull:!0};default:return{builder:"text",notNull:!0}}},Ct=(e,t)=>{const n=ie(e);return n.kind==="id"&&n.tableName!==void 0&&t.has(n.tableName)?n.tableName:void 0},Yr=(e,t,n)=>{f(e,"drizzle column name");const r=Ze(t),a=r.mode?`, { mode: "${r.mode}" }`:"";let o=`${r.builder}("${e}"${a})`;r.typeAnnotation&&(o+=`.$type<${r.typeAnnotation}>()`);const i=Ct(t,n);return i!==void 0&&(o+=`.references((): AnySQLiteColumn => ${i}._id)`),r.notNull&&t.column?.notNull!==!1&&(o+=".notNull()"),o},Xr=e=>{const t=e.unique?"uniqueIndex":"index",n=e.fields.map(r=>(f(r,"drizzle index field"),`t.${r}`)).join(", ");return` ${X(e.name)}: ${t}(${JSON.stringify(e.name)}).on(${n}),`},Zr=(e,t)=>{f(e.name,"table name");const n=[' _id: text("_id").primaryKey(),',' _creationTime: integer("_creationTime").notNull(),',...Object.entries(e.shape).map(([a,o])=>` ${a}: ${Yr(a,o,t)},`)].join(`
2610
+ `),r=e.indexes.length>0?`, (t) => ({
2611
+ ${e.indexes.map(a=>Xr(a)).join(`
2612
+ `)}
2613
+ })`:"";return`export const ${e.name} = sqliteTable("${e.name}", {
2614
+ ${n}
2615
+ }${r});`},ea=e=>e.typeAnnotation?.includes('Id<"')??!1,ta=(e,t)=>{const n=new Set(["integer","sqliteTable","text"]),r=new Set;let a=!1,o=!1;for(const i of e){for(const s of Object.values(i.shape)){const c=Ze(s);n.add(c.builder),o=o||ea(c),a=a||Ct(s,t)!==void 0}for(const s of i.indexes)r.add(s.unique?"uniqueIndex":"index")}return{columns:[...n].toSorted((i,s)=>i.localeCompare(s)),indexes:[...r].toSorted((i,s)=>i.localeCompare(s)),needsAnyColumn:a,needsId:o}},gt=(e,t=!1)=>{if(e.length===0)return`${S}export {};
2616
+ `;const n=se(t),r=new Set(e.map(g=>g.name)),{columns:a,indexes:o,needsAnyColumn:i,needsId:s}=ta(e,r),c=[...o,...a].toSorted((g,y)=>g.localeCompare(y)),d=e.map(g=>Zr(g,r)).join(`
2617
+
2618
+ `),h=[i?`import type { AnySQLiteColumn } from "${n.serverDrizzle}";
2619
+ `:"",s?`import type { Id } from "./dataModel.js";
2620
+ `:""].join("");return`${S}import { ${c.join(", ")} } from "${n.serverDrizzle}";
2621
+ ${h}
2622
+ ${d}
2623
+ `},Na=(e,t=!1)=>{const n=e.tables.filter(a=>a.shardMode==="global"),r=e.tables.filter(a=>a.shardMode!=="global");return{global:E(gt(n,t),Je),shard:E(gt(r,t),Je)}},Aa=e=>e?`${S}/**
2624
+ * The \`SchedulerDO\` Durable Object class, re-exported so a worker entry can
2625
+ * forward it — wrangler binds only what the entry exports:
2626
+ *
2627
+ * \`export * from "./lunora/_generated/scheduler.js";\`
2628
+ *
2629
+ * A Vite-first (class-A) app needs no such line: the generated worker entry
2630
+ * forwards this module for you whenever it exists.
2631
+ */
2632
+ export { SchedulerDO } from "@lunora/scheduler";
2633
+ `:"",Oa=e=>{const t=new Map;for(const i of e){const s=t.get(i.cron);s?s.push(i):t.set(i.cron,[i])}const n=[...t.keys()].map(i=>` ${JSON.stringify(i)},`).join(`
2634
+ `),r=n.length>0?`
2635
+ ${n}
2636
+ `:"",a=[...t.entries()].map(([i,s])=>{const c=s.map(d=>{const h=d.workflow?`workflow: ${JSON.stringify(d.workflow.binding)}`:`functionPath: ${JSON.stringify(d.functionPath)}`;return` { name: ${JSON.stringify(d.name)}, ${h}, args: ${JSON.stringify(d.args)} },`}).join(`
2637
+ `);return` ${JSON.stringify(i)}: [
2638
+ ${c}
2639
+ ],`}).join(`
2640
+ `),o=a.length>0?`
2641
+ ${a}
2642
+ `:"";return`${S}/**
2643
+ * One scheduled cron invocation. Exactly one target is set: \`functionPath\` is
2644
+ * the \`namespace:fn\` dispatch ref (matches \`__lunoraRef\`), invoked on the
2645
+ * shard; \`workflow\` is a \`WORKFLOW_*\` binding name whose durable workflow is
2646
+ * started fresh per fire. \`args\` are forwarded verbatim (a workflow's become
2647
+ * its \`params\`).
2648
+ */
2649
+ export interface LunoraCronJob {
2650
+ args: Record<string, unknown>;
2651
+ functionPath?: string;
2652
+ name: string;
2653
+ workflow?: string;
2654
+ }
2655
+
2656
+ /**
2657
+ * Deduplicated cron schedules — mirror of wrangler's \`triggers.crons\`. The
2658
+ * Lunora vite plugin keeps \`wrangler.jsonc\` in sync with this array.
2659
+ */
2660
+ export const LUNORA_CRON_TRIGGERS: ReadonlyArray<string> = [${r}];
2661
+
2662
+ /**
2663
+ * Dispatcher map keyed by cron expression. The Worker's \`scheduled()\` handler
2664
+ * looks up \`event.cron\` here and dispatches every job in the list.
2665
+ */
2666
+ export const LUNORA_CRONS: Record<string, ReadonlyArray<LunoraCronJob>> = {${o}};
2667
+ `},Ia=e=>{const t=[...e].toSorted((r,a)=>r.name.localeCompare(a.name)).map(r=>{const a=[`name: ${JSON.stringify(r.name)}`,`table: ${JSON.stringify(r.table)}`];return r.field!==void 0&&a.push(`field: ${JSON.stringify(r.field)}`),r.dimensions!==void 0&&a.push(`dimensions: ${JSON.stringify(r.dimensions)}`),r.metric!==void 0&&a.push(`metric: ${JSON.stringify(r.metric)}`),r.metadata!==void 0&&a.push(`metadata: ${JSON.stringify(r.metadata)}`),` { ${a.join(", ")} },`}).join(`
2668
+ `),n=t.length>0?`
2669
+ ${t}
2670
+ `:"";return`${S}/**
2671
+ * One vector index declared in \`schema.ts\` — an inline \`.vectorize()\` column
2672
+ * or a standalone \`defineVectorIndex()\`. \`field\` is the source column (absent
2673
+ * for a \`select\`-derived Shape B index); \`metadata\` lists the row fields
2674
+ * mirrored into Vectorize for filtering.
2675
+ */
2676
+ export interface LunoraVectorIndex {
2677
+ dimensions?: number;
2678
+ field?: string;
2679
+ metadata?: ReadonlyArray<string>;
2680
+ metric?: "cosine" | "dot-product" | "euclidean";
2681
+ name: string;
2682
+ table: string;
2683
+ }
2684
+
2685
+ /**
2686
+ * Static registry of every vector index in the schema, sorted by name.
2687
+ * Vectorize cannot enumerate indexes at runtime, so the worker passes this to
2688
+ * \`createWorker({ vectorIntrospector })\` to back the studio's vector browser.
2689
+ */
2690
+ export const LUNORA_VECTOR_INDEXES: ReadonlyArray<LunoraVectorIndex> = [${n}];
2691
+ `},Ca=e=>[...new Set(e.map(t=>t.cron))];export{ga as A,En as C,S as G,ar as U,ba as a,wa as b,va as c,Oa as d,Ra as e,fa as f,Na as g,xa as h,Aa as i,ka as j,Ta as k,Ia as l,Sa as m,Ca as n,vr as o,$a as p,ya as q,I as r};