@lunora/codegen 1.0.0-alpha.150 → 1.0.0-alpha.152

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 (69) hide show
  1. package/dist/index.d.mts +15 -2
  2. package/dist/index.d.ts +15 -2
  3. package/dist/index.mjs +1 -1
  4. package/dist/packem_shared/AGENTS_FILENAME-CRdh_pOr.mjs +1 -0
  5. package/dist/packem_shared/CONTAINERS_FILENAME-BssQL6eI.mjs +1 -0
  6. package/dist/packem_shared/FLAGS_FILENAME-Bn_wWM4d.mjs +1 -0
  7. package/dist/packem_shared/{GENERATED_HEADER--PCtJMQc.mjs → GENERATED_HEADER-w7DB5GZI.mjs} +1 -1
  8. package/dist/packem_shared/NOTIFY_FILENAME-BH0djo6F.mjs +1 -0
  9. package/dist/packem_shared/{OPENRPC_VERSION-C4UXIUyy.mjs → OPENRPC_VERSION-BAgBIsQM.mjs} +1 -1
  10. package/dist/packem_shared/QUEUES_FILENAME-CIfuMMG3.mjs +1 -0
  11. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-S3Ey8df4.mjs +9 -0
  12. package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-CWESEYLi.mjs +2 -0
  13. package/dist/packem_shared/{SchemaSnapshotParseError-yZc-1mxR.mjs → SchemaSnapshotParseError-yrVM8bTa.mjs} +1 -1
  14. package/dist/packem_shared/WORKFLOWS_FILENAME-CSqONfHS.mjs +1 -0
  15. package/dist/packem_shared/{buildOpenApiDocument-D5xOB07T.mjs → buildOpenApiDocument-ZdoDQXmt.mjs} +1 -1
  16. package/dist/packem_shared/chain-DboeOxBl.mjs +1 -0
  17. package/dist/packem_shared/classify-procedure-call-CRm5Etni.mjs +1 -0
  18. package/dist/packem_shared/context-property-calls-CBUh7oAY.mjs +1 -0
  19. package/dist/packem_shared/discoverAuthApiCalls-B5Qmjmyk.mjs +1 -0
  20. package/dist/packem_shared/discoverCrons-c0ig327z.mjs +1 -0
  21. package/dist/packem_shared/discoverFunctions-DeuEs6Ce.mjs +1 -0
  22. package/dist/packem_shared/discoverHttpRoutes-zWSSyyOj.mjs +1 -0
  23. package/dist/packem_shared/discoverHyperdriveCalls-C6kV2CPz.mjs +1 -0
  24. package/dist/packem_shared/discoverInserts-BB7o_8pU.mjs +1 -0
  25. package/dist/packem_shared/discoverMaskProcedures-CNqwOwXv.mjs +1 -0
  26. package/dist/packem_shared/discoverMigrations-CQjGLKW-.mjs +1 -0
  27. package/dist/packem_shared/discoverNondeterministicCalls-B07wjsd2.mjs +1 -0
  28. package/dist/packem_shared/discoverQueries-Dz2vDpE9.mjs +1 -0
  29. package/dist/packem_shared/discoverR2sqlCalls-CuHe5smg.mjs +1 -0
  30. package/dist/packem_shared/discoverRlsMetadata-CXWVNLk4.mjs +1 -0
  31. package/dist/packem_shared/discoverRlsProcedures-DXdr9YHi.mjs +1 -0
  32. package/dist/packem_shared/discoverSandboxUsage-BJHLHZR_.mjs +1 -0
  33. package/dist/packem_shared/discoverStorageRulesMetadata-CUa2Dtb4.mjs +1 -0
  34. package/dist/packem_shared/{emit-CXfWb_pY.mjs → emit-Dsr5DNpc.mjs} +291 -129
  35. package/dist/packem_shared/{emitApp-DcrQNgaz.mjs → emitApp-kFg_xYlc.mjs} +10 -2
  36. package/dist/packem_shared/{exported-procedure-chains-M40rbSYM.mjs → exported-procedure-chains-Cmt3Pr92.mjs} +1 -1
  37. package/dist/packem_shared/index-BsVoa-4F.mjs +1 -0
  38. package/dist/packem_shared/listLunoraSourceFiles-BvqYMy81.mjs +1 -0
  39. package/dist/packem_shared/queries-D8DheHbi.mjs +1 -0
  40. package/package.json +10 -10
  41. package/dist/packem_shared/AGENTS_FILENAME-B1Abt3Gl.mjs +0 -1
  42. package/dist/packem_shared/CONTAINERS_FILENAME-CtcysaKS.mjs +0 -1
  43. package/dist/packem_shared/FLAGS_FILENAME-B3VZ6vTw.mjs +0 -1
  44. package/dist/packem_shared/NOTIFY_FILENAME-CKO5gtGe.mjs +0 -1
  45. package/dist/packem_shared/QUEUES_FILENAME-C-pzLGy4.mjs +0 -1
  46. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-DazENDSB.mjs +0 -9
  47. package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-B1BUjuG5.mjs +0 -2
  48. package/dist/packem_shared/WORKFLOWS_FILENAME-BfvqsG9Z.mjs +0 -1
  49. package/dist/packem_shared/ast-BYoSZlae.mjs +0 -1
  50. package/dist/packem_shared/chain-Dygm2apY.mjs +0 -1
  51. package/dist/packem_shared/classify-procedure-call-Ib2EMtFH.mjs +0 -1
  52. package/dist/packem_shared/context-property-calls-Br0sVv8E.mjs +0 -1
  53. package/dist/packem_shared/discoverAuthApiCalls-DoNQSgso.mjs +0 -1
  54. package/dist/packem_shared/discoverCrons-DCxl4hty.mjs +0 -1
  55. package/dist/packem_shared/discoverFunctions-CXURzTDU.mjs +0 -1
  56. package/dist/packem_shared/discoverHttpRoutes-2XhMbeh5.mjs +0 -1
  57. package/dist/packem_shared/discoverHyperdriveCalls-DTq4PKT5.mjs +0 -1
  58. package/dist/packem_shared/discoverInserts-qU5F4W4g.mjs +0 -1
  59. package/dist/packem_shared/discoverMaskProcedures-BZsC5dHd.mjs +0 -1
  60. package/dist/packem_shared/discoverMigrations-BFkJQ8Sy.mjs +0 -1
  61. package/dist/packem_shared/discoverNondeterministicCalls-B-NNzzPW.mjs +0 -1
  62. package/dist/packem_shared/discoverQueries-B4emAFn5.mjs +0 -1
  63. package/dist/packem_shared/discoverR2sqlCalls-C2GtrFnl.mjs +0 -1
  64. package/dist/packem_shared/discoverRlsMetadata-BKcUnSNr.mjs +0 -1
  65. package/dist/packem_shared/discoverRlsProcedures-fAhxiIyD.mjs +0 -1
  66. package/dist/packem_shared/discoverSandboxUsage-CJhnKA0_.mjs +0 -1
  67. package/dist/packem_shared/discoverStorageRulesMetadata-C7XmORWp.mjs +0 -1
  68. package/dist/packem_shared/index-xGhyK9J_.mjs +0 -1
  69. package/dist/packem_shared/queries-Ddvo5dCs.mjs +0 -1
@@ -1,4 +1,4 @@
1
- import{agentComponent as dt}from"@lunora/agent/component";import{LunoraError as Z}from"@lunora/errors";import{serializeSchemaSnapshot as xn,hashSchemaSnapshot as vn}from"./SCHEMA_SNAPSHOT_VERSION-B1BUjuG5.mjs";import{s as $}from"./paths-C-9fwEgQ.mjs";const ut=[{contextProperty:"access",key:"access",moduleSpecifier:"@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"},{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",serverCtxField:{field:`
1
+ import{agentComponent as dt}from"@lunora/agent/component";import{LunoraError as Z}from"@lunora/errors";import{serializeSchemaSnapshot as xn,hashSchemaSnapshot as vn}from"./SCHEMA_SNAPSHOT_VERSION-CWESEYLi.mjs";import{s as $}from"./paths-C-9fwEgQ.mjs";const ut=[{contextProperty:"access",key:"access",moduleSpecifier:"@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"},{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",serverCtxField:{field:`
2
2
  /** Analytics Engine telemetry sink. Fire-and-forget and sampled; do not read it back in-handler. */
3
3
  readonly analytics: import("@lunora/bindings/analytics").AnalyticsClient;`,tier:"every"}},{appMethod:{configKey:"browser",doc:"Override the Browser Rendering binding backing `ctx.browser` (defaults to `env.BROWSER`).",method:"browser"},contextProperty:"browser",key:"browser",moduleSpecifier:"@lunora/browser",serverCtxField:{field:`
4
4
  /** Browser Rendering (screenshots/PDF/scrape). Non-deterministic — available only in actions. */
@@ -15,14 +15,14 @@ import{agentComponent as dt}from"@lunora/agent/component";import{LunoraError as
15
15
  /**
16
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
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"}],Rn=ut,Sn=new Map(Rn.flatMap(e=>e.serverCtxField?[[e.key,e.serverCtxField]]:[])),eo=ut.flatMap(e=>"appMethod"in e?[{appMethod:e.appMethod,key:e.key}]:[]),pt=/^(?:"(?:[^"\\]|\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4}))*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u,$n=new Set(["any","bigint","boolean","date","id","null","number","storage","string","timestamp"]),ht=e=>e.column!==void 0,Nn=(e,t)=>{switch(e){case"bigint":return`if (typeof ${t} !== "bigint") return DEFER;
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"}],Rn=ut,Sn=new Map(Rn.flatMap(e=>e.serverCtxField?[[e.key,e.serverCtxField]]:[])),eo=ut.flatMap(e=>"appMethod"in e?[{appMethod:e.appMethod,key:e.key}]:[]),pt=/^(?:"(?:[^"\\]|\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4}))*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u,$n=new Set(["any","bigint","boolean","date","id","null","number","storage","string","timestamp"]),ht=e=>e.column!==void 0,Tn=(e,t)=>{switch(e){case"bigint":return`if (typeof ${t} !== "bigint") return DEFER;
19
19
  `;case"boolean":return`if (typeof ${t} !== "boolean") return DEFER;
20
20
  `;case"date":case"number":case"timestamp":return`if (typeof ${t} !== "number" || !Number.isFinite(${t})) return DEFER;
21
21
  `;case"null":return`if (${t} !== null) return DEFER;
22
22
  `;default:return`if (typeof ${t} !== "string") return DEFER;
23
- `}},Tn=(e,t)=>e.stringMaxLength===void 0?"":`if (${t}.length > ${String(e.stringMaxLength)}) return DEFER;
23
+ `}},Nn=(e,t)=>e.stringMaxLength===void 0?"":`if (${t}.length > ${String(e.stringMaxLength)}) return DEFER;
24
24
  `,An=(e,t)=>{const n=e.literalValue?.trim();if(!(n===void 0||!pt.test(n)))return{out:t,pre:`if (${t} !== ${n}) return DEFER;
25
- `}},Ge=(e,t,n)=>{if(!ht(e)&&!(e.unmodelledRefinement||e.sourceText!==void 0)){if($n.has(e.kind))return e.kind==="any"?{out:t,pre:""}:{out:t,pre:Nn(e.kind,t)+Tn(e,t)};switch(e.kind){case"array":return On(e,t,n);case"literal":return An(e,t);case"object":return Ln(e,t,n);default:return}}},On=(e,t,n)=>{const{inner:r}=e;if(!r)return;const o=n.next(),s=`__arr${String(o)}`,i=`__i${String(o)}`,a=`__e${String(o)}`,c=Ge(r,a,n);if(!c)return;const d=`if (!Array.isArray(${t})) return DEFER;
25
+ `}},Ge=(e,t,n)=>{if(!ht(e)&&!(e.unmodelledRefinement||e.sourceText!==void 0)){if($n.has(e.kind))return e.kind==="any"?{out:t,pre:""}:{out:t,pre:Tn(e.kind,t)+Nn(e,t)};switch(e.kind){case"array":return On(e,t,n);case"literal":return An(e,t);case"object":return Ln(e,t,n);default:return}}},On=(e,t,n)=>{const{inner:r}=e;if(!r)return;const o=n.next(),s=`__arr${String(o)}`,i=`__i${String(o)}`,a=`__e${String(o)}`,c=Ge(r,a,n);if(!c)return;const d=`if (!Array.isArray(${t})) return DEFER;
26
26
  const ${s} = new Array(${t}.length);
27
27
  for (let ${i} = 0; ${i} < ${t}.length; ${i}++) {
28
28
  const ${a} = ${t}[${i}];
@@ -38,18 +38,18 @@ ${d} = true;
38
38
  `,c=p}const d=In(s,i,c,n);if(!d)return;r+=d.pre,o.push(d.entry)}return{entries:o.join(", "),pre:r}},gt=e=>{const t=`Object.getPrototypeOf(${e})`;return`if (${t} !== Object.prototype && ${t} !== null) return DEFER;
39
39
  `},Ln=(e,t,n)=>{const r=e.shape??{},o=mt(r,t,n);if(!o)return;const s=n.next(),i=`__obj${String(s)}`,a=`if (typeof ${t} !== "object" || ${t} === null || Array.isArray(${t})) return DEFER;
40
40
  ${gt(t)}${o.pre}const ${i} = { ${o.entries} };
41
- `;return{out:i,pre:a}},_n=e=>{let t=0;const r=mt(e,"source",{next:()=>(t+=1,t)});if(r)return`(source) => {
41
+ `;return{out:i,pre:a}},En=e=>{let t=0;const r=mt(e,"source",{next:()=>(t+=1,t)});if(r)return`(source) => {
42
42
  if (typeof source !== "object" || source === null || Array.isArray(source)) return DEFER;
43
43
  ${gt("source")}${r.pre}return { ${r.entries} };
44
44
  }`},S=`// GENERATED by @lunora/codegen — do not edit.
45
45
  // Run \`lunora codegen\` to regenerate.
46
46
 
47
- `,ae=(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"},ft=/^[A-Za-z_$][\w$]*$/u,En=/^[\w./-]+$/u,b=(e,t)=>{if(!ft.test(e))throw new Z("INTERNAL",`@lunora/codegen: ${t} is not a valid JS identifier: ${JSON.stringify(e)}`)},He=e=>e.charAt(0).toUpperCase()+e.slice(1),j=e=>ft.test(e)?e:JSON.stringify(e),X=e=>e==="__proto__"?`[${JSON.stringify(e)}]`:j(e),Dn={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"},Pn=e=>{if(e===void 0)return"unknown";if(!pt.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},A=e=>{const t=Dn[e.kind];if(t!==void 0)return t;switch(e.kind){case"array":return`Array<${e.inner?A(e.inner):"unknown"}>`;case"from":return e.tsType??"unknown";case"id":{const n=e.tableName??"_unknown_";return b(n,"v.id() table name"),`Id<"${n}">`}case"literal":return Pn(e.literalValue);case"object":return e.shape?`{ ${Object.entries(e.shape).map(([r,o])=>bt(r,o)).join("; ")} }`:"Record<string, unknown>";case"optional":return`${e.inner?A(e.inner):"unknown"} | undefined`;case"record":{const n=e.keyType?A(e.keyType):"string",r=e.valueType?A(e.valueType):"unknown";return`Record<${n}, ${r}>`}case"union":return e.members&&e.members.length>0?e.members.map(n=>A(n)).join(" | "):"never";default:return"unknown"}},bt=(e,t)=>{const n=j(e);if(t.kind==="optional"){const r=t.inner?A(t.inner):"unknown";return`${n}?: ${r}`}return`${n}: ${A(t)}`},xe=e=>{const t=Object.entries(e);return t.length===0?"{}":`{ ${t.map(([n,r])=>bt(n,r)).join("; ")} }`},yt=e=>e.kind==="optional"||!!e.column?.hasDefault,se=e=>e.kind==="optional"&&e.inner?e.inner:e,Fn=e=>{b(e.name,"table name");const t=Object.entries(e.shape).map(([r,o])=>{const s=se(o),i=A(s),a=j(r);return yt(o)?` ${a}?: ${i};`:` ${a}: ${i};`}).join(`
47
+ `,ae=(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"},ft=/^[A-Za-z_$][\w$]*$/u,_n=/^[\w./-]+$/u,b=(e,t)=>{if(!ft.test(e))throw new Z("INTERNAL",`@lunora/codegen: ${t} is not a valid JS identifier: ${JSON.stringify(e)}`)},He=e=>e.charAt(0).toUpperCase()+e.slice(1),q=e=>ft.test(e)?e:JSON.stringify(e),X=e=>e==="__proto__"?`[${JSON.stringify(e)}]`:q(e),Dn={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"},Pn=e=>{if(e===void 0)return"unknown";if(!pt.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},A=e=>{const t=Dn[e.kind];if(t!==void 0)return t;switch(e.kind){case"array":return`Array<${e.inner?A(e.inner):"unknown"}>`;case"from":return e.tsType??"unknown";case"id":{const n=e.tableName??"_unknown_";return b(n,"v.id() table name"),`Id<"${n}">`}case"literal":return Pn(e.literalValue);case"object":return e.shape?`{ ${Object.entries(e.shape).map(([r,o])=>bt(r,o)).join("; ")} }`:"Record<string, unknown>";case"optional":return`${e.inner?A(e.inner):"unknown"} | undefined`;case"record":{const n=e.keyType?A(e.keyType):"string",r=e.valueType?A(e.valueType):"unknown";return`Record<${n}, ${r}>`}case"union":return e.members&&e.members.length>0?e.members.map(n=>A(n)).join(" | "):"never";default:return"unknown"}},bt=(e,t)=>{const n=q(e);if(t.kind==="optional"){const r=t.inner?A(t.inner):"unknown";return`${n}?: ${r}`}return`${n}: ${A(t)}`},xe=e=>{const t=Object.entries(e);return t.length===0?"{}":`{ ${t.map(([n,r])=>bt(n,r)).join("; ")} }`},yt=e=>e.kind==="optional"||!!e.column?.hasDefault,se=e=>e.kind==="optional"&&e.inner?e.inner:e,Mn=e=>{b(e.name,"table name");const t=Object.entries(e.shape).map(([r,o])=>{const s=se(o),i=A(s),a=q(r);return yt(o)?` ${a}?: ${i};`:` ${a}: ${i};`}).join(`
48
48
  `),n=t?`
49
49
  ${t}
50
50
  `:"";return`export interface Insert_${e.name} {
51
51
  _id?: Id<"${e.name}">;
52
- _creationTime?: number;${n}}`},Je="schema",to=e=>{for(const u of e.tables)b(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,k])=>{const N=j(x);if(k.kind==="optional"){const T=k.inner?A(k.inner):"unknown";return` ${N}?: ${T};`}return` ${N}: ${A(k)};`}).join(`
52
+ _creationTime?: number;${n}}`},Je="schema",to=e=>{for(const u of e.tables)b(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,k])=>{const T=q(x);if(k.kind==="optional"){const N=k.inner?A(k.inner):"unknown";return` ${T}?: ${N};`}return` ${T}: ${A(k)};`}).join(`
53
53
  `),y=l?`
54
54
  ${l}
55
55
  `:"",R=u.commitOrdered===!0?`
@@ -62,14 +62,14 @@ ${l}
62
62
  `),i=e.tables.map(u=>{const l=u.searchIndexes.map(y=>JSON.stringify(y.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
63
63
  `),a=e.tables.map(u=>{const l=u.rankIndexes.map(y=>JSON.stringify(y.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
64
64
  `),c=e.tables.map(u=>{const l=u.shardMode==="global"?"":(u.geoIndexes??[]).map(y=>JSON.stringify(y.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
65
- `),d=e.vectorIndexes.map(u=>JSON.stringify(u.name)).join(" | ")||"never",p=e.tables.map(u=>Fn(u)).join(`
65
+ `),d=e.vectorIndexes.map(u=>JSON.stringify(u.name)).join(" | ")||"never",p=e.tables.map(u=>Mn(u)).join(`
66
66
 
67
67
  `),m=e.tables.map(u=>` ${u.name}: Insert_${u.name};`).join(`
68
- `),w=e.tables.map(u=>{const l=u.relations.map(y=>(b(y.table,"relation target table"),` ${j(y.name)}: ${y.kind==="one"?"OneRelation":"ManyRelation"}<"${y.table}">;`)).join(`
68
+ `),w=e.tables.map(u=>{const l=u.relations.map(y=>(b(y.table,"relation target table"),` ${q(y.name)}: ${y.kind==="one"?"OneRelation":"ManyRelation"}<"${y.table}">;`)).join(`
69
69
  `);return l?` ${u.name}: {
70
70
  ${l}
71
71
  };`:` ${u.name}: {};`}).join(`
72
- `);return _(`${S}export type TableName = ${t};
72
+ `);return E(`${S}export type TableName = ${t};
73
73
 
74
74
  /**
75
75
  * The tables **this app declared** — every {@link TableName} except those an add-on
@@ -161,8 +161,8 @@ export interface Relations {
161
161
  ${w}
162
162
  }
163
163
 
164
- `,Je)},Mn=/import\("(?<spec>(?:\.\.?\/)*_generated\/[^"]+)"\)/gu,wt=/^(?:\.\.?\/)*_generated\//u,kt=/\.[A-Za-z]\w*$/u,Bn=e=>e.replaceAll(Mn,(t,n)=>{const r=n.replace(wt,"./");return`import("${kt.test(r)?r:`${r}.js`}")`}),jn=/import\("(?<spec>[^"]+)"\)/gu,it="node_modules/",qn=/^@types\//u,Wn=e=>e.replaceAll(jn,(t,n)=>{const r=n.lastIndexOf(it);if(r===-1||r>0&&n[r-1]!=="/")return t;const o=n.slice(r+it.length);return o===""||qn.test(o)?t:`import("${o}")`}),Un=/import\("(?<spec>\.\.?\/[^"]+)"\)/gu,Kn=/^(?:\.\.?\/)+/u,Qn=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("/")},Vn=(e,t)=>e.replaceAll(Un,(n,r)=>{if(wt.test(r.replace(Kn,"")))return n;const o=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",i=`../${Qn(`${o}/${r}`)}`;return`import("${kt.test(i)?i:`${i}.js`}")`}),_=(e,t)=>Bn(Vn(Wn(e),t)),Hn=["client","do","errors","flags","observability","platform","ratelimit","runtime","server","values"],Gn=new RegExp(String.raw`import\("@lunora/(?<pkg>${Hn.join("|")})(?<subpath>/[^"]*)?"\)`,"gu"),Jn={flags:new Set(["/web"]),platform:new Set},xt=(e,t)=>t?e.replaceAll(Gn,(n,r,o)=>{const s=Jn[r];return o&&s&&!s.has(o)?n:`import("lunorash/${r}${o??""}")`}):e,ze=e=>["Doc","Id"].filter(t=>new RegExp(String.raw`(?<![.$\w])${t}<`,"u").test(e)),vt=e=>e.output===void 0||e.output.kind==="any"||e.kind==="stream"?e.returnType:A(e.output),Ye=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))},lt=e=>{const t=([n,r])=>{const o=r.toSorted((s,i)=>s.exportName.localeCompare(i.exportName)).map(s=>{const i=_(xe(s.args),s.filePath),a=_(vt(s),s.filePath);return` ${s.exportName}: FunctionReference<"${s.kind}", ${i}, ${a}>;`}).join(`
165
- `);return` ${j($(n))}: {
164
+ `,Je)},Fn=/import\("(?<spec>(?:\.\.?\/)*_generated\/[^"]+)"\)/gu,wt=/^(?:\.\.?\/)*_generated\//u,kt=/\.[A-Za-z]\w*$/u,Bn=e=>e.replaceAll(Fn,(t,n)=>{const r=n.replace(wt,"./");return`import("${kt.test(r)?r:`${r}.js`}")`}),qn=/import\("(?<spec>[^"]+)"\)/gu,it="node_modules/",jn=/^@types\//u,Wn=e=>e.replaceAll(qn,(t,n)=>{const r=n.lastIndexOf(it);if(r===-1||r>0&&n[r-1]!=="/")return t;const o=n.slice(r+it.length);return o===""||jn.test(o)?t:`import("${o}")`}),Un=/import\("(?<spec>\.\.?\/[^"]+)"\)/gu,Kn=/^(?:\.\.?\/)+/u,Qn=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("/")},Vn=(e,t)=>e.replaceAll(Un,(n,r)=>{if(wt.test(r.replace(Kn,"")))return n;const o=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",i=`../${Qn(`${o}/${r}`)}`;return`import("${kt.test(i)?i:`${i}.js`}")`}),E=(e,t)=>Bn(Vn(Wn(e),t)),Hn=["client","do","errors","flags","observability","platform","ratelimit","runtime","server","values"],Gn=new RegExp(String.raw`import\("@lunora/(?<pkg>${Hn.join("|")})(?<subpath>/[^"]*)?"\)`,"gu"),Jn={flags:new Set(["/web"]),platform:new Set},xt=(e,t)=>t?e.replaceAll(Gn,(n,r,o)=>{const s=Jn[r];return o&&s&&!s.has(o)?n:`import("lunorash/${r}${o??""}")`}):e,ze=e=>["Doc","Id"].filter(t=>new RegExp(String.raw`(?<![.$\w])${t}<`,"u").test(e)),vt=e=>e.output===void 0||e.output.kind==="any"||e.kind==="stream"?e.returnType:A(e.output),Ye=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))},lt=e=>{const t=([n,r])=>{const o=r.toSorted((s,i)=>s.exportName.localeCompare(i.exportName)).map(s=>{const i=E(xe(s.args),s.filePath),a=E(vt(s),s.filePath);return` ${s.exportName}: FunctionReference<"${s.kind}", ${i}, ${a}>;`}).join(`
165
+ `);return` ${q($(n))}: {
166
166
  ${o}
167
167
  };`};return Ye(e).map(n=>t(n)).join(`
168
168
  `)},zn=(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+=`
@@ -207,8 +207,8 @@ ${i}
207
207
  */
208
208
  const lunoraAgentRuntimeFunctions = agentComponent().functions;
209
209
  `}},er=(e,t)=>{if(!e)return{importLine:"",lines:"",prelude:""};const n=t.find(r=>$(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"}},tr=(e,t)=>{if(e.length===0)return[];const n=Rt(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 o of e)o.voice&&r.push({args:{threadKey:{kind:"string"}},exportName:`${o.exportName}Voice`,filePath:"agents",kind:"stream",returnType:"Record<string, unknown>"});return r.filter(o=>!n.has(o.exportName))},nr=(e,t)=>{if(e.length===0)return[];const n=new Set(t.filter(r=>$(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}))},rr=e=>{const t=e.filter(i=>i.stream);if(t.length===0)return{block:"",body:""};const n=Ye(t),r=n.map(([i,a])=>{const c=a.map(d=>{const p=_(d.chunkType??"unknown",d.filePath),m=_(xe(d.searchParams),d.filePath),w=_(xe(d.params),d.filePath);return` ${j(d.exportName)}: HttpStreamRef<${p}, ${m}, ${w}>;`}).join(`
211
- `);return` ${j($(i))}: {
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"}},tr=(e,t)=>{if(e.length===0)return[];const n=Rt(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 o of e)o.voice&&r.push({args:{threadKey:{kind:"string"}},exportName:`${o.exportName}Voice`,filePath:"agents",kind:"stream",returnType:"Record<string, unknown>"});return r.filter(o=>!n.has(o.exportName))},nr=(e,t)=>{if(e.length===0)return[];const n=new Set(t.filter(r=>$(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}))},rr=e=>{const t=e.filter(i=>i.stream);if(t.length===0)return{block:"",body:""};const n=Ye(t),r=n.map(([i,a])=>{const c=a.map(d=>{const p=E(d.chunkType??"unknown",d.filePath),m=E(xe(d.searchParams),d.filePath),w=E(xe(d.params),d.filePath);return` ${q(d.exportName)}: HttpStreamRef<${p}, ${m}, ${w}>;`}).join(`
211
+ `);return` ${q($(i))}: {
212
212
  ${c}
213
213
  };`}).join(`
214
214
  `),o=n.map(([i,a])=>{const c=a.map(d=>` ${X(d.exportName)}: { method: ${JSON.stringify(d.method)}, path: ${JSON.stringify(d.path)} },`).join(`
@@ -232,7 +232,7 @@ import type { ${l.join(", ")} } from "./dataModel.js";
232
232
  ${p}
233
233
  `:"",x=m?`
234
234
  ${m}
235
- `:"",k=zn(i,t),N=`export interface ApiTypes {${R}}
235
+ `:"",k=zn(i,t),T=`export interface ApiTypes {${R}}
236
236
 
237
237
  export const api = anyApi as unknown as ApiTypes;
238
238
 
@@ -240,10 +240,10 @@ export const api = anyApi as unknown as ApiTypes;
240
240
  export interface InternalApiTypes {${x}}
241
241
 
242
242
  export const internal = anyApi as unknown as InternalApiTypes;
243
- ${k.block}${w.block}`,T=[...N.includes("FunctionReference<")?["FunctionReference"]:[],...w.block===""?[]:["HttpStreamRef"]],D=T.length>0?`import type { ${T.join(", ")} } from "${a.client}";
243
+ ${k.block}${w.block}`,N=[...T.includes("FunctionReference<")?["FunctionReference"]:[],...w.block===""?[]:["HttpStreamRef"]],D=N.length>0?`import type { ${N.join(", ")} } from "${a.client}";
244
244
  `:"";return xt(`${S}import { anyApi } from "${a.client}";
245
245
  ${D}${k.importLine}${y}
246
- ${N}`,s)},ro=e=>e?`${S}import { createSeedClient as createSeedClientBase } from "@lunora/seed";
246
+ ${T}`,s)},ro=e=>e?`${S}import { createSeedClient as createSeedClientBase } from "@lunora/seed";
247
247
  import type { SeedClient, SeedClientOptions } from "@lunora/seed";
248
248
 
249
249
  import schema from "../schema.js";
@@ -260,7 +260,7 @@ import type { InsertModel } from "./dataModel.js";
260
260
  * const { posts } = await seed.posts((x) => x([10, 20]));
261
261
  */
262
262
  export const createSeedClient = (options?: SeedClientOptions): SeedClient<InsertModel> => createSeedClientBase<InsertModel>(schema, options);
263
- `:"",oo=(e,t,n=!1)=>{if(e.length===0||!t)return"";const r=ae(n),o=c=>c.table===void 0?"Row":`Doc<${JSON.stringify(c.table)}> & Row`,s=e.map(c=>{b(c.exportName,"shape export name");const d=o(c),p=_(xe(c.args),c.filePath),m=Object.keys(c.args).length>0,w=m?` args: ${p};`:` args?: ${p};`,u=m?`(args: ${p}) => void`:`(args?: ${p}) => void`,l=`${c.exportName}CollectionOptions`;return`/** Options for the \`${c.exportName}\` shape binding. */
263
+ `:"",oo=(e,t,n=!1)=>{if(e.length===0||!t)return"";const r=ae(n),o=c=>c.table===void 0?"Row":`Doc<${JSON.stringify(c.table)}> & Row`,s=e.map(c=>{b(c.exportName,"shape export name");const d=o(c),p=E(xe(c.args),c.filePath),m=Object.keys(c.args).length>0,w=m?` args: ${p};`:` args?: ${p};`,u=m?`(args: ${p}) => void`:`(args?: ${p}) => void`,l=`${c.exportName}CollectionOptions`;return`/** Options for the \`${c.exportName}\` shape binding. */
264
264
  export interface ${He(l)} {
265
265
  ${w}
266
266
  /**
@@ -321,14 +321,35 @@ ${s}
321
321
  throw new LunoraError("FUNCTION_NOT_FOUND", \`function not registered: \${functionPath}\`);
322
322
  }
323
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
+
324
345
  return (await registered.handler(context, args ?? {})) as R;
325
- };`,sr=(e,t,n=[],r=[])=>{const o=new Map,s=l=>{o.has(l)||o.set(l,or(l,o.size))};for(const l of e)s(l.filePath);for(const l of t)s(l.filePath);for(const l of n)s(l.filePath);for(const l of r)s(l.filePath);const i=[...o.entries()].map(([l,y])=>{if(!En.test(l))throw new Z("INTERNAL",`@lunora/codegen: refusing to emit import for unsafe file path: ${JSON.stringify(l)}`);return`import * as ${y} from "../${l}.js";`}).join(`
346
+ };`,sr=(e,t,n=[],r=[])=>{const o=new Map,s=l=>{o.has(l)||o.set(l,or(l,o.size))};for(const l of e)s(l.filePath);for(const l of t)s(l.filePath);for(const l of n)s(l.filePath);for(const l of r)s(l.filePath);const i=[...o.entries()].map(([l,y])=>{if(!_n.test(l))throw new Z("INTERNAL",`@lunora/codegen: refusing to emit import for unsafe file path: ${JSON.stringify(l)}`);return`import * as ${y} from "../${l}.js";`}).join(`
326
347
  `),a=e.map(l=>` "${$(l.filePath)}:${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
327
348
  `),c=t.map(l=>` ${JSON.stringify(l.id)}: ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredDataMigration,`).join(`
328
349
  `),d=n.map(l=>` "${$(l.filePath)}:${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
329
350
  `),p=r.map(l=>` "${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredShape,`).join(`
330
351
  `),m=n.map(l=>`${$(l.filePath)}:${l.exportName}`),w=[a,d].filter(l=>l.length>0).join(`
331
- `),u=e.map(l=>{if(l.lifecycle||Object.keys(l.args).length===0)return;const y=_n(l.args);return y===void 0?void 0:`installCompiledValidatorMap(${o.get(l.filePath)??""}.${l.exportName}.args, ${y});`}).filter(l=>l!==void 0).join(`
352
+ `),u=e.map(l=>{if(l.lifecycle||Object.keys(l.args).length===0)return;const y=En(l.args);return y===void 0?void 0:`installCompiledValidatorMap(${o.get(l.filePath)??""}.${l.exportName}.args, ${y});`}).filter(l=>l!==void 0).join(`
332
353
  `);return{dispatchBody:w.length>0?`
333
354
  ${w}
334
355
  `:"",importBlock:i.length>0?`${i}
@@ -337,19 +358,19 @@ ${w}
337
358
  ${c}
338
359
  `:"",mutatorPaths:m,shapeBody:p.length>0?`
339
360
  ${p}
340
- `:""}},ir=e=>{const t=Ye(e),n=t.map(([o,s])=>{const i=s.map(a=>{const c=_(xe(a.args),a.filePath),d=c==="{}"?"?":"",p=_(vt(a),a.filePath);return a.kind==="stream"?` ${a.exportName}: (args${d}: ${c}) => Promise<AsyncIterable<${p}>>;`:` ${a.exportName}: (args${d}: ${c}) => Promise<${p}>;`}).join(`
341
- `);return` ${j($(o))}: {
361
+ `:""}},ir=e=>{const t=Ye(e),n=t.map(([o,s])=>{const i=s.map(a=>{const c=E(xe(a.args),a.filePath),d=c==="{}"?"?":"",p=E(vt(a),a.filePath);return a.kind==="stream"?` ${a.exportName}: (args${d}: ${c}) => Promise<AsyncIterable<${p}>>;`:` ${a.exportName}: (args${d}: ${c}) => Promise<${p}>;`}).join(`
362
+ `);return` ${q($(o))}: {
342
363
  ${i}
343
364
  };`}).join(`
344
365
  `);return{implementation:t.map(([o,s])=>{const i=$(o),a=s.map(c=>` ${X(c.exportName)}: (args) => callRegistered(context, "${i}:${c.exportName}", args),`).join(`
345
366
  `);return` ${X(i)}: {
346
367
  ${a}
347
368
  },`}).join(`
348
- `),types:n}},lr=(e,t=[])=>{const n=new Set;for(const r of e.tables)for(const o of Object.values(r.shape)){const s=se(o);s.kind==="storage"&&typeof s.bucket=="string"&&s.bucket!==""&&n.add(s.bucket)}for(const r of t)r!==""&&r!=="default"&&n.add(r);return["default",...[...n].toSorted((r,o)=>r.localeCompare(o))]},ao=({agents:e=[],containers:t=[],env:n,hasAccessFacade:r=!1,hasAi:o=!1,hasAnalytics:s=!1,hasBrowser:i=!1,hasVectors:a=!0,hasFlags:c=!1,hasHyperdrive:d=!1,hasImages:p=!1,hasKv:m=!1,hasNotify:w=!1,hasPayments:u=!1,hasPipelines:l=!1,hasR2sql:y=!1,hasX402:R=!1,identity:x,queues:k=[],schema:N,storageRuleBuckets:T=[],useUmbrella:D=!1,workflows:C=[]}={})=>{const f=ae(D),P=lr(N??{tables:[]},T).map(h=>JSON.stringify(h)).join(" | "),ie=o?`import type { LunoraAi } from "@lunora/ai";
349
- `:"",F=o?`
369
+ `),types:n}},lr=(e,t=[])=>{const n=new Set;for(const r of e.tables)for(const o of Object.values(r.shape)){const s=se(o);s.kind==="storage"&&typeof s.bucket=="string"&&s.bucket!==""&&n.add(s.bucket)}for(const r of t)r!==""&&r!=="default"&&n.add(r);return["default",...[...n].toSorted((r,o)=>r.localeCompare(o))]},ao=({agents:e=[],containers:t=[],env:n,hasAccessFacade:r=!1,hasAi:o=!1,hasAnalytics:s=!1,hasBrowser:i=!1,hasVectors:a=!0,hasFlags:c=!1,hasHyperdrive:d=!1,hasImages:p=!1,hasKv:m=!1,hasNotify:w=!1,hasPayments:u=!1,hasPipelines:l=!1,hasR2sql:y=!1,hasX402:R=!1,identity:x,queues:k=[],schema:T,storageRuleBuckets:N=[],useUmbrella:D=!1,workflows:C=[]}={})=>{const f=ae(D),P=lr(T??{tables:[]},N).map(h=>JSON.stringify(h)).join(" | "),ie=o?`import type { LunoraAi } from "@lunora/ai";
370
+ `:"",M=o?`
350
371
  readonly ai: LunoraAi;`:"",le=u?`import type { LunoraPayment } from "@lunora/payment";
351
372
  `:"",ce=u?`
352
- readonly payments: LunoraPayment;`:"",q=R?`import type { X402Pay } from "@lunora/x402/pay";
373
+ readonly payments: LunoraPayment;`:"",j=R?`import type { X402Pay } from "@lunora/x402/pay";
353
374
  `:"",I=R?`
354
375
  readonly x402: X402Pay;`:"",ee=t.length>0?`import type { ContainerAccessor } from "@lunora/container";
355
376
  `:"",L=t.length>0?`
@@ -378,7 +399,7 @@ ${v}`:""}
378
399
  }
379
400
 
380
401
  /** Alias for {@link CloudflareBindings} — the typed shape of \`env\`. */
381
- export type Env = CloudflareBindings;`,M=(h,We,Ne)=>{const re=Sn.get(h);if(re!==void 0&&re.tier!==Ne)throw new Z("INTERNAL",`@lunora/codegen: capability "${h}" declares tier "${re.tier}" in SERVER_CTX_FIELDS but is emitted onto the "${Ne}" context — update whichever is wrong.`);return We?re?.field??"":""},de=M("kv",m,"every"),ue=M("analytics",s,"every"),Oe=M("hyperdrive",d,"action"),Ie=M("browser",i,"action"),pe=M("images",p,"action"),V=M("pipelines",l,"action"),H=M("r2sql",y,"action"),W=a&&(N?.vectorIndexes.length??0)>0,G=W?' | "vectors"':"",te=W?`
402
+ export type Env = CloudflareBindings;`,F=(h,We,Te)=>{const re=Sn.get(h);if(re!==void 0&&re.tier!==Te)throw new Z("INTERNAL",`@lunora/codegen: capability "${h}" declares tier "${re.tier}" in SERVER_CTX_FIELDS but is emitted onto the "${Te}" context — update whichever is wrong.`);return We?re?.field??"":""},de=F("kv",m,"every"),ue=F("analytics",s,"every"),Oe=F("hyperdrive",d,"action"),Ie=F("browser",i,"action"),pe=F("images",p,"action"),V=F("pipelines",l,"action"),H=F("r2sql",y,"action"),W=a&&(T?.vectorIndexes.length??0)>0,G=W?' | "vectors"':"",te=W?`
382
403
  readonly vectors: VectorSearch<VectorIndexName>;`:"",J=W?`
383
404
  readonly vectors: VectorSearchReader<VectorIndexName>;`:"",ne=W?`import type { VectorSearch, VectorSearchReader } from "${f.server}";
384
405
  import type { VectorIndexName } from "./dataModel.js";
@@ -406,9 +427,9 @@ export interface LunoraWorkflows {
406
427
  ${C.map(h=>` get(name: ${JSON.stringify(h.exportName)}): WorkflowHandle<WorkflowParamsOf<typeof lunoraWorkflowDefinitions.${h.exportName}>>;`).join(`
407
428
  `)}
408
429
  }`:"",ve=B?' | "workflows"':"",Re=B?`
409
- readonly workflows: LunoraWorkflows;`:"",he=k.length>0,_e=he?`import type { QueueProducer } from "@lunora/queue";
430
+ readonly workflows: LunoraWorkflows;`:"",he=k.length>0,Ee=he?`import type { QueueProducer } from "@lunora/queue";
410
431
  import type * as lunoraQueueDefinitions from "../queues.js";
411
- `:"",Ee=he?`
432
+ `:"",_e=he?`
412
433
 
413
434
  /** Message body type carried by a \`defineQueue\` definition (its phantom \`__lunoraBody\`). */
414
435
  type QueueBodyOf<Definition> = Definition extends { __lunoraBody?: infer Body } ? (unknown extends Body ? unknown : NonNullable<Body>) : unknown;
@@ -426,17 +447,17 @@ export interface LunoraAgents {
426
447
  ${e.map(h=>(b(h.exportName,`agent export "${h.exportName}"`),` readonly ${h.exportName}: AgentHandle;`)).join(`
427
448
  `)}
428
449
  }`:"",$e=me?`
429
- readonly agents: LunoraAgents;`:"",Fe=x?`import type { InferIdentity } from "${f.server}";
450
+ readonly agents: LunoraAgents;`:"",Me=x?`import type { InferIdentity } from "${f.server}";
430
451
  import type * as lunoraIdentityContract from "../identity.js";
431
- `:"",Me=x?`
452
+ `:"",Fe=x?`
432
453
 
433
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. */
434
455
  export type Identity = InferIdentity<typeof lunoraIdentityContract.${x.exportName}>;
435
456
 
436
457
  /** \`ctx.auth\` narrowed so \`getIdentity()\` resolves the declared {@link Identity} contract instead of the untyped claim bag. */
437
458
  type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: () => Promise<Identity | null> };`:"",ge=x?' | "auth"':"",fe=x?`
438
- readonly auth: NarrowedAuth;`:"",Be=x?", Identity":"",je=n?`import type * as lunoraEnvContract from "../env.js";
439
- `:"",qe=n?`
459
+ readonly auth: NarrowedAuth;`:"",Be=x?", Identity":"",qe=n?`import type * as lunoraEnvContract from "../env.js";
460
+ `:"",je=n?`
440
461
 
441
462
  /** This app's declared env contract (\`defineEnv\` in \`lunora/env.ts\`) — the validated, coercion-aware shape of \`ctx.env\`. */
442
463
  export type LunoraEnv = ReturnType<typeof lunoraEnvContract.${n.exportName}>;`:"",be=n?' | "env"':"",ye=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 "${f.server}";
@@ -496,7 +517,7 @@ export type {
496
517
  } from "${f.serverDataModel}";
497
518
 
498
519
  import type { DataModel, Doc, GeoIndexNamesByTable, Id as IdOfTable, IndexNamesByTable, Insert, InsertModel, RankIndexNamesByTable, Relations, SearchIndexNamesByTable, TableName } from "./dataModel.js";
499
- ${ne}${ie}${le}${q}${ee}${Ce}${_e}${De}${Fe}${je}
520
+ ${ne}${ie}${le}${j}${ee}${Ce}${Ee}${De}${Me}${qe}
500
521
  export type { AppTableName, DataModel, Doc, Id, TableName } from "./dataModel.js";
501
522
 
502
523
  /**
@@ -556,7 +577,7 @@ export interface OrmWriter extends OrmReader {
556
577
  }
557
578
 
558
579
  /** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
559
- export type StorageBucketName = ${P};${Ae}${Le}${Ee}${Pe}${Me}${qe}
580
+ export type StorageBucketName = ${P};${Ae}${Le}${_e}${Pe}${Fe}${je}
560
581
 
561
582
  /**
562
583
  * Project-typed contexts. The base contexts from \`@lunora/server\` are
@@ -634,7 +655,7 @@ export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${G}$
634
655
  export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${G}${ve}${ge}${be}> {
635
656
  readonly db: Omit<DatabaseWriter, "asId" | "query" | "get"> & DatabaseWriterFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
636
657
  readonly orm: OrmWriter;
637
- readonly storage: StorageBase<StorageBucketName>;${te}${U}${F}${ce}${I}${L}${de}${K}${Q}${Oe}${Ie}${pe}${ue}${V}${H}${ye}${Re}${Se}${$e}${fe}
658
+ readonly storage: StorageBase<StorageBucketName>;${te}${U}${M}${ce}${I}${L}${de}${K}${Q}${Oe}${Ie}${pe}${ue}${V}${H}${ye}${Re}${Se}${$e}${fe}
638
659
  }
639
660
 
640
661
  /**
@@ -710,11 +731,11 @@ export const v = vBase as unknown as Omit<typeof vBase, "id"> & {
710
731
  id: <T extends TableName>(table: T) => ColumnValidator<IdOfTable<T>, IdOfTable<T>>;
711
732
  };
712
733
  `},cr=e=>{const t={connect:[],disconnect:[],init:[],reactor:[]};for(const n of e)n.lifecycle&&t[n.lifecycle].push(`${$(n.filePath)}:${n.exportName}`);return t},so=e=>{const{agents:t=[],functions:n,migrations:r=[],mutators:o=[],shapes:s=[],useUmbrella:i=!1,usesSandbox:a=!1}=e,c=n.length>0,d=ae(i),{dispatchBody:p,importBlock:m,installBlock:w,migrationBody:u,mutatorPaths:l,shapeBody:y}=sr(n,r,o,s),R=Zn(t,n),x=er(a,n),k=[R.lines,x.lines].filter(v=>v.length>0).join(`
713
- `);let N=p;k.length>0&&(N=p.length>0?`${p.trimEnd()}
734
+ `);let T=p;k.length>0&&(T=p.length>0?`${p.trimEnd()}
714
735
  ${k}
715
736
  `:`
716
737
  ${k}
717
- `);const T=cr(n),D=s.length>0?`import type { RegisteredShape } from "${d.server}";
738
+ `);const N=cr(n),D=s.length>0?`import type { RegisteredShape } from "${d.server}";
718
739
  `:"",C=s.length>0?`
719
740
  /**
720
741
  * Replication-shape registry — one entry per \`defineShape\` in \`lunora/shapes.ts\`.
@@ -738,11 +759,11 @@ export const LUNORA_MUTATOR_PATHS: ReadonlySet<string> = new Set([${l.map(v=>JSO
738
759
  * parser as a zero-allocation fast path; anything it can't model is deferred.
739
760
  */
740
761
  ${w}
741
- `:"",F=ir(n),le=F.types?`
742
- ${F.types}
743
- `:"",ce=F.implementation?`
744
- ${F.implementation}
745
- `:"",q=ze(F.types),I=q.length>0?`import type { ${q.join(", ")} } from "./dataModel.js";
762
+ `:"",M=ir(n),le=M.types?`
763
+ ${M.types}
764
+ `:"",ce=M.implementation?`
765
+ ${M.implementation}
766
+ `:"",j=ze(M.types),I=j.length>0?`import type { ${j.join(", ")} } from "./dataModel.js";
746
767
  `:"",ee=c?"context":"_context",L=c?`${ar}
747
768
 
748
769
  `:"";return xt(`${S}${m}${R.importLine}${x.importLine}${P}${D}import { LunoraError } from "${d.server}";
@@ -778,13 +799,18 @@ export interface RegisteredLunoraFunction {
778
799
  lifecycle?: "connect" | "disconnect" | "init" | "reactor";
779
800
  /** \`"internal"\` functions are rejected on the external RPC path; absence === public. */
780
801
  visibility?: "internal" | "public";
802
+ /**
803
+ * \`.x402({ price })\` tag on a paid public procedure. The origin worker
804
+ * paywalls it; the shard refuses to subscribe it (\`isPaidFunction\`).
805
+ */
806
+ x402?: { readonly price: number | string };
781
807
  }
782
808
  ${R.prelude}${x.prelude}
783
809
  /**
784
810
  * Static dispatch table. The key matches the \`__lunoraRef\` the client
785
811
  * emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
786
812
  */
787
- export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${N}};
813
+ export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${T}};
788
814
  ${ie}${C}${f}
789
815
  /**
790
816
  * Lifecycle manifest: the function paths the generated ShardDO dispatches when a
@@ -796,10 +822,10 @@ ${ie}${C}${f}
796
822
  * anonymous — all via system dispatch.
797
823
  */
798
824
  export const LUNORA_LIFECYCLE_HOOKS: { connect: readonly string[]; disconnect: readonly string[]; init: readonly string[]; reactor: readonly string[] } = {
799
- connect: [${T.connect.map(v=>JSON.stringify(v)).join(", ")}],
800
- disconnect: [${T.disconnect.map(v=>JSON.stringify(v)).join(", ")}],
801
- init: [${T.init.map(v=>JSON.stringify(v)).join(", ")}],
802
- reactor: [${T.reactor.map(v=>JSON.stringify(v)).join(", ")}],
825
+ connect: [${N.connect.map(v=>JSON.stringify(v)).join(", ")}],
826
+ disconnect: [${N.disconnect.map(v=>JSON.stringify(v)).join(", ")}],
827
+ init: [${N.init.map(v=>JSON.stringify(v)).join(", ")}],
828
+ reactor: [${N.reactor.map(v=>JSON.stringify(v)).join(", ")}],
803
829
  };
804
830
 
805
831
  /**
@@ -899,7 +925,7 @@ const aiStub: LunoraAi = {
899
925
  const ${e} = {
900
926
  ${s}
901
927
  }${r};
902
- `},E={build:"",configField:"",contextField:"",importLines:[],stub:""},wr=e=>e?{build:`
928
+ `},_={build:"",configField:"",contextField:"",importLines:[],stub:""},wr=e=>e?{build:`
903
929
  const kvBinding = config.kv?.(env) ?? (env as Record<string, unknown>).KV;
904
930
  // KV is not this shard's SQLite, so nothing appends a \`__cdc_log\` entry
905
931
  // when a value changes — a subscription that read one can never be proven
@@ -913,24 +939,24 @@ ${s}
913
939
  ]);
914
940
  `,configField:`
915
941
  kv?: (env: Record<string, unknown>) => KVNamespaceLike;`,contextField:`
916
- kv,`,importLines:['import type { Kv, KVNamespaceLike } from "@lunora/bindings/kv";','import { createKv } from "@lunora/bindings/kv";'],stub:O("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"])}:E,kr=(e,t)=>e?{build:`
942
+ kv,`,importLines:['import type { Kv, KVNamespaceLike } from "@lunora/bindings/kv";','import { createKv } from "@lunora/bindings/kv";'],stub:O("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"])}:_,kr=(e,t)=>e?{build:`
917
943
  const flags: import("${t}").LunoraFlags = createFlags(flagsConfig, env, {
918
944
  provider: () => config.flags?.(env),
919
945
  targetingKey: () => flagsConfig.identify?.({ identity: identity ?? null, userId: userId ?? null }),
920
946
  });
921
947
  `,configField:`
922
948
  flags?: (env: Record<string, unknown>) => import("${t}").Provider;`,contextField:`
923
- flags,`,importLines:[`import { createFlags } from "${t}";`,'import flagsConfig from "../flags.js";'],stub:""}:E,xr=e=>e?{build:`
949
+ flags,`,importLines:[`import { createFlags } from "${t}";`,'import flagsConfig from "../flags.js";'],stub:""}:_,xr=e=>e?{build:`
924
950
  const { notify, push } = createNotify(notifyConfig, env, { log, metrics });
925
951
  `,configField:"",contextField:`
926
952
  notify,
927
- push,`,importLines:['import { createNotify } from "@lunora/notify";','import notifyConfig from "../notify.js";'],stub:""}:E,vr=e=>e?{build:`
953
+ push,`,importLines:['import { createNotify } from "@lunora/notify";','import notifyConfig from "../notify.js";'],stub:""}:_,vr=e=>e?{build:`
928
954
  const envConfig = lunoraEnvContract.${e.exportName}(env);
929
955
  `,configField:"",contextField:`
930
- env: envConfig,`,importLines:['import * as lunoraEnvContract from "../env.js";'],stub:""}:E,Rr=e=>e?{build:`
956
+ env: envConfig,`,importLines:['import * as lunoraEnvContract from "../env.js";'],stub:""}:_,Rr=e=>e?{build:`
931
957
  const access = accessFacade(identity, userId);
932
958
  `,configField:"",contextField:`
933
- access,`,importLines:['import { accessFacade } from "@lunora/cloudflare-access/context";'],stub:""}:E,Sr=(e,t,n)=>{if(!t)return{constant:"",evaluateOverride:"",subscriptionOverride:""};const r=a=>`
959
+ access,`,importLines:['import { accessFacade } from "@lunora/cloudflare-access/context";'],stub:""}:_,Sr=(e,t,n)=>{if(!t)return{constant:"",evaluateOverride:"",subscriptionOverride:""};const r=a=>`
934
960
  const env = (this.env ?? {}) as Record<string, unknown>;
935
961
  const flags: import("${n}").LunoraFlags = createFlags(flagsConfig, env, {
936
962
  provider: () => config.flags?.(env),
@@ -997,20 +1023,20 @@ const LUNORA_FLAG_KEYS: ReadonlyArray<{ key: string; type: "boolean" | "number"
997
1023
  const analytics: AnalyticsClient = analyticsBinding ? createAnalytics(analyticsBinding as AnalyticsEngineDatasetLike) : analyticsStub;
998
1024
  `,configField:`
999
1025
  analytics?: (env: Record<string, unknown>) => AnalyticsEngineDatasetLike;`,contextField:`
1000
- analytics,`,importLines:['import type { AnalyticsClient, AnalyticsEngineDatasetLike } from "@lunora/bindings/analytics";','import { createAnalytics } from "@lunora/bindings/analytics";'],stub:O("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"]})}:E,Nr=e=>e?{build:`
1026
+ analytics,`,importLines:['import type { AnalyticsClient, AnalyticsEngineDatasetLike } from "@lunora/bindings/analytics";','import { createAnalytics } from "@lunora/bindings/analytics";'],stub:O("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"]})}:_,Tr=e=>e?{build:`
1001
1027
  const imagesBinding = config.images?.(env) ?? (env as Record<string, unknown>).IMAGES;
1002
1028
  const images: Images = imagesBinding ? createImages({ binding: imagesBinding as ImagesBindingLike }) : imagesStub;
1003
1029
  `,configField:`
1004
1030
  images?: (env: Record<string, unknown>) => ImagesBindingLike;`,contextField:`
1005
- images,`,importLines:['import type { Images, ImagesBindingLike } from "@lunora/bindings/images";','import { createImages } from "@lunora/bindings/images";'],stub:O("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"])}:E,Tr=e=>e?{build:`
1031
+ images,`,importLines:['import type { Images, ImagesBindingLike } from "@lunora/bindings/images";','import { createImages } from "@lunora/bindings/images";'],stub:O("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"])}:_,Nr=e=>e?{build:`
1006
1032
  const sql: SqlClient = config.sql ? config.sql(env) : sqlStub;
1007
1033
  `,configField:`
1008
1034
  sql?: (env: Record<string, unknown>) => SqlClient;`,contextField:`
1009
- sql,`,importLines:['import type { SqlClient } from "@lunora/hyperdrive";'],stub:O("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"])}:E,Ar=e=>e?{build:`
1035
+ sql,`,importLines:['import type { SqlClient } from "@lunora/hyperdrive";'],stub:O("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"])}:_,Ar=e=>e?{build:`
1010
1036
  const browser: Browser = config.browser ? config.browser(env) : browserStub;
1011
1037
  `,configField:`
1012
1038
  browser?: (env: Record<string, unknown>) => Browser;`,contextField:`
1013
- browser,`,importLines:['import type { Browser } from "@lunora/browser";'],stub:O("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"])}:E,Or=e=>e?{build:`
1039
+ browser,`,importLines:['import type { Browser } from "@lunora/browser";'],stub:O("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"])}:_,Or=e=>e?{build:`
1014
1040
  const r2sqlEnv = env as Record<string, unknown>;
1015
1041
  const r2sqlAccountId = (r2sqlEnv.R2_SQL_ACCOUNT_ID ?? r2sqlEnv.CLOUDFLARE_ACCOUNT_ID) as string | undefined;
1016
1042
  const r2sqlToken = r2sqlEnv.R2_SQL_TOKEN as string | undefined;
@@ -1021,11 +1047,11 @@ const LUNORA_FLAG_KEYS: ReadonlyArray<{ key: string; type: "boolean" | "number"
1021
1047
  ? createR2Sql({ accountId: r2sqlAccountId, apiToken: r2sqlToken, bucket: r2sqlBucket })
1022
1048
  : r2sqlStub;
1023
1049
  `,configField:`
1024
- r2sql?: (env: Record<string, unknown>) => R2SqlClient;`,contextField:"",importLines:['import type { R2SqlClient } from "@lunora/bindings/r2sql";','import { createR2Sql } from "@lunora/bindings/r2sql";'],stub:O("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"]})}:E,Ir=e=>e?{build:`
1050
+ r2sql?: (env: Record<string, unknown>) => R2SqlClient;`,contextField:"",importLines:['import type { R2SqlClient } from "@lunora/bindings/r2sql";','import { createR2Sql } from "@lunora/bindings/r2sql";'],stub:O("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"]})}:_,Ir=e=>e?{build:`
1025
1051
  const pipelinesBinding = config.pipelines?.(env) ?? (env as Record<string, unknown>).PIPELINES;
1026
1052
  const pipelines: PipelineClient = pipelinesBinding ? createPipelines({ binding: pipelinesBinding as PipelineBindingLike }) : pipelinesStub;
1027
1053
  `,configField:`
1028
- pipelines?: (env: Record<string, unknown>) => PipelineBindingLike;`,contextField:"",importLines:['import type { PipelineBindingLike, PipelineClient } from "@lunora/bindings/pipelines";','import { createPipelines } from "@lunora/bindings/pipelines";'],stub:O("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"])}:E,io=(e,t)=>{if(e.length===0)return"";const n=t?`, ${JSON.stringify(t)}`:"",r=e.map(s=>(b(s.exportName,`container export "${s.exportName}"`),b(s.className,`container class "${s.className}"`),`/** Container DO for the \`${s.exportName}\` definition (binding \`${s.bindingName}\`). */
1054
+ pipelines?: (env: Record<string, unknown>) => PipelineBindingLike;`,contextField:"",importLines:['import type { PipelineBindingLike, PipelineClient } from "@lunora/bindings/pipelines";','import { createPipelines } from "@lunora/bindings/pipelines";'],stub:O("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"])}:_,io=(e,t)=>{if(e.length===0)return"";const n=t?`, ${JSON.stringify(t)}`:"",r=e.map(s=>(b(s.exportName,`container export "${s.exportName}"`),b(s.className,`container class "${s.className}"`),`/** Container DO for the \`${s.exportName}\` definition (binding \`${s.bindingName}\`). */
1029
1055
  export class ${s.className} extends LunoraContainer {
1030
1056
  public constructor(ctx: ConstructorParameters<typeof LunoraContainer>[0], env: Record<string, unknown>) {
1031
1057
  super(ctx, env, ${s.exportName}, "${s.exportName}"${n});
@@ -1139,7 +1165,7 @@ ${r}
1139
1165
  const LUNORA_WORKFLOWS: ReadonlyArray<WorkflowBindingSpec> = [
1140
1166
  ${t}
1141
1167
  ];
1142
- `}},_r=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)b(n.exportName,`queue export "${n.exportName}"`),b(n.bindingName,`queue binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}", name: ${JSON.stringify(n.name)} },`).join(`
1168
+ `}},Er=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)b(n.exportName,`queue export "${n.exportName}"`),b(n.bindingName,`queue binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}", name: ${JSON.stringify(n.name)} },`).join(`
1143
1169
  `);return{build:`
1144
1170
  const queues = createQueueContext(env, LUNORA_QUEUES);
1145
1171
  `,contextField:`
@@ -1148,7 +1174,7 @@ ${t}
1148
1174
  const LUNORA_QUEUES: ReadonlyArray<QueueBindingSpec> = [
1149
1175
  ${t}
1150
1176
  ];
1151
- `}},Er=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)b(n.exportName,`agent export "${n.exportName}"`),b(n.bindingName,`agent binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}"${n.publicRun===!0?", publicRun: true":""} },`).join(`
1177
+ `}},_r=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)b(n.exportName,`agent export "${n.exportName}"`),b(n.bindingName,`agent binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}"${n.publicRun===!0?", publicRun: true":""} },`).join(`
1152
1178
  `);return{build:`
1153
1179
  const agents = createAgentContext(env, LUNORA_AGENTS);
1154
1180
  `,contextField:`
@@ -1164,21 +1190,21 @@ const ${e}: ${t} = ${JSON.stringify(r,void 0,4)};
1164
1190
  protected override ${n}(): ${t} {
1165
1191
  return ${e};
1166
1192
  }
1167
- `}),Dr=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 St("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. */")},Pr=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 St("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. */")},Fr=e=>e?{imports:['import type { LunoraDatabaseLike as LunoraPaymentDbLike, LunoraPayment, PaymentsFromContextOptions } from "@lunora/payment";','import { paymentsFromContext } from "@lunora/payment";'],build:`
1193
+ `}),Dr=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 St("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. */")},Pr=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 St("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. */")},Mr=e=>e?{imports:['import type { LunoraDatabaseLike as LunoraPaymentDbLike, LunoraPayment, PaymentsFromContextOptions } from "@lunora/payment";','import { paymentsFromContext } from "@lunora/payment";'],build:`
1168
1194
  const payments: LunoraPayment = config.payment
1169
1195
  ? paymentsFromContext({ auth: { userId: userId ?? null }, db: db as unknown as LunoraPaymentDbLike }, config.payment(env))
1170
1196
  : paymentStub;
1171
1197
  `,configField:`
1172
1198
  payment?: (env: Record<string, unknown>) => PaymentsFromContextOptions;`,contextField:`
1173
- payments,`,stub:O("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:""},Mr=e=>e?{imports:['import type { X402Pay, X402PayConfig } from "@lunora/x402/pay";','import { lazyX402Pay } from "@lunora/x402/pay";'],build:`
1199
+ payments,`,stub:O("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:""},Fr=e=>e?{imports:['import type { X402Pay, X402PayConfig } from "@lunora/x402/pay";','import { lazyX402Pay } from "@lunora/x402/pay";'],build:`
1174
1200
  const x402: X402Pay = config.x402
1175
1201
  ? lazyX402Pay(config.x402(env), { getSecret: (name: string) => secrets.get(name) })
1176
1202
  : x402Stub;
1177
1203
  `,configField:`
1178
- x402?: (env: Record<string, unknown>) => X402PayConfig;`,stub:O("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:""},Br=(e,t,n,r,o)=>["AdvisorProcedure","AdvisoryFinding","DatabaseWriterLike","DataMigrationLike","ExportRow",...r?["FlagsResult"]:[],"ImportShardResult","KeyRange","MaskPoliciesResult","MigrationRunResult","QueryReadScope",...n?["QueuesResult"]:[],"RunShardApplyCdcArgs","RunShardExportArgs","RunShardImportArgs","RunShardMigrationArgs","RlsPoliciesResult","RunShardRankBeforeArgs","RunShardRankPageArgs","RunShardWriteArgs","RunShardWriteResult","SchedulerLike",...o?["SearchBackfillProgress"]:[],"TransactionHeadroomTracker","SchemaLike","ShardDOState","ShardRankPageResult","SqlExec","StorageRulesResult","StudioFeaturesResult","SystemReaderStorageLike","TelemetrySink",...t?["WorkflowsResult"]:[],...e?["WriteHook"]:[]],po=({advisories:e=[],advisorProcedures:t=[],agents:n=[],containers:r=[],env:o,flagKeys:s=[],hasAccessFacade:i=!1,hasAi:a=!1,hasAnalytics:c=!1,hasBrowser:d=!1,hasFlags:p=!1,hasHyperdrive:m=!1,hasImages:w=!1,hasKv:u=!1,hasNotify:l=!1,hasPayments:y=!1,hasPipelines:R=!1,hasR2sql:x=!1,hasX402:k=!1,maskMetadata:N,mutators:T=[],queues:D=[],rlsMetadata:C,schema:f,schemaSnapshot:P,shapes:ie=[],storageRules:F,studioFeatures:le,useUmbrella:ce=!1,workflows:q=[]})=>{const I=ae(ce),ee=T.length>0,L=ie.length>0,v=P===void 0?"":xn(P),Ae=P===void 0?"":`
1204
+ x402?: (env: Record<string, unknown>) => X402PayConfig;`,stub:O("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:""},Br=(e,t,n,r,o)=>["AdvisorProcedure","AdvisoryFinding","DatabaseWriterLike","DataMigrationLike","ExportRow",...r?["FlagsResult"]:[],"ImportShardResult","KeyRange","MaskPoliciesResult","MigrationRunResult","QueryReadScope",...n?["QueuesResult"]:[],"RunShardApplyCdcArgs","RunShardExportArgs","RunShardImportArgs","RunShardMigrationArgs","RlsPoliciesResult","RunShardRankBeforeArgs","RunShardRankPageArgs","RunShardWriteArgs","RunShardWriteResult","SchedulerLike",...o?["SearchBackfillProgress"]:[],"TransactionHeadroomTracker","SchemaLike","ShardDOState","ShardRankPageResult","SqlExec","StorageRulesResult","StudioFeaturesResult","SystemReaderStorageLike","TelemetrySink",...t?["WorkflowsResult"]:[],...e?["WriteHook"]:[]],po=({advisories:e=[],advisorProcedures:t=[],agents:n=[],containers:r=[],env:o,flagKeys:s=[],hasAccessFacade:i=!1,hasAi:a=!1,hasAnalytics:c=!1,hasBrowser:d=!1,hasFlags:p=!1,hasHyperdrive:m=!1,hasImages:w=!1,hasKv:u=!1,hasNotify:l=!1,hasPayments:y=!1,hasPipelines:R=!1,hasR2sql:x=!1,hasX402:k=!1,maskMetadata:T,mutators:N=[],queues:D=[],rlsMetadata:C,schema:f,schemaSnapshot:P,shapes:ie=[],storageRules:M,studioFeatures:le,useUmbrella:ce=!1,workflows:j=[]})=>{const I=ae(ce),ee=N.length>0,L=ie.length>0,v=P===void 0?"":xn(P),Ae=P===void 0?"":`
1179
1205
  /** 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. */
1180
1206
  const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(vn(P))}, json: ${JSON.stringify(v)} };
1181
- `,M=P===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",{build:de,configField:ue,contextField:Oe,stub:Ie}=yr(a),pe=Rr(i),V=wr(u),H=kr(p,I.flags),W=Sr(s,p,I.flags),G=xr(l),te=vr(o),J=$r(c),ne=Nr(w),U=Tr(m),K=Ar(d),Q=Or(x),B=Ir(R),{build:Ce,contextField:Le,importLines:ve,specs:Re}=_r(D),{build:he,contextField:_e,importLines:Ee,specs:Se}=Cr(r,f.jurisdiction),{build:me,contextField:De,importLines:Pe,specs:$e}=Lr(q),{build:Fe,contextField:Me,importLines:ge,specs:fe}=Er(n),{build:Be,configField:je,contextField:qe,imports:be,stub:ye}=Fr(y),{build:Ze,configField:h,imports:We,stub:Ne}=Mr(k),re=e,Nt=t,Tt=C??{policies:[],roles:[]},At=N??{columns:[]},Ot=F??{rules:[]},It=le??{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:Ct,override:Lt}=Dr(q),{constant:_t,override:Et}=Pr(D),z=f.vectorIndexes.length>0,et=new Set(f.tables.filter(g=>typeof g.shardMode=="object"&&g.shardMode.kind==="shardBy").map(g=>g.name)),Te=f.vectorIndexes.some(g=>et.has(g.table)),we=f.tables.some(g=>g.shardMode==="global"),Ue=f.tables.some(g=>g.shardMode==="global"&&g.globalBackend==="hyperdrive"),tt=f.tables.some(g=>g.shardMode==="global"&&g.globalBackend!=="hyperdrive"),oe=f.tables.some(g=>g.externalSource!==void 0),nt=f.tables.some(g=>g.memory===!0),Ke=f.tables.some(g=>g.shardMode!=="global"&&g.searchIndexes.length>0);if(tt&&Ue)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 Qe=f.tables.length>0,Dt=dr(f),Pt=pr(f),Ft=fr(f),Mt=ur(f),rt=hr(f),ot=Br(z,q.length>0,D.length>0,p,Ke);L&&ot.push("WhereInput");const at=br(we),Bt=L?`
1207
+ `,F=P===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",{build:de,configField:ue,contextField:Oe,stub:Ie}=yr(a),pe=Rr(i),V=wr(u),H=kr(p,I.flags),W=Sr(s,p,I.flags),G=xr(l),te=vr(o),J=$r(c),ne=Tr(w),U=Nr(m),K=Ar(d),Q=Or(x),B=Ir(R),{build:Ce,contextField:Le,importLines:ve,specs:Re}=Er(D),{build:he,contextField:Ee,importLines:_e,specs:Se}=Cr(r,f.jurisdiction),{build:me,contextField:De,importLines:Pe,specs:$e}=Lr(j),{build:Me,contextField:Fe,importLines:ge,specs:fe}=_r(n),{build:Be,configField:qe,contextField:je,imports:be,stub:ye}=Mr(y),{build:Ze,configField:h,imports:We,stub:Te}=Fr(k),re=e,Tt=t,Nt=C??{policies:[],roles:[]},At=T??{columns:[]},Ot=M??{rules:[]},It=le??{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:Ct,override:Lt}=Dr(j),{constant:Et,override:_t}=Pr(D),z=f.vectorIndexes.length>0,et=new Set(f.tables.filter(g=>typeof g.shardMode=="object"&&g.shardMode.kind==="shardBy").map(g=>g.name)),Ne=f.vectorIndexes.some(g=>et.has(g.table)),we=f.tables.some(g=>g.shardMode==="global"),Ue=f.tables.some(g=>g.shardMode==="global"&&g.globalBackend==="hyperdrive"),tt=f.tables.some(g=>g.shardMode==="global"&&g.globalBackend!=="hyperdrive"),oe=f.tables.some(g=>g.externalSource!==void 0),nt=f.tables.some(g=>g.memory===!0),Ke=f.tables.some(g=>g.shardMode!=="global"&&g.searchIndexes.length>0);if(tt&&Ue)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 Qe=f.tables.length>0,Dt=dr(f),Pt=pr(f),Mt=fr(f),Ft=ur(f),rt=hr(f),ot=Br(z,j.length>0,D.length>0,p,Ke);L&&ot.push("WhereInput");const at=br(we),Bt=L?`
1182
1208
  protected override resolveShape(name: string, args: Record<string, unknown>, identity?: { identity?: Record<string, unknown>; userId?: string }): { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string } | undefined {
1183
1209
  const shape = LUNORA_SHAPES[name];
1184
1210
 
@@ -1230,17 +1256,17 @@ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.st
1230
1256
 
1231
1257
  return { columns: shape.columns, effectiveWhere, global: isGlobal, table: shape.table };
1232
1258
  }
1233
- `:"",jt=ee?`
1259
+ `:"",qt=ee?`
1234
1260
  protected override isCustomMutator(functionPath: string): boolean {
1235
1261
  return LUNORA_MUTATOR_PATHS.has(functionPath);
1236
1262
  }
1237
- `:"",qt=L?"\n/** Per-table RLS read policies (hoisted from `.use(rls(...))` chains) the shape resolver AND-merges into each `defineShape` predicate so partial replication honours read policies. */\nconst LUNORA_RLS_READ_REGISTRY = buildRlsReadRegistry(Object.values(LUNORA_FUNCTIONS));\n":"",Wt=L?"assertShapeShardable, ":"",ke=[`import type { ${ot.join(", ")} } from "${I.do}";`,`import { applyCdcChanges, ${Ke?"backfillSearchIndexes, ":""}buildReprojectionMigration, ${nt?"clearMemoryTables, ":""}${Wt}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${oe?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}markUnvouchableReads, ${Te?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${at.importFragment}ShardDO as ShardDOBase } from "${I.do}";`,...oe?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${I.do}";`]:[],L?`import { asBucketStorage, buildRlsReadRegistry, composeShapeReadWhere, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes } from "${I.server}";`:`import { asBucketStorage, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes } from "${I.server}";`];Qe&&ke.push(`import { bindOrm, bindTableFacade } from "${I.server}";`),z&&ke.push('import type { SchemaLike as VectorSchemaLike, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectors, createVectorSyncHook } from "@lunora/bindings/vectors";'),a&&ke.push('import type { AiBindingLike, LunoraAi } from "@lunora/ai";','import { createAi } from "@lunora/ai";'),ke.push(...pe.importLines,...V.importLines,...H.importLines,...G.importLines,...te.importLines,...J.importLines,...ne.importLines,...U.importLines,...K.importLines,...Q.importLines,...B.importLines,...Ee,...Pe,...ve,...ge,...be,...We,"",'import schema from "../schema.js";',`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...ee?["LUNORA_MUTATOR_PATHS"]:[],...L?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const Ut=z?`
1263
+ `:"",jt=L?"\n/** Per-table RLS read policies (hoisted from `.use(rls(...))` chains) the shape resolver AND-merges into each `defineShape` predicate so partial replication honours read policies. */\nconst LUNORA_RLS_READ_REGISTRY = buildRlsReadRegistry(Object.values(LUNORA_FUNCTIONS));\n":"",Wt=L?"assertShapeShardable, ":"",ke=[`import type { ${ot.join(", ")} } from "${I.do}";`,`import { applyCdcChanges, ${Ke?"backfillSearchIndexes, ":""}buildReprojectionMigration, ${nt?"clearMemoryTables, ":""}${Wt}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${oe?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}markUnvouchableReads, ${Ne?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${at.importFragment}ShardDO as ShardDOBase } from "${I.do}";`,...oe?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${I.do}";`]:[],L?`import { asBucketStorage, beginDeferredSchedules, buildRlsReadRegistry, composeShapeReadWhere, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${I.server}";`:`import { asBucketStorage, beginDeferredSchedules, createSecrets, flushDeferredDeletes, LunoraError, withDeferredDeletes, withDeferredSchedules } from "${I.server}";`];Qe&&ke.push(`import { bindOrm, bindTableFacade } from "${I.server}";`),z&&ke.push('import type { SchemaLike as VectorSchemaLike, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectors, createVectorSyncHook } from "@lunora/bindings/vectors";'),a&&ke.push('import type { AiBindingLike, LunoraAi } from "@lunora/ai";','import { createAi } from "@lunora/ai";'),ke.push(...pe.importLines,...V.importLines,...H.importLines,...G.importLines,...te.importLines,...J.importLines,...ne.importLines,...U.importLines,...K.importLines,...Q.importLines,...B.importLines,..._e,...Pe,...ve,...ge,...be,...We,"",'import schema from "../schema.js";',`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...ee?["LUNORA_MUTATOR_PATHS"]:[],...L?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const Ut=z?`
1238
1264
  vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",Kt=tt?`
1239
1265
  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;`:"",Qt=Ue?`
1240
1266
  hyperdriveGlobal?: (env: Record<string, unknown>, request?: { cdc?: boolean; cdcRetentionMs?: number; identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",Vt=oe?`
1241
- sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",Ht='throw new Error("ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().");',Gt='throw new Error("ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().");',Jt='throw new Error("ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().");',zt='throw new Error("ctx.storage: no storage configured. Pass `storage` to createShardDO().");',Yt=we?O("globalDbStub: DatabaseWriterLike",Ht,["aggregate","count","delete","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",Xt=z?O("vectorsStub: VectorSearchLike",Gt,["deleteByIds","getByIds","query","upsert","upsertNow"]):"",Zt=Te?`
1267
+ sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",Ht='throw new Error("ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().");',Gt='throw new Error("ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().");',Jt='throw new Error("ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().");',zt='throw new Error("ctx.storage: no storage configured. Pass `storage` to createShardDO().");',Yt=we?O("globalDbStub: DatabaseWriterLike",Ht,["aggregate","count","delete","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",Xt=z?O("vectorsStub: VectorSearchLike",Gt,["deleteByIds","getByIds","query","upsert","upsertNow"]):"",Zt=Ne?`
1242
1268
  const vectorShardKey = this.currentShardKey();
1243
- `:"",en=Te?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",tn=Te?`, { namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, shardedIndexNames: [${f.vectorIndexes.filter(g=>et.has(g.table)).map(g=>JSON.stringify(g.name)).join(", ")}] }`:"",nn=z?`
1269
+ `:"",en=Ne?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",tn=Ne?`, { namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, shardedIndexNames: [${f.vectorIndexes.filter(g=>et.has(g.table)).map(g=>JSON.stringify(g.name)).join(", ")}] }`:"",nn=z?`
1244
1270
  let vectors: VectorSearchLike;
1245
1271
  let onWrite: WriteHook | undefined;
1246
1272
 
@@ -1561,6 +1587,10 @@ ${oe?`
1561
1587
  protected override isQueryFunction(functionPath: string): boolean {
1562
1588
  return LUNORA_FUNCTIONS[functionPath]?.kind === "query";
1563
1589
  }
1590
+
1591
+ protected override isPaidFunction(functionPath: string): boolean {
1592
+ return LUNORA_FUNCTIONS[functionPath]?.x402 !== undefined;
1593
+ }
1564
1594
  `,gn=Qe?`
1565
1595
  const facade = db as unknown as Record<string, ReturnType<typeof bindTableFacade>>;
1566
1596
  ${f.tables.map(g=>` facade[${JSON.stringify(g.name)}] = bindTableFacade(db, ${JSON.stringify(g.name)});`).join(`
@@ -1595,10 +1625,10 @@ const LUNORA_TABLE_INDEXES: Record<string, Array<{ fields: string[]; name: strin
1595
1625
  const LUNORA_TABLE_COLUMNS: Record<
1596
1626
  string,
1597
1627
  Array<{ bucket?: string; enumValues?: string[]; isStorage?: boolean; name: string; nullable?: boolean; optional: boolean; pk?: boolean; ref?: string; type: string }>
1598
- > = ${JSON.stringify(Ft,void 0,4)};
1628
+ > = ${JSON.stringify(Mt,void 0,4)};
1599
1629
 
1600
1630
  /** Storage-key columns per table (\`v.storage(...)\` fields) for the file browser's records↔files join. */
1601
- const LUNORA_STORAGE_COLUMNS: Record<string, string[]> = ${JSON.stringify(Mt,void 0,4)};
1631
+ const LUNORA_STORAGE_COLUMNS: Record<string, string[]> = ${JSON.stringify(Ft,void 0,4)};
1602
1632
 
1603
1633
  /** Declarative TTL policies (\`.ttl(field, { after? })\`) the DO alarm sweep auto-expires rows for. */
1604
1634
  const LUNORA_TTL_SWEEPS: Array<{ after?: number; field: string; softDeleteField?: string; table: string }> = ${JSON.stringify(rt,void 0,4)};
@@ -1607,10 +1637,10 @@ const LUNORA_TTL_SWEEPS: Array<{ after?: number; field: string; softDeleteField?
1607
1637
  const LUNORA_ADVISORIES: AdvisoryFinding[] = ${JSON.stringify(re,void 0,4)};
1608
1638
 
1609
1639
  /** Every declared procedure (discovered by @lunora/codegen) served via \`__lunora_admin__:getAdvisorProcedures\` — the health map's denominator. */
1610
- const LUNORA_ADVISOR_PROCEDURES: AdvisorProcedure[] = ${JSON.stringify(Nt,void 0,4)};
1640
+ const LUNORA_ADVISOR_PROCEDURES: AdvisorProcedure[] = ${JSON.stringify(Tt,void 0,4)};
1611
1641
 
1612
1642
  /** Read-only RLS metadata (policies + roles discovered from \`.use(rls(...))\` chains) served via \`__lunora_admin__:rlsPolicies\` for the studio's RLS inspector. */
1613
- const LUNORA_RLS_METADATA: RlsPoliciesResult = ${JSON.stringify(Tt,void 0,4)};
1643
+ const LUNORA_RLS_METADATA: RlsPoliciesResult = ${JSON.stringify(Nt,void 0,4)};
1614
1644
 
1615
1645
  /** 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. */
1616
1646
  const LUNORA_MASK_METADATA: MaskPoliciesResult = ${JSON.stringify(At,void 0,4)};
@@ -1620,7 +1650,7 @@ const LUNORA_STORAGE_RULES: StorageRulesResult = ${JSON.stringify(Ot,void 0,4)};
1620
1650
 
1621
1651
  /** 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. */
1622
1652
  const LUNORA_STUDIO_FEATURES: StudioFeaturesResult = ${JSON.stringify(It,void 0,4)};
1623
- ${Ae}${W.constant}${qt}${Ct}${_t}${Se}${$e}${Re}${fe}
1653
+ ${Ae}${W.constant}${jt}${Ct}${Et}${Se}${$e}${Re}${fe}
1624
1654
  export interface ShardDOConfig {
1625
1655
  /** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
1626
1656
  cdc?: boolean;
@@ -1634,9 +1664,9 @@ export interface ShardDOConfig {
1634
1664
  observability?: (env: Record<string, unknown>) => TelemetrySink | undefined;
1635
1665
  /** \`unknown\` because \`@lunora/scheduler\`'s \`Scheduler\` is not assignable to \`SchedulerLike\`; the shard casts it. */
1636
1666
  scheduler?: (env: Record<string, unknown>) => unknown;
1637
- storage?: (env: Record<string, unknown>) => unknown;${Ut}${ue}${V.configField}${H.configField}${J.configField}${ne.configField}${U.configField}${K.configField}${Q.configField}${B.configField}${je}${h}${Kt}${Qt}${Vt}
1667
+ storage?: (env: Record<string, unknown>) => unknown;${Ut}${ue}${V.configField}${H.configField}${J.configField}${ne.configField}${U.configField}${K.configField}${Q.configField}${B.configField}${qe}${h}${Kt}${Qt}${Vt}
1638
1668
  }
1639
- ${O("schedulerStub",Jt,["cancel","runAfter","runAt"])}${O("storageStub",zt,["delete","download","getMetadata","getSignedUrl","getUrl","head","list","upload"],{sync:["getUrl"]})}${Yt}${dn}${Xt}${Ie}${V.stub}${H.stub}${J.stub}${ne.stub}${U.stub}${K.stub}${Q.stub}${B.stub}${ye}${Ne}
1669
+ ${O("schedulerStub",Jt,["cancel","runAfter","runAt"])}${O("storageStub",zt,["delete","download","getMetadata","getSignedUrl","getUrl","head","list","upload"],{sync:["getUrl"]})}${Yt}${dn}${Xt}${Ie}${V.stub}${H.stub}${J.stub}${ne.stub}${U.stub}${K.stub}${Q.stub}${B.stub}${ye}${Te}
1640
1670
  // Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
1641
1671
  // referencing call fails loudly with a clear error instead of overflowing the
1642
1672
  // stack. Tracked across the awaited handler chain (one DO invocation is
@@ -1644,7 +1674,32 @@ ${O("schedulerStub",Jt,["cancel","runAfter","runAt"])}${O("storageStub",zt,["del
1644
1674
  const MAX_RUN_DEPTH = 32;
1645
1675
  let runDepth = 0;
1646
1676
 
1647
- const dispatchRun = async (expected: FunctionKind, functionPath: string, args: Record<string, unknown>, ctx: unknown): Promise<unknown> => {
1677
+ const dispatchRun = async (
1678
+ expected: FunctionKind,
1679
+ functionPath: string,
1680
+ args: Record<string, unknown>,
1681
+ ctx: unknown,
1682
+ // The kind of the context making the call, and — for a mutation target — the
1683
+ // transaction wrapper to run it under. Both are supplied by \`buildCtx\`, which
1684
+ // is the only place that knows which dispatch this \`ctx\` belongs to. Widened
1685
+ // past \`FunctionKind\` because the registry also carries \`"stream"\`, and
1686
+ // \`undefined\` for a ctx built outside a registered function (admin, migration).
1687
+ callerKind?: "stream" | FunctionKind,
1688
+ runTransactional?: (work: () => Promise<unknown>) => Promise<unknown>,
1689
+ ): Promise<unknown> => {
1690
+ // A query is read-only. The ctx object installs \`run*\` on every kind (one
1691
+ // shape, built once), so the TYPE is all that stops a query from writing —
1692
+ // and a cast walks straight past it, inside a subscription re-run that can
1693
+ // execute many times per write. Checked on the CALLER, before the registry
1694
+ // lookup, so \`ctx.runMutation\` from a query fails identically whatever it
1695
+ // aimed at.
1696
+ if (callerKind === "query" && expected !== "query") {
1697
+ throw new LunoraError(
1698
+ "RUN_KIND_FORBIDDEN",
1699
+ \`ctx.run\${expected[0]!.toUpperCase()}\${expected.slice(1)}: a query may only compose other queries (tried to run "\${functionPath}")\`,
1700
+ );
1701
+ }
1702
+
1648
1703
  const registered = LUNORA_FUNCTIONS[functionPath];
1649
1704
 
1650
1705
  if (!registered) {
@@ -1662,6 +1717,18 @@ const dispatchRun = async (expected: FunctionKind, functionPath: string, args: R
1662
1717
  runDepth += 1;
1663
1718
 
1664
1719
  try {
1720
+ // A mutation composed from an action (or an http action) is the shape the
1721
+ // docs prescribe for atomicity — "do the transactional reads and writes in
1722
+ // a mutation and call it from the action". It only holds if the composed
1723
+ // dispatch gets the same BEGIN/COMMIT span and single-writer gate the
1724
+ // top-level mutation path gets; without it every write autocommits and a
1725
+ // mid-handler throw leaves the earlier ones durable. A \`runMutation\` from
1726
+ // inside a mutation rides the enclosing span instead of opening a second
1727
+ // one (see \`runMutationTransaction\`).
1728
+ if (runTransactional) {
1729
+ return await runTransactional(async () => registered.handler(ctx, args));
1730
+ }
1731
+
1665
1732
  return await registered.handler(ctx, args);
1666
1733
  } finally {
1667
1734
  runDepth -= 1;
@@ -1705,51 +1772,114 @@ export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOSt
1705
1772
  // stamp no deps.
1706
1773
  const ctx = this.buildCtx({ functionPath, headroom, scope, trusted: registered.lifecycle === "init" });
1707
1774
 
1708
- // A mutation's writes must commit all-or-nothing: wrap its dispatch in
1709
- // the DO's BEGIN/COMMIT span so any throw (a validator, an RLS denial,
1710
- // an OCC conflict, a failed row mid-\`insertMany\`/\`patchMany\`) rolls
1711
- // back every write the mutation made. Queries are read-only and actions
1712
- // do external I/O that can't be rolled back, so both dispatch directly.
1713
- // \`ctx.run*\` composition runs inside this span (it never re-enters
1714
- // handleRpc); runInTransaction's own guard rejects accidental nesting.
1775
+ // A mutation's writes must commit all-or-nothing, so its dispatch runs
1776
+ // under \`runMutationTransaction\` — the ONE place that opens the span,
1777
+ // settles the deferred schedules and flushes the deferred deletes. The
1778
+ // same helper backs \`ctx.runMutation\` and \`runReactor\`, because a
1779
+ // mutation reached by composition is promised exactly these guarantees
1780
+ // and used to get none of them.
1715
1781
  //
1716
1782
  // The replay bookkeeping (idempotency dedup row + custom-mutator
1717
- // watermark advance) commits INSIDE this span via
1783
+ // watermark advance) commits INSIDE the span via
1718
1784
  // \`commitMutationBookkeeping\`, so the writes, the dedup row, and the
1719
1785
  // watermark are atomic — a crash can't leave the writes durable without
1720
- // the replay guard.
1786
+ // the replay guard. It is deliberately NOT in the shared helper: it is
1787
+ // per-REQUEST bookkeeping, and a composed sub-mutation has no request of
1788
+ // its own to record.
1721
1789
  if (registered.kind === "mutation") {
1722
- const result = await this.runInTransaction(async () => {
1790
+ return await this.runMutationTransaction(ctx, async () => {
1723
1791
  const value = await registered.handler(ctx, args);
1724
1792
 
1725
1793
  this.commitMutationBookkeeping(value);
1726
1794
 
1727
1795
  return value;
1728
1796
  });
1797
+ }
1798
+
1799
+ const result = await registered.handler(ctx, args);
1800
+
1801
+ // An action is not itself transactional, but \`ctx.runMutation\` runs its
1802
+ // submutations on THIS ctx, so anything they queued after their own
1803
+ // transaction settled lands here. A dispatch with nothing queued no-ops.
1804
+ await this.deferPastResponse(flushDeferredDeletes(ctx));
1805
+
1806
+ return result;
1807
+ }
1808
+
1809
+ /**
1810
+ * Run one mutation handler with the atomicity every caller of a mutation is
1811
+ * promised: a BEGIN/COMMIT span (with the single-writer gate
1812
+ * \`ShardRunner.runInTransaction\` composes in front of it), scheduled jobs
1813
+ * held until that span commits, and the deferred object deletes flushed
1814
+ * only once it has.
1815
+ *
1816
+ * Shared by the three entry points that dispatch a mutation handler — the
1817
+ * top-level RPC, \`ctx.runMutation\` from an action or http action, and a
1818
+ * reactor — because the guarantee is a property of the mutation, not of how
1819
+ * it was reached. The docs tell users to put the transactional part in a
1820
+ * mutation and call it from the action precisely so that composition is
1821
+ * atomic.
1822
+ *
1823
+ * A \`ctx.runMutation\` issued from inside a mutation is NESTED: SQLite-in-DO
1824
+ * has no savepoints and \`runInTransaction\` rejects a second open, so it
1825
+ * rides the enclosing span, which also owns its commit and therefore its
1826
+ * scheduler flush.
1827
+ */
1828
+ private async runMutationTransaction<T>(ctx: unknown, work: () => Promise<T>): Promise<T> {
1829
+ const settleSchedules = beginDeferredSchedules(ctx as { scheduler?: unknown });
1729
1830
 
1730
- // The transaction committed, so the rows are durable and the objects
1731
- // their deletion orphaned can go. Deliberately AFTER the span, never
1732
- // inside it: an R2 delete cannot roll back, so a delete issued from
1733
- // within a transaction that later aborts destroys data the surviving
1734
- // row still points at. A throw above skips this entirely and the
1735
- // queue dies with \`ctx\`.
1831
+ if (this.isInTransaction()) {
1832
+ try {
1833
+ const nested = await work();
1834
+
1835
+ await settleSchedules(true);
1836
+
1837
+ return nested;
1838
+ } catch (error) {
1839
+ await settleSchedules(false);
1840
+
1841
+ throw error;
1842
+ }
1843
+ }
1844
+
1845
+ let result: T;
1846
+
1847
+ try {
1848
+ result = await this.runInTransaction(work);
1849
+ } catch (error) {
1850
+ // The span rolled back, so the rows are gone and the jobs that were
1851
+ // to run "after I commit" must go with them. Dropping them is the
1852
+ // whole point of buffering: a persisted job for a write that never
1853
+ // landed fires against state that does not exist.
1854
+ await settleSchedules(false);
1855
+
1856
+ throw error;
1857
+ }
1858
+
1859
+ // Committed. Schedules first and AWAITED: \`runAfter(0, ...)\` is
1860
+ // documented as the deterministic equivalent of an \`afterCommit\` hook,
1861
+ // so the job must be enqueued after the commit, and a failure to enqueue
1862
+ // it must be visible rather than swallowed.
1863
+ //
1864
+ // In a \`finally\`, because both halves of this are post-commit cleanup
1865
+ // and neither is allowed to cancel the other. \`settleSchedules\` drains
1866
+ // its whole queue and then rethrows what failed, so a SchedulerDO that
1867
+ // refused one job used to take the object cleanup down with it — the
1868
+ // rows were durably gone and their objects leaked with nothing logged.
1869
+ try {
1870
+ await settleSchedules(true);
1871
+ } finally {
1872
+ // Then the objects whose rows are now durably gone. Deliberately AFTER
1873
+ // the span, never inside it: an R2 delete cannot roll back, so a delete
1874
+ // issued from within a transaction that later aborts destroys data the
1875
+ // surviving row still points at.
1736
1876
  //
1737
1877
  // \`flushDeferredDeletes\` never rejects — the mutation has already
1738
1878
  // succeeded, so a failed cleanup must not turn into a failed response.
1739
1879
  // A leaked object is reported through \`ctx.log\` instead, with its key.
1740
1880
  await this.deferPastResponse(flushDeferredDeletes(ctx));
1741
-
1742
- return result;
1743
1881
  }
1744
1882
 
1745
- const result = await registered.handler(ctx, args);
1746
-
1747
- // An action is not itself transactional, but \`ctx.runMutation\` runs its
1748
- // submutations on THIS ctx, so their queued deletes land here — this is
1749
- // the first point at which those writes are known to have committed. A
1750
- // dispatch with nothing queued no-ops.
1751
- await this.deferPastResponse(flushDeferredDeletes(ctx));
1752
-
1753
1883
  return result;
1754
1884
  }
1755
1885
 
@@ -1783,7 +1913,7 @@ ${at.override}
1783
1913
  return { ranges: footprint.ranges(), result, tables: footprint.tables };
1784
1914
  }
1785
1915
 
1786
- protected override executeStream(functionPath: string, args: Record<string, unknown>): null | { durable?: { ttlMs?: number }; iterator: (signal: AbortSignal) => AsyncIterable<unknown> } {
1916
+ protected override executeStream(functionPath: string, args: Record<string, unknown>, identity?: { identity?: Record<string, unknown>; userId?: string }): null | { durable?: { ttlMs?: number }; iterator: (signal: AbortSignal) => AsyncIterable<unknown> } {
1787
1917
  const registered = LUNORA_FUNCTIONS[functionPath];
1788
1918
 
1789
1919
  if (!registered || registered.kind !== "stream" || registered.visibility === "internal") {
@@ -1794,10 +1924,15 @@ ${at.override}
1794
1924
 
1795
1925
  return {
1796
1926
  ...(registered.durable ? { durable: registered.durable as { ttlMs?: number } } : {}),
1797
- iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath }), args, signal),
1927
+ // Identity threaded EXPLICITLY from the socket, exactly as
1928
+ // \`executeSubscription\` above: the iterator is pulled after this
1929
+ // frame returned, interleaved with unrelated dispatches, so
1930
+ // \`buildCtx\`'s per-request fallback would run an \`rls()\` /
1931
+ // \`ctx.auth\` stream as nobody — or as a concurrent RPC's caller.
1932
+ iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath, identity }), args, signal),
1798
1933
  };
1799
1934
  }
1800
- ${jt}${Bt}${cn}${pn}
1935
+ ${qt}${Bt}${cn}${pn}
1801
1936
  protected override lifecycleHookPaths(event: "connect" | "disconnect" | "init" | "reactor"): readonly string[] {
1802
1937
  return LUNORA_LIFECYCLE_HOOKS[event];
1803
1938
  }
@@ -1808,10 +1943,12 @@ ${un}
1808
1943
  // the new baseline and the footprint as the "should I even re-run this"
1809
1944
  // gate, instead of pushing a frame down a socket.
1810
1945
  //
1811
- // Wrapped in \`runInTransaction\` because a reactor handler is a mutation
1812
- // and its writes must commit all-or-nothing. Safe to open here: the refresh
1813
- // drain runs OUTSIDE any dispatch transaction (it is post-flush background
1814
- // work), so this never nests.
1946
+ // Dispatched through \`runMutationTransaction\` because a reactor handler is
1947
+ // a mutation: its writes must commit all-or-nothing, the jobs it schedules
1948
+ // must wait for that commit, and the objects its row deletes orphaned must
1949
+ // be flushed once it lands. Safe to open a span here: the refresh drain
1950
+ // runs OUTSIDE any dispatch transaction (it is post-flush background work),
1951
+ // so this never nests.
1815
1952
  //
1816
1953
  // NO identity is threaded, and the ctx is built \`trusted\`. A reactor fires
1817
1954
  // because data moved, not because anyone asked, so there is no user for RLS
@@ -1830,18 +1967,13 @@ ${un}
1830
1967
 
1831
1968
  const footprint = createReadFootprint();
1832
1969
  const ctx = this.buildCtx({ functionPath, headroom: this.subscriptionHeadroom(), onRead: footprint.onRead, onReadRange: footprint.onReadRange, trusted: true });
1833
- const outcome = (await this.runInTransaction(async () => registered.handler(ctx, { previousDigest } as unknown as Record<string, unknown>))) as {
1970
+ const outcome = (await this.runMutationTransaction(ctx, async () =>
1971
+ registered.handler(ctx, { previousDigest } as unknown as Record<string, unknown>),
1972
+ )) as {
1834
1973
  digest: string;
1835
1974
  ran: boolean;
1836
1975
  };
1837
1976
 
1838
- // A reactor IS a mutation, so its ctx carries the deferred-delete queue
1839
- // and its transaction has just committed. Without this the queue would
1840
- // die with \`ctx\` and the objects would leak with nothing to find: no
1841
- // error, no warning, no failed request — a reactor that reaps orphaned
1842
- // rows is a textbook use of one.
1843
- await this.deferPastResponse(flushDeferredDeletes(ctx));
1844
-
1845
1977
  return { digest: outcome.digest, ran: outcome.ran, tables: [...footprint.tables] };
1846
1978
  }
1847
1979
 
@@ -1883,7 +2015,7 @@ ${un}
1883
2015
  protected override studioFeatures(): StudioFeaturesResult {
1884
2016
  return LUNORA_STUDIO_FEATURES;
1885
2017
  }
1886
- ${W.evaluateOverride}${W.subscriptionOverride}${Lt}${Et}
2018
+ ${W.evaluateOverride}${W.subscriptionOverride}${Lt}${_t}
1887
2019
  protected override advisories(): AdvisoryFinding[] {
1888
2020
  return LUNORA_ADVISORIES;
1889
2021
  }
@@ -2072,7 +2204,7 @@ ${Y()}
2072
2204
  return;
2073
2205
  }
2074
2206
 
2075
- runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false${M} });
2207
+ runShardMigrations(this.sql as SqlExec, schema as unknown as SchemaLike, { cdc: config.cdc ?? false${F} });
2076
2208
  this.migrated = true;
2077
2209
  }
2078
2210
  ${Ke?`
@@ -2094,12 +2226,38 @@ ${Ke?`
2094
2226
  // dispatch path) fall back to the per-request fields as before.
2095
2227
  const userId = options.identity ? options.identity.userId : this.getCurrentUserId();
2096
2228
  const identity = options.identity ? options.identity.identity : this.getCurrentIdentity();
2097
- ${nn}${de}${fn}${he}${me}${Ce}${Fe}
2229
+ ${nn}${de}${fn}${he}${me}${Ce}${Me}
2230
+ // Which dispatch this ctx belongs to. Drives the two deferral facades
2231
+ // below and the \`ctx.run*\` caller guard; a ctx built for an
2232
+ // admin/lifecycle path has no registered function, and so no kind.
2233
+ const contextKind = LUNORA_FUNCTIONS[options.functionPath ?? ""]?.kind;
2098
2234
  // \`list\`/\`get\` are the two methods \`ctx.db.system.query("_scheduled_functions")\`
2099
2235
  // reaches through, and pending jobs live in the SchedulerDO — nothing the
2100
2236
  // CDC changelog records — so reading them must forfeit a delta resume.
2101
2237
  // The scheduler's own \`runAfter\`/\`runAt\`/\`cancel\` are writes and stay unstamped.
2102
- const scheduler = markUnvouchableReads((config.scheduler?.(env) ?? schedulerStub) as SchedulerLike, options.onRead, ["get", "list"]);
2238
+ const schedulerBase = markUnvouchableReads((config.scheduler?.(env) ?? schedulerStub) as SchedulerLike, options.onRead, ["get", "list"]);
2239
+ // \`ctx.scheduler.runAfter(0, ...)\` is documented as the deterministic
2240
+ // equivalent of an \`afterCommit\` hook, and the SchedulerDO persists the
2241
+ // job the moment it is called — so inside a mutation the call is BUFFERED
2242
+ // and replayed after the transaction commits (a rollback drops it).
2243
+ // Wrapped on the same dispatches as the deferred-delete queue and for the
2244
+ // same reason: \`ctx.runMutation\` hands the CALLER's ctx to the callee, so
2245
+ // a mutation reached from an action schedules through the action's ctx.
2246
+ // An action's own schedules stay immediate — the window is only open
2247
+ // while a transaction is (see \`runMutationTransaction\`).
2248
+ //
2249
+ // Every kind but \`query\` is wrapped, because \`runMutationTransaction\`
2250
+ // is installed on \`ctx.runMutation\` for every kind but \`query\`: a
2251
+ // STREAM ctx, and an admin/lifecycle ctx with no registered kind at all,
2252
+ // both get the BEGIN/COMMIT span for a mutation they compose, and used
2253
+ // to get it with the deferral missing — so that mutation's job reached
2254
+ // the SchedulerDO while its transaction was still open, and survived the
2255
+ // rollback. A query cannot host a mutation handler (\`dispatchRun\`
2256
+ // refuses it) and its ctx is built on the hot subscription path, so it
2257
+ // stays unwrapped.
2258
+ //
2259
+ // Wrapped OUTSIDE the read-stamping facade so \`get\`/\`list\` stay stamped.
2260
+ const scheduler = contextKind === "query" ? schedulerBase : withDeferredSchedules(schedulerBase);
2103
2261
  // Build the storage adapter once and share it between \`ctx.storage\`
2104
2262
  // and \`ctx.db.system._storage\` so both read the same R2 binding. The
2105
2263
  // \`storageStub\` fallback satisfies SystemReaderStorageLike structurally
@@ -2134,7 +2292,6 @@ ${nn}${de}${fn}${he}${me}${Ce}${Fe}
2134
2292
  // resolves through it and stays stamped, and applied only to
2135
2293
  // \`ctx.storage\` so \`ctx.db.system._storage\` (which shares the adapter
2136
2294
  // above) is untouched.
2137
- const contextKind = LUNORA_FUNCTIONS[options.functionPath ?? ""]?.kind;
2138
2295
  const contextStorage = contextKind === "mutation" || contextKind === "action" ? withDeferredDeletes(storage) : storage;
2139
2296
  // \`ctx.log\`: the DO base builds the attributed logger (structured
2140
2297
  // fields + \`.with(...)\` child + trace correlation) and routes each call
@@ -2197,11 +2354,15 @@ ${yn}
2197
2354
  scheduler,
2198
2355
  span,
2199
2356
  storage: contextStorage,
2200
- trace,${an}${Oe}${bn}${qe}${_e}${De}${Le}${Me}
2357
+ trace,${an}${Oe}${bn}${je}${Ee}${De}${Le}${Fe}
2201
2358
  };
2202
2359
  ${kn}${wn}
2203
- ctx.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, ctx);
2204
- ctx.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("mutation", reference.__lunoraRef, fnArgs, ctx);
2360
+ ctx.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, ctx, contextKind);
2361
+ // The composed mutation runs under the SAME wrapper the top-level RPC
2362
+ // uses, so "do the transactional work in a mutation and call it from the
2363
+ // action" — the recipe the docs give for atomicity — actually is atomic.
2364
+ ctx.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) =>
2365
+ dispatchRun("mutation", reference.__lunoraRef, fnArgs, ctx, contextKind, async (work) => this.runMutationTransaction(ctx, work));
2205
2366
  // \`ctx.runQuery(ref, args, { untracked: true })\` runs the sub-query on
2206
2367
  // its OWN context, built without the subscription's read-footprint
2207
2368
  // hooks — so its reads never enter this subscription's footprint and a
@@ -2222,14 +2383,15 @@ ${kn}${wn}
2222
2383
  runOptions?.untracked === true
2223
2384
  ? this.buildCtx({ functionPath: options.functionPath, headroom: options.headroom, identity: { identity, userId }, scope: options.scope })
2224
2385
  : ctx,
2386
+ contextKind,
2225
2387
  );
2226
2388
 
2227
2389
  return ctx;
2228
2390
  }
2229
2391
  };
2230
- `},Xe=e=>{switch(e.kind){case"array":case"object":case"record":case"union":return{builder:"text",mode:"json",notNull:!0,typeAnnotation:A(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:A(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?Xe(e.inner):{builder:"text",notNull:!1},notNull:!1};case"storage":case"string":return{builder:"text",notNull:!0};default:return{builder:"text",notNull:!0}}},$t=(e,t)=>{const n=se(e);return n.kind==="id"&&n.tableName!==void 0&&t.has(n.tableName)?n.tableName:void 0},jr=(e,t,n)=>{b(e,"drizzle column name");const r=Xe(t),o=r.mode?`, { mode: "${r.mode}" }`:"";let s=`${r.builder}("${e}"${o})`;r.typeAnnotation&&(s+=`.$type<${r.typeAnnotation}>()`);const i=$t(t,n);return i!==void 0&&(s+=`.references((): AnySQLiteColumn => ${i}._id)`),r.notNull&&(s+=".notNull()"),s},qr=e=>{const t=e.unique?"uniqueIndex":"index",n=e.fields.map(r=>(b(r,"drizzle index field"),`t.${r}`)).join(", ");return` ${X(e.name)}: ${t}(${JSON.stringify(e.name)}).on(${n}),`},Wr=(e,t)=>{b(e.name,"table name");const n=[' _id: text("_id").primaryKey(),',' _creationTime: integer("_creationTime").notNull(),',...Object.entries(e.shape).map(([o,s])=>` ${o}: ${jr(o,s,t)},`)].join(`
2392
+ `},Xe=e=>{switch(e.kind){case"array":case"object":case"record":case"union":return{builder:"text",mode:"json",notNull:!0,typeAnnotation:A(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:A(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?Xe(e.inner):{builder:"text",notNull:!1},notNull:!1};case"storage":case"string":return{builder:"text",notNull:!0};default:return{builder:"text",notNull:!0}}},$t=(e,t)=>{const n=se(e);return n.kind==="id"&&n.tableName!==void 0&&t.has(n.tableName)?n.tableName:void 0},qr=(e,t,n)=>{b(e,"drizzle column name");const r=Xe(t),o=r.mode?`, { mode: "${r.mode}" }`:"";let s=`${r.builder}("${e}"${o})`;r.typeAnnotation&&(s+=`.$type<${r.typeAnnotation}>()`);const i=$t(t,n);return i!==void 0&&(s+=`.references((): AnySQLiteColumn => ${i}._id)`),r.notNull&&(s+=".notNull()"),s},jr=e=>{const t=e.unique?"uniqueIndex":"index",n=e.fields.map(r=>(b(r,"drizzle index field"),`t.${r}`)).join(", ");return` ${X(e.name)}: ${t}(${JSON.stringify(e.name)}).on(${n}),`},Wr=(e,t)=>{b(e.name,"table name");const n=[' _id: text("_id").primaryKey(),',' _creationTime: integer("_creationTime").notNull(),',...Object.entries(e.shape).map(([o,s])=>` ${o}: ${qr(o,s,t)},`)].join(`
2231
2393
  `),r=e.indexes.length>0?`, (t) => ({
2232
- ${e.indexes.map(o=>qr(o)).join(`
2394
+ ${e.indexes.map(o=>jr(o)).join(`
2233
2395
  `)}
2234
2396
  })`:"";return`export const ${e.name} = sqliteTable("${e.name}", {
2235
2397
  ${n}
@@ -2241,7 +2403,7 @@ ${n}
2241
2403
  `:""].join("");return`${S}import { ${c.join(", ")} } from "${n.serverDrizzle}";
2242
2404
  ${p}
2243
2405
  ${d}
2244
- `},ho=(e,t=!1)=>{const n=e.tables.filter(o=>o.shardMode==="global"),r=e.tables.filter(o=>o.shardMode!=="global");return{global:_(ct(n,t),Je),shard:_(ct(r,t),Je)}},mo=e=>{const t=new Map;for(const i of e){const a=t.get(i.cron);a?a.push(i):t.set(i.cron,[i])}const n=[...t.keys()].map(i=>` ${JSON.stringify(i)},`).join(`
2406
+ `},ho=(e,t=!1)=>{const n=e.tables.filter(o=>o.shardMode==="global"),r=e.tables.filter(o=>o.shardMode!=="global");return{global:E(ct(n,t),Je),shard:E(ct(r,t),Je)}},mo=e=>{const t=new Map;for(const i of e){const a=t.get(i.cron);a?a.push(i):t.set(i.cron,[i])}const n=[...t.keys()].map(i=>` ${JSON.stringify(i)},`).join(`
2245
2407
  `),r=n.length>0?`
2246
2408
  ${n}
2247
2409
  `:"",o=[...t.entries()].map(([i,a])=>{const c=a.map(d=>{const p=d.workflow?`workflow: ${JSON.stringify(d.workflow.binding)}`:`functionPath: ${JSON.stringify(d.functionPath)}`;return` { name: ${JSON.stringify(d.name)}, ${p}, args: ${JSON.stringify(d.args)} },`}).join(`