@lunora/codegen 1.0.0-alpha.118 → 1.0.0-alpha.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +15 -22
- package/dist/index.d.ts +15 -22
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{GENERATED_HEADER-BIP8JE2o.mjs → GENERATED_HEADER-Cx5Qh2XJ.mjs} +1 -1
- package/dist/packem_shared/{MUTATORS_FILENAME-sGAt-yC1.mjs → MUTATORS_FILENAME-B2-W8Bm3.mjs} +1 -1
- package/dist/packem_shared/{NOTIFY_FILENAME-CTZI61e3.mjs → NOTIFY_FILENAME-IJXY4yHt.mjs} +1 -1
- package/dist/packem_shared/{OPENRPC_VERSION-CK8lskQm.mjs → OPENRPC_VERSION-14yZ-O3V.mjs} +1 -1
- package/dist/packem_shared/{SCHEMA_SNAPSHOT_FILENAME-KYdkEFZu.mjs → SCHEMA_SNAPSHOT_FILENAME-CbOqo05-.mjs} +1 -1
- package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-B0cTpK1D.mjs +2 -0
- package/dist/packem_shared/{SchemaSnapshotParseError-Dc1aZAph.mjs → SchemaSnapshotParseError-BoHDfCO3.mjs} +1 -1
- package/dist/packem_shared/{buildOpenApiDocument-xvbIL1q6.mjs → buildOpenApiDocument-DI8bgteQ.mjs} +1 -1
- package/dist/packem_shared/discoverFunctions-006teuNG.mjs +1 -0
- package/dist/packem_shared/{discoverHttpRoutes-DOirwCWw.mjs → discoverHttpRoutes-BxF4FRZL.mjs} +1 -1
- package/dist/packem_shared/{discoverMaskProcedures-D-09Qb8S.mjs → discoverMaskProcedures-BCl3fNrQ.mjs} +1 -1
- package/dist/packem_shared/{discoverNondeterministicCalls-DhNToI6C.mjs → discoverNondeterministicCalls-BA91Z-vF.mjs} +1 -1
- package/dist/packem_shared/{discoverR2sqlCalls-B6e4j4rD.mjs → discoverR2sqlCalls-M2ob41UX.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsMetadata-Pw0_c1aS.mjs → discoverRlsMetadata-Dwt2p8IK.mjs} +1 -1
- package/dist/packem_shared/{discoverSchema-D_wHstA-.mjs → discoverSchema-gJ93Xjpx.mjs} +1 -1
- package/dist/packem_shared/{discoverStorageRulesMetadata-DthHu3mC.mjs → discoverStorageRulesMetadata-69Y8wZb2.mjs} +1 -1
- package/dist/packem_shared/{emit-C935cHCe.mjs → emit-C0azW4mT.mjs} +307 -218
- package/dist/packem_shared/{emitApp-BpHICt5i.mjs → emitApp-Dyr-onNf.mjs} +1 -1
- package/dist/packem_shared/{formatAdvisories-BjTV22uR.mjs → formatAdvisories-CkEu1eye.mjs} +1 -1
- package/package.json +6 -6
- package/dist/packem_shared/SCHEMA_SNAPSHOT_VERSION-DIqnj_32.mjs +0 -2
- package/dist/packem_shared/discoverFunctions-4eSpCR6m.mjs +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{agentComponent as
|
|
1
|
+
import{agentComponent as at}from"@lunora/agent/component";import{LunoraError as te}from"@lunora/errors";import{serializeSchemaSnapshot as fn,hashSchemaSnapshot as yn}from"./SCHEMA_SNAPSHOT_VERSION-B0cTpK1D.mjs";import{s as N}from"./paths-C-9fwEgQ.mjs";const st=[{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,59 +15,60 @@ import{agentComponent as ot}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"}],
|
|
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"}],bn=st,kn=new Map(bn.flatMap(e=>e.serverCtxField?[[e.key,e.serverCtxField]]:[])),Vr=st.flatMap(e=>"appMethod"in e?[{appMethod:e.appMethod,key:e.key}]:[]),it=/^(?:"(?:[^"\\]|\\(?:["\\/bfnrt]|u[0-9A-Fa-f]{4}))*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u,wn=new Set(["any","bigint","boolean","date","id","null","number","storage","string","timestamp"]),lt=e=>e.column!==void 0,xn=(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
|
-
`}},
|
|
24
|
-
`}},We=(e,t,n)=>{if(!
|
|
23
|
+
`}},vn=(e,t)=>{const n=e.literalValue?.trim();if(!(n===void 0||!it.test(n)))return{out:t,pre:`if (${t} !== ${n}) return DEFER;
|
|
24
|
+
`}},We=(e,t,n)=>{if(!lt(e)&&!(e.hasRefinement||e.sourceText!==void 0)){if(wn.has(e.kind))return e.kind==="any"?{out:t,pre:""}:{out:t,pre:xn(e.kind,t)};switch(e.kind){case"array":return $n(e,t,n);case"literal":return vn(e,t);case"object":return Nn(e,t,n);default:return}}},$n=(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=We(r,a,n);if(!c)return;const d=`if (!Array.isArray(${t})) return DEFER;
|
|
25
25
|
const ${s} = new Array(${t}.length);
|
|
26
26
|
for (let ${i} = 0; ${i} < ${t}.length; ${i}++) {
|
|
27
27
|
const ${a} = ${t}[${i}];
|
|
28
28
|
${c.pre}${s}[${i}] = ${c.out};
|
|
29
29
|
}
|
|
30
|
-
`;return{out:s,pre:d}},
|
|
30
|
+
`;return{out:s,pre:d}},Rn=(e,t,n,r)=>{const o=JSON.stringify(e);if(t.kind==="optional"){if(t.hasRefinement||t.sourceText!==void 0||lt(t))return;const{inner:i}=t;if(!i)return;const a=We(i,n,r);if(!a)return;const c=r.next(),d=`__has${String(c)}`,p=`__val${String(c)}`,h=`let ${d} = false;
|
|
31
31
|
let ${p};
|
|
32
32
|
if (${n} !== undefined) {
|
|
33
33
|
${a.pre}${p} = ${a.out};
|
|
34
34
|
${d} = true;
|
|
35
35
|
}
|
|
36
|
-
`;return{entry:`...(${d} ? { ${o}: ${p} } : {})`,pre:h}}const s=We(t,n,r);if(s)return{entry:`${o}: ${s.out}`,pre:s.pre}}
|
|
37
|
-
`,c=p}const d=
|
|
38
|
-
`},
|
|
39
|
-
${
|
|
40
|
-
`;return{out:i,pre:a}},
|
|
36
|
+
`;return{entry:`...(${d} ? { ${o}: ${p} } : {})`,pre:h}}const s=We(t,n,r);if(s)return{entry:`${o}: ${s.out}`,pre:s.pre}},Sn=new Set(Object.getOwnPropertyNames(Object.prototype)),ct=(e,t,n)=>{let r="";const o=[];for(const s of Object.keys(e)){const i=e[s];if(!i)return;const a=JSON.stringify(s);let c=`${t}[${a}]`;if(Sn.has(s)){const p=`__f${String(n.next())}`;r+=`const ${p} = Object.hasOwn(${t}, ${a}) ? ${t}[${a}] : undefined;
|
|
37
|
+
`,c=p}const d=Rn(s,i,c,n);if(!d)return;r+=d.pre,o.push(d.entry)}return{entries:o.join(", "),pre:r}},dt=e=>{const t=`Object.getPrototypeOf(${e})`;return`if (${t} !== Object.prototype && ${t} !== null) return DEFER;
|
|
38
|
+
`},Nn=(e,t,n)=>{const r=e.shape??{},o=ct(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;
|
|
39
|
+
${dt(t)}${o.pre}const ${i} = { ${o.entries} };
|
|
40
|
+
`;return{out:i,pre:a}},Tn=e=>{let t=0;const r=ct(e,"source",{next:()=>(t+=1,t)});if(r)return`(source) => {
|
|
41
41
|
if (typeof source !== "object" || source === null || Array.isArray(source)) return DEFER;
|
|
42
|
-
${
|
|
43
|
-
}`},
|
|
42
|
+
${dt("source")}${r.pre}return { ${r.entries} };
|
|
43
|
+
}`},S=`// GENERATED by @lunora/codegen — do not edit.
|
|
44
44
|
// Run \`lunora codegen\` to regenerate.
|
|
45
45
|
|
|
46
|
-
`,ne=(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"},
|
|
46
|
+
`,ne=(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"},ut=/^[A-Za-z_$][\w$]*$/u,An=/^[\w./-]+$/u,g=(e,t)=>{if(!ut.test(e))throw new te("INTERNAL",`@lunora/codegen: ${t} is not a valid JS identifier: ${JSON.stringify(e)}`)},qe=e=>e.charAt(0).toUpperCase()+e.slice(1),E=e=>ut.test(e)?e:JSON.stringify(e),On={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"},In=e=>{if(e===void 0)return"unknown";if(!it.test(e))throw new te("INTERNAL",`@lunora/codegen: v.literal() argument is not a parseable string/number/boolean/null literal: ${JSON.stringify(e)}`);return e},A=e=>{const t=On[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 g(n,"v.id() table name"),`Id<"${n}">`}case"literal":return In(e.literalValue);case"object":return e.shape?`{ ${Object.entries(e.shape).map(([r,o])=>pt(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"}},pt=(e,t)=>{const n=E(e);if(t.kind==="optional"){const r=t.inner?A(t.inner):"unknown";return`${n}?: ${r}`}return`${n}: ${A(t)}`},ke=e=>{const t=Object.entries(e);return t.length===0?"{}":`{ ${t.map(([n,r])=>pt(n,r)).join("; ")} }`},mt=e=>e.kind==="optional"||!!e.column?.hasDefault,re=e=>e.kind==="optional"&&e.inner?e.inner:e,Cn=e=>{g(e.name,"table name");const t=Object.entries(e.shape).map(([r,o])=>{const s=re(o),i=A(s),a=E(r);return mt(o)?` ${a}?: ${i};`:` ${a}: ${i};`}).join(`
|
|
47
47
|
`),n=t?`
|
|
48
48
|
${t}
|
|
49
49
|
`:"";return`export interface Insert_${e.name} {
|
|
50
50
|
_id?: Id<"${e.name}">;
|
|
51
|
-
_creationTime?: number;${n}}`},Ue="schema",
|
|
51
|
+
_creationTime?: number;${n}}`},Ue="schema",Hr=e=>{for(const u of e.tables)g(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])=>{const I=E($);if(x.kind==="optional"){const T=x.inner?A(x.inner):"unknown";return` ${I}?: ${T};`}return` ${I}: ${A(x)};`}).join(`
|
|
52
52
|
`),f=l?`
|
|
53
53
|
${l}
|
|
54
|
-
`:""
|
|
54
|
+
`:"",w=u.commitOrdered===!0?`
|
|
55
|
+
_commitSeq: number;`:"";return`export interface Doc_${u.name} {
|
|
55
56
|
_id: Id<"${u.name}">;
|
|
56
|
-
_creationTime: number;${f}}`}).join(`
|
|
57
|
+
_creationTime: number;${w}${f}}`}).join(`
|
|
57
58
|
|
|
58
59
|
`),o=e.tables.map(u=>` ${u.name}: Doc_${u.name};`).join(`
|
|
59
60
|
`),s=e.tables.map(u=>{const l=u.indexes.map(f=>JSON.stringify(f.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
|
|
60
61
|
`),i=e.tables.map(u=>{const l=u.searchIndexes.map(f=>JSON.stringify(f.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
|
|
61
62
|
`),a=e.tables.map(u=>{const l=u.rankIndexes.map(f=>JSON.stringify(f.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
|
|
62
63
|
`),c=e.tables.map(u=>{const l=u.shardMode==="global"?"":(u.geoIndexes??[]).map(f=>JSON.stringify(f.name)).join(" | ");return` ${u.name}: ${l||"never"};`}).join(`
|
|
63
|
-
`),d=e.vectorIndexes.map(u=>JSON.stringify(u.name)).join(" | ")||"never",p=e.tables.map(u=>
|
|
64
|
+
`),d=e.vectorIndexes.map(u=>JSON.stringify(u.name)).join(" | ")||"never",p=e.tables.map(u=>Cn(u)).join(`
|
|
64
65
|
|
|
65
66
|
`),h=e.tables.map(u=>` ${u.name}: Insert_${u.name};`).join(`
|
|
66
|
-
`),
|
|
67
|
+
`),y=e.tables.map(u=>{const l=u.relations.map(f=>(g(f.table,"relation target table"),` ${E(f.name)}: ${f.kind==="one"?"OneRelation":"ManyRelation"}<"${f.table}">;`)).join(`
|
|
67
68
|
`);return l?` ${u.name}: {
|
|
68
69
|
${l}
|
|
69
70
|
};`:` ${u.name}: {};`}).join(`
|
|
70
|
-
`);return
|
|
71
|
+
`);return D(`${S}export type TableName = ${t};
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
74
|
* The tables **this app declared** — every {@link TableName} except those an add-on
|
|
@@ -156,14 +157,14 @@ export interface ManyRelation<Target extends keyof DataModel> {
|
|
|
156
157
|
|
|
157
158
|
/** Per-table relation map keyed by accessor name. \`{}\` for tables with none. */
|
|
158
159
|
export interface Relations {
|
|
159
|
-
${
|
|
160
|
+
${y}
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
`,Ue)},
|
|
163
|
-
`);return` ${
|
|
163
|
+
`,Ue)},Ln=/import\("(?<spec>(?:\.\.?\/)*_generated\/[^"]+)"\)/gu,ht=/^(?:\.\.?\/)*_generated\//u,gt=/\.[A-Za-z]\w*$/u,_n=e=>e.replaceAll(Ln,(t,n)=>{const r=n.replace(ht,"./");return`import("${gt.test(r)?r:`${r}.js`}")`}),En=/import\("(?<spec>\.\.?\/[^"]+)"\)/gu,Dn=/^(?:\.\.?\/)+/u,Fn=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("/")},Pn=(e,t)=>e.replaceAll(En,(n,r)=>{if(ht.test(r.replace(Dn,"")))return n;const o=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",i=`../${Fn(`${o}/${r}`)}`;return`import("${gt.test(i)?i:`${i}.js`}")`}),D=(e,t)=>_n(Pn(e,t)),Mn=["client","do","errors","flags","observability","platform","ratelimit","runtime","server","values"],Bn=new RegExp(String.raw`import\("@lunora/(?<pkg>${Mn.join("|")})(?<subpath>/[^"]*)?"\)`,"gu"),jn={flags:new Set(["/web"]),platform:new Set},ft=(e,t)=>t?e.replaceAll(Bn,(n,r,o)=>{const s=jn[r];return o&&s&&!s.has(o)?n:`import("lunorash/${r}${o??""}")`}):e,Ke=e=>["Doc","Id"].filter(t=>new RegExp(String.raw`\b${t}<`,"u").test(e)),qn=e=>e.output===void 0||e.output.kind==="any"?e.returnType:A(e.output),Qe=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))},rt=e=>{const t=([n,r])=>{const o=r.toSorted((s,i)=>s.exportName.localeCompare(i.exportName)).map(s=>{const i=D(ke(s.args),s.filePath),a=D(qn(s),s.filePath);return` ${s.exportName}: FunctionReference<"${s.kind}", ${i}, ${a}>;`}).join(`
|
|
164
|
+
`);return` ${E(N(n))}: {
|
|
164
165
|
${o}
|
|
165
166
|
};`};return Qe(e).map(n=>t(n)).join(`
|
|
166
|
-
`)},
|
|
167
|
+
`)},Wn=(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+=`
|
|
167
168
|
/** A typed reference to a durable workflow, addressable for \`cronJobs()\` targets. Mirrors \`@lunora/scheduler\`'s \`WorkflowReference\` structurally. */
|
|
168
169
|
export interface WorkflowReference<Params = Record<string, unknown>> {
|
|
169
170
|
readonly isLunoraWorkflow: true;
|
|
@@ -195,7 +196,7 @@ export const agents: AgentsRef = {
|
|
|
195
196
|
${i}
|
|
196
197
|
};
|
|
197
198
|
`,n.push(`import type { AgentRunInput } from "@lunora/agent";
|
|
198
|
-
`)}return{block:r,importLine:n.join("")}},
|
|
199
|
+
`)}return{block:r,importLine:n.join("")}},Un=()=>Object.keys(at().functions).toSorted((e,t)=>e.localeCompare(t)),yt=e=>new Set(e.filter(t=>N(t.filePath)==="agents").map(t=>t.exportName)),Kn=()=>new Set(Object.entries(at().functions).filter(([,e])=>e.visibility==="internal").map(([e])=>e)),Qn=(e,t)=>{const n={importLine:"",lines:"",prelude:""};if(e.length===0)return n;const r=yt(t),o=Kn(),s=t.find(a=>N(a.filePath)==="agents"&&o.has(a.exportName));if(s)throw new te("INTERNAL",`@lunora/codegen: "agents:${s.exportName}" is reserved for the durable agent loop's internal dispatch — rename the "${s.exportName}" export in ${s.filePath}`);const i=Un().filter(a=>!r.has(a));return i.length===0?n:{importLine:`import { agentComponent } from "@lunora/agent/component";
|
|
199
200
|
`,lines:i.map(a=>` "agents:${a}": lunoraAgentRuntimeFunctions.${a} as unknown as RegisteredLunoraFunction,`).join(`
|
|
200
201
|
`),prelude:`
|
|
201
202
|
/**
|
|
@@ -204,13 +205,13 @@ ${i}
|
|
|
204
205
|
* subscribe to the public queries (\`agents:agentMessages\` is the live thread view).
|
|
205
206
|
*/
|
|
206
207
|
const lunoraAgentRuntimeFunctions = agentComponent().functions;
|
|
207
|
-
`}},
|
|
208
|
-
`,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"}},
|
|
209
|
-
`);return` ${
|
|
208
|
+
`}},Vn=(e,t)=>{if(!e)return{importLine:"",lines:"",prelude:""};const n=t.find(r=>N(r.filePath)==="sandbox"&&r.exportName==="invoke");if(n)throw new te("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";
|
|
209
|
+
`,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"}},Hn=(e,t)=>{if(e.length===0)return[];const n=yt(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))},Jn=(e,t)=>{if(e.length===0)return[];const n=new Set(t.filter(r=>N(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}))},Gn=e=>{const t=e.filter(i=>i.stream);if(t.length===0)return{block:"",body:""};const n=Qe(t),r=n.map(([i,a])=>{const c=a.map(d=>{const p=D(d.chunkType??"unknown",d.filePath),h=D(ke(d.searchParams),d.filePath),y=D(ke(d.params),d.filePath);return` ${E(d.exportName)}: HttpStreamRef<${p}, ${h}, ${y}>;`}).join(`
|
|
210
|
+
`);return` ${E(N(i))}: {
|
|
210
211
|
${c}
|
|
211
212
|
};`}).join(`
|
|
212
|
-
`),o=n.map(([i,a])=>{const c=a.map(d=>` ${
|
|
213
|
-
`);return` ${
|
|
213
|
+
`),o=n.map(([i,a])=>{const c=a.map(d=>` ${E(d.exportName)}: { method: ${JSON.stringify(d.method)}, path: ${JSON.stringify(d.path)} },`).join(`
|
|
214
|
+
`);return` ${E(N(i))}: {
|
|
214
215
|
${c}
|
|
215
216
|
},`}).join(`
|
|
216
217
|
`);return{block:`
|
|
@@ -222,26 +223,26 @@ ${r}
|
|
|
222
223
|
export const httpStreams: HttpStreamsRef = {
|
|
223
224
|
${o}
|
|
224
225
|
};
|
|
225
|
-
`,body:r}},
|
|
226
|
+
`,body:r}},Jr=e=>{const{agents:t=[],functions:n,httpRoutes:r=[],mutators:o=[],useUmbrella:s=!1,workflows:i=[]}=e,a=ne(s),c=[...n.filter(v=>v.visibility!=="internal"),...Hn(t,n),...Jn(o,n)],d=n.filter(v=>v.visibility==="internal"),p=rt(c),h=rt(d),y=Gn(r),u=`${p}
|
|
226
227
|
${h}
|
|
227
|
-
${
|
|
228
|
+
${y.body}`,l=Ke(u),f=l.length>0?`
|
|
228
229
|
import type { ${l.join(", ")} } from "./dataModel.js";
|
|
229
|
-
`:"",
|
|
230
|
+
`:"",w=p?`
|
|
230
231
|
${p}
|
|
231
|
-
`:""
|
|
232
|
+
`:"",$=h?`
|
|
232
233
|
${h}
|
|
233
|
-
`:"",
|
|
234
|
+
`:"",x=Wn(i,t),I=`export interface ApiTypes {${w}}
|
|
234
235
|
|
|
235
236
|
export const api = anyApi as unknown as ApiTypes;
|
|
236
237
|
|
|
237
238
|
/** Internal functions — callable only server-side via \`ctx.run*\`, never from a client. */
|
|
238
|
-
export interface InternalApiTypes {${
|
|
239
|
+
export interface InternalApiTypes {${$}}
|
|
239
240
|
|
|
240
241
|
export const internal = anyApi as unknown as InternalApiTypes;
|
|
241
|
-
${
|
|
242
|
-
`:"";return
|
|
243
|
-
${
|
|
244
|
-
${I}`,s)},
|
|
242
|
+
${x.block}${y.block}`,T=[...I.includes("FunctionReference<")?["FunctionReference"]:[],...y.block===""?[]:["HttpStreamRef"]],_=T.length>0?`import type { ${T.join(", ")} } from "${a.client}";
|
|
243
|
+
`:"";return ft(`${S}import { anyApi } from "${a.client}";
|
|
244
|
+
${_}${x.importLine}${f}
|
|
245
|
+
${I}`,s)},Gr=e=>e?`${S}import { createSeedClient as createSeedClientBase } from "@lunora/seed";
|
|
245
246
|
import type { SeedClient, SeedClientOptions } from "@lunora/seed";
|
|
246
247
|
|
|
247
248
|
import schema from "../schema.js";
|
|
@@ -258,9 +259,9 @@ import type { InsertModel } from "./dataModel.js";
|
|
|
258
259
|
* const { posts } = await seed.posts((x) => x([10, 20]));
|
|
259
260
|
*/
|
|
260
261
|
export const createSeedClient = (options?: SeedClientOptions): SeedClient<InsertModel> => createSeedClientBase<InsertModel>(schema, options);
|
|
261
|
-
`:"",
|
|
262
|
+
`:"",zr=(e,t,n=!1)=>{if(e.length===0||!t)return"";const r=ne(n),o=c=>c.table===void 0?"Row":`Doc<${JSON.stringify(c.table)}> & Row`,s=e.map(c=>{g(c.exportName,"shape export name");const d=o(c),p=D(ke(c.args),c.filePath),h=Object.keys(c.args).length>0,y=h?` args: ${p};`:` args?: ${p};`,u=h?`(args: ${p}) => void`:`(args?: ${p}) => void`,l=`${c.exportName}CollectionOptions`;return`/** Options for the \`${c.exportName}\` shape binding. */
|
|
262
263
|
export interface ${qe(l)} {
|
|
263
|
-
${
|
|
264
|
+
${y}
|
|
264
265
|
/**
|
|
265
266
|
* Share the optimistic-overlay gate with other collections + mutators on this
|
|
266
267
|
* shard. Defaults to the shared per-shard registry, which is almost always what
|
|
@@ -305,14 +306,14 @@ export const ${c.exportName}Collection = (
|
|
|
305
306
|
};`}).join(`
|
|
306
307
|
|
|
307
308
|
`),i=Ke(s),a=i.length>0?`import type { ${i.join(", ")} } from "./dataModel.js";
|
|
308
|
-
`:"";return`${
|
|
309
|
+
`:"";return`${S}import type { LunoraClient, SubscriptionError } from "${r.client}";
|
|
309
310
|
import { lunoraCollectionOptions } from "@lunora/db/collections";
|
|
310
311
|
import type { CheckpointRegistry, LunoraCollectionOptions, Row } from "@lunora/db";
|
|
311
312
|
import type { Collection } from "@tanstack/db";
|
|
312
313
|
import { createCollection } from "@tanstack/db";
|
|
313
314
|
${a}
|
|
314
315
|
${s}
|
|
315
|
-
`},
|
|
316
|
+
`},zn=(e,t)=>`lunora_${N(e)}_${String(t)}`,Yn=`const callRegistered = async <R>(context: CallerCtx, functionPath: string, args: Record<string, unknown> | undefined): Promise<R> => {
|
|
316
317
|
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
317
318
|
|
|
318
319
|
if (!registered) {
|
|
@@ -320,45 +321,45 @@ ${s}
|
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
return (await registered.handler(context, args ?? {})) as R;
|
|
323
|
-
};`,
|
|
324
|
-
`),a=e.map(l=>` "${
|
|
324
|
+
};`,Xn=(e,t,n=[],r=[])=>{const o=new Map,s=l=>{o.has(l)||o.set(l,zn(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,f])=>{if(!An.test(l))throw new te("INTERNAL",`@lunora/codegen: refusing to emit import for unsafe file path: ${JSON.stringify(l)}`);return`import * as ${f} from "../${l}.js";`}).join(`
|
|
325
|
+
`),a=e.map(l=>` "${N(l.filePath)}:${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
|
|
325
326
|
`),c=t.map(l=>` ${JSON.stringify(l.id)}: ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredDataMigration,`).join(`
|
|
326
|
-
`),d=n.map(l=>` "${
|
|
327
|
+
`),d=n.map(l=>` "${N(l.filePath)}:${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredLunoraFunction,`).join(`
|
|
327
328
|
`),p=r.map(l=>` "${l.exportName}": ${o.get(l.filePath)??""}.${l.exportName} as unknown as RegisteredShape,`).join(`
|
|
328
|
-
`),h=n.map(l=>`${
|
|
329
|
-
`),u=e.map(l=>{if(l.lifecycle||Object.keys(l.args).length===0)return;const f=
|
|
330
|
-
`);return{dispatchBody:
|
|
331
|
-
${
|
|
329
|
+
`),h=n.map(l=>`${N(l.filePath)}:${l.exportName}`),y=[a,d].filter(l=>l.length>0).join(`
|
|
330
|
+
`),u=e.map(l=>{if(l.lifecycle||Object.keys(l.args).length===0)return;const f=Tn(l.args);return f===void 0?void 0:`installCompiledValidatorMap(${o.get(l.filePath)??""}.${l.exportName}.args, ${f});`}).filter(l=>l!==void 0).join(`
|
|
331
|
+
`);return{dispatchBody:y.length>0?`
|
|
332
|
+
${y}
|
|
332
333
|
`:"",importBlock:i.length>0?`${i}
|
|
333
334
|
|
|
334
335
|
`:"",installBlock:u,migrationBody:c.length>0?`
|
|
335
336
|
${c}
|
|
336
337
|
`:"",mutatorPaths:h,shapeBody:p.length>0?`
|
|
337
338
|
${p}
|
|
338
|
-
`:""}},
|
|
339
|
-
`);return` ${
|
|
339
|
+
`:""}},Zn=e=>{const t=Qe(e),n=t.map(([o,s])=>{const i=s.map(a=>{const c=D(ke(a.args),a.filePath),d=c==="{}"?"?":"",p=D(a.returnType,a.filePath);return a.kind==="stream"?` ${a.exportName}: (args${d}: ${c}) => Promise<AsyncIterable<${p}>>;`:` ${a.exportName}: (args${d}: ${c}) => Promise<${p}>;`}).join(`
|
|
340
|
+
`);return` ${E(N(o))}: {
|
|
340
341
|
${i}
|
|
341
342
|
};`}).join(`
|
|
342
|
-
`);return{implementation:t.map(([o,s])=>{const i=
|
|
343
|
-
`);return` ${
|
|
343
|
+
`);return{implementation:t.map(([o,s])=>{const i=N(o),a=s.map(c=>` ${c.exportName}: (args) => callRegistered(context, "${i}:${c.exportName}", args),`).join(`
|
|
344
|
+
`);return` ${E(i)}: {
|
|
344
345
|
${a}
|
|
345
346
|
},`}).join(`
|
|
346
|
-
`),types:n}},
|
|
347
|
+
`),types:n}},er=(e,t=[])=>{const n=new Set;for(const r of e.tables)for(const o of Object.values(r.shape)){const s=re(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))]},Yr=({agents:e=[],containers:t=[],env:n,hasAccessFacade:r=!1,hasAi:o=!1,hasAnalytics:s=!1,hasBrowser:i=!1,hasFlags:a=!1,hasHyperdrive:c=!1,hasImages:d=!1,hasKv:p=!1,hasNotify:h=!1,hasPayments:y=!1,hasPipelines:u=!1,hasR2sql:l=!1,hasX402:f=!1,identity:w,queues:$=[],schema:x,storageRuleBuckets:I=[],useUmbrella:T=!1,workflows:_=[]}={})=>{const v=ne(T),k=er(x??{tables:[]},I).map(m=>JSON.stringify(m)).join(" | "),M=o?`import type { LunoraAi } from "@lunora/ai";
|
|
347
348
|
`:"",oe=o?`
|
|
348
|
-
readonly ai: LunoraAi;`:"",B=
|
|
349
|
-
`:"",ae=
|
|
349
|
+
readonly ai: LunoraAi;`:"",B=y?`import type { LunoraPayment } from "@lunora/payment";
|
|
350
|
+
`:"",ae=y?`
|
|
350
351
|
readonly payments: LunoraPayment;`:"",se=f?`import type { X402Pay } from "@lunora/x402/pay";
|
|
351
352
|
`:"",W=f?`
|
|
352
|
-
readonly x402: X402Pay;`:"",
|
|
353
|
+
readonly x402: X402Pay;`:"",C=t.length>0?`import type { ContainerAccessor } from "@lunora/container";
|
|
353
354
|
`:"",X=t.length>0?`
|
|
354
355
|
readonly containers: {${t.map(m=>`
|
|
355
356
|
readonly ${m.exportName}: ContainerAccessor;`).join("")}
|
|
356
|
-
};`:"",
|
|
357
|
-
readonly ${m.bindingName}?: unknown;`)),...
|
|
358
|
-
readonly ${m.bindingName}?: unknown;`))
|
|
357
|
+
};`:"",L=[...o?[" /** Workers AI binding (the conventional `env.AI`), narrowing `ctx.ai`. */\n readonly AI?: unknown;"]:[],...t.map(m=>(g(m.bindingName,`container binding "${m.bindingName}"`),` /** Durable Object namespace for the \`${m.exportName}\` container. */
|
|
358
|
+
readonly ${m.bindingName}?: unknown;`)),..._.map(m=>(g(m.bindingName,`workflow binding "${m.bindingName}"`),` /** Workflow binding for the \`${m.exportName}\` workflow. */
|
|
359
|
+
readonly ${m.bindingName}?: unknown;`)),...$.map(m=>(g(m.bindingName,`queue binding "${m.bindingName}"`),` /** Queue producer binding for the \`${m.exportName}\` queue. */
|
|
359
360
|
readonly ${m.bindingName}?: unknown;`)),...e.map(m=>(g(m.bindingName,`agent binding "${m.bindingName}"`),` /** Workflow binding for the \`${m.exportName}\` agent. */
|
|
360
361
|
readonly ${m.bindingName}?: unknown;`))].join(`
|
|
361
|
-
`),
|
|
362
|
+
`),R=`
|
|
362
363
|
|
|
363
364
|
/**
|
|
364
365
|
* This project's Cloudflare bindings, as far as codegen can discover them from
|
|
@@ -371,24 +372,24 @@ ${a}
|
|
|
371
372
|
* surface, so treat them as \`Fetcher\`/\`Service<unknown>\` and cast.
|
|
372
373
|
*/
|
|
373
374
|
export interface CloudflareBindings {
|
|
374
|
-
readonly [binding: string]: unknown;${
|
|
375
|
-
${
|
|
375
|
+
readonly [binding: string]: unknown;${L?`
|
|
376
|
+
${L}`:""}
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
/** Alias for {@link CloudflareBindings} — the typed shape of \`env\`. */
|
|
379
|
-
export type Env = CloudflareBindings;`,j=(m,Me)=>Me?
|
|
380
|
-
readonly vectors: VectorSearch<VectorIndexName>;`:"",de=
|
|
381
|
-
readonly vectors: VectorSearchReader<VectorIndexName>;`:"",
|
|
380
|
+
export type Env = CloudflareBindings;`,j=(m,Me)=>Me?kn.get(m)?.field??"":"",ie=j("kv",p),le=j("analytics",s),Se=j("hyperdrive",c),Ne=j("browser",i),Te=j("images",d),ce=j("pipelines",u),V=j("r2sql",l),P=(x?.vectorIndexes.length??0)>0,H=P?' | "vectors"':"",Z=P?`
|
|
381
|
+
readonly vectors: VectorSearch<VectorIndexName>;`:"",de=P?`
|
|
382
|
+
readonly vectors: VectorSearchReader<VectorIndexName>;`:"",J=P?`import type { VectorSearch, VectorSearchReader } from "${v.server}";
|
|
382
383
|
import type { VectorIndexName } from "./dataModel.js";
|
|
383
384
|
`:"",U=r?`
|
|
384
385
|
/** Verified Cloudflare Access identity — a synchronous facade over the resolved claims (email / groups / hasGroup / claims). Anonymous when no Access token is present. */
|
|
385
386
|
readonly access: import("@lunora/cloudflare-access/context").AccessFacade;`:"",K=a?`
|
|
386
387
|
/** Feature-flag evaluation (OpenFeature). Reads are memoized per request; evaluations never throw — a provider error resolves to the supplied default. */
|
|
387
|
-
readonly flags: import("${
|
|
388
|
+
readonly flags: import("${v.flags}").LunoraFlags;`:"",Q=h?`
|
|
388
389
|
/** Multi-channel notifications (@lunora/notify): send / chat / inApp / webhook plus the push device sub-facade. Sends are external I/O — confine them to action handlers. */
|
|
389
390
|
readonly notify: import("@lunora/notify").LunoraNotify;
|
|
390
391
|
/** Device push sub-facade — the same object as ctx.notify.push (register / send / broadcast). Sends belong in action handlers. */
|
|
391
|
-
readonly push: import("@lunora/notify").LunoraPush;`:"",q=
|
|
392
|
+
readonly push: import("@lunora/notify").LunoraPush;`:"",q=_.length>0,ee=q?`import type { WorkflowHandle } from "@lunora/workflow";
|
|
392
393
|
import type * as lunoraWorkflowDefinitions from "../workflows.js";
|
|
393
394
|
`:"",Ae=q?`
|
|
394
395
|
|
|
@@ -401,10 +402,10 @@ type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params
|
|
|
401
402
|
|
|
402
403
|
/** This project's declared workflows, addressable from \`ctx.workflows\` by their \`lunora/workflows.ts\` export name. */
|
|
403
404
|
export interface LunoraWorkflows {
|
|
404
|
-
${
|
|
405
|
+
${_.map(m=>` get(name: ${JSON.stringify(m.exportName)}): WorkflowHandle<WorkflowParamsOf<typeof lunoraWorkflowDefinitions.${m.exportName}>>;`).join(`
|
|
405
406
|
`)}
|
|
406
|
-
}`:"",
|
|
407
|
-
readonly workflows: LunoraWorkflows;`:"",ue
|
|
407
|
+
}`:"",we=q?' | "workflows"':"",xe=q?`
|
|
408
|
+
readonly workflows: LunoraWorkflows;`:"",ue=$.length>0,Oe=ue?`import type { QueueProducer } from "@lunora/queue";
|
|
408
409
|
import type * as lunoraQueueDefinitions from "../queues.js";
|
|
409
410
|
`:"",Ie=ue?`
|
|
410
411
|
|
|
@@ -413,7 +414,7 @@ type QueueBodyOf<Definition> = Definition extends { __lunoraBody?: infer Body }
|
|
|
413
414
|
|
|
414
415
|
/** This project's declared queues, addressable from \`ctx.queues\` by their \`lunora/queues.ts\` export name. */
|
|
415
416
|
export interface LunoraQueues {
|
|
416
|
-
${
|
|
417
|
+
${$.map(m=>` readonly ${m.exportName}: QueueProducer<QueueBodyOf<typeof lunoraQueueDefinitions.${m.exportName}>>;`).join(`
|
|
417
418
|
`)}
|
|
418
419
|
}`:"",ve=ue?`
|
|
419
420
|
readonly queues: LunoraQueues;`:"",pe=e.length>0,Ce=pe?`import type { AgentHandle } from "@lunora/agent";
|
|
@@ -424,20 +425,20 @@ export interface LunoraAgents {
|
|
|
424
425
|
${e.map(m=>(g(m.exportName,`agent export "${m.exportName}"`),` readonly ${m.exportName}: AgentHandle;`)).join(`
|
|
425
426
|
`)}
|
|
426
427
|
}`:"",$e=pe?`
|
|
427
|
-
readonly agents: LunoraAgents;`:"",_e=
|
|
428
|
+
readonly agents: LunoraAgents;`:"",_e=w?`import type { InferIdentity } from "${v.server}";
|
|
428
429
|
import type * as lunoraIdentityContract from "../identity.js";
|
|
429
|
-
`:"",Ee=
|
|
430
|
+
`:"",Ee=w?`
|
|
430
431
|
|
|
431
432
|
/** 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. */
|
|
432
|
-
export type Identity = InferIdentity<typeof lunoraIdentityContract.${
|
|
433
|
+
export type Identity = InferIdentity<typeof lunoraIdentityContract.${w.exportName}>;
|
|
433
434
|
|
|
434
435
|
/** \`ctx.auth\` narrowed so \`getIdentity()\` resolves the declared {@link Identity} contract instead of the untyped claim bag. */
|
|
435
|
-
type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: () => Promise<Identity | null> };`:"",me=
|
|
436
|
-
readonly auth: NarrowedAuth;`:"",De=
|
|
436
|
+
type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: () => Promise<Identity | null> };`:"",me=w?' | "auth"':"",he=w?`
|
|
437
|
+
readonly auth: NarrowedAuth;`:"",De=w?", Identity":"",Fe=n?`import type * as lunoraEnvContract from "../env.js";
|
|
437
438
|
`:"",Pe=n?`
|
|
438
439
|
|
|
439
440
|
/** This app's declared env contract (\`defineEnv\` in \`lunora/env.ts\`) — the validated, coercion-aware shape of \`ctx.env\`. */
|
|
440
|
-
export type LunoraEnv = ReturnType<typeof lunoraEnvContract.${n.exportName}>;`:"",ge=n?' | "env"':"",fe=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`${
|
|
441
|
+
export type LunoraEnv = ReturnType<typeof lunoraEnvContract.${n.exportName}>;`:"",ge=n?' | "env"':"",fe=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 "${v.server}";
|
|
441
442
|
import type {
|
|
442
443
|
ActionBuilder,
|
|
443
444
|
ActionCtx as ActionCtxBase,
|
|
@@ -458,7 +459,7 @@ import type {
|
|
|
458
459
|
Storage as StorageBase,
|
|
459
460
|
TableReader,
|
|
460
461
|
Validator,
|
|
461
|
-
} from "${
|
|
462
|
+
} from "${v.server}";
|
|
462
463
|
|
|
463
464
|
import type {
|
|
464
465
|
DatabaseReaderFacade as DatabaseReaderFacadeOf,
|
|
@@ -467,7 +468,7 @@ import type {
|
|
|
467
468
|
TableReaderFacade as TableReaderFacadeOf,
|
|
468
469
|
TableWriterFacade as TableWriterFacadeOf,
|
|
469
470
|
WithArg as WithArgOf,
|
|
470
|
-
} from "${
|
|
471
|
+
} from "${v.serverDataModel}";
|
|
471
472
|
|
|
472
473
|
export type {
|
|
473
474
|
AggregateOp,
|
|
@@ -490,10 +491,10 @@ export type {
|
|
|
490
491
|
Where,
|
|
491
492
|
WhereOf,
|
|
492
493
|
WhereOperators,
|
|
493
|
-
} from "${
|
|
494
|
+
} from "${v.serverDataModel}";
|
|
494
495
|
|
|
495
496
|
import type { DataModel, Doc, GeoIndexNamesByTable, Id as IdOfTable, IndexNamesByTable, Insert, InsertModel, RankIndexNamesByTable, Relations, SearchIndexNamesByTable, TableName } from "./dataModel.js";
|
|
496
|
-
${
|
|
497
|
+
${J}${M}${B}${se}${C}${ee}${Oe}${Ce}${_e}${Fe}
|
|
497
498
|
export type { AppTableName, DataModel, Doc, Id, TableName } from "./dataModel.js";
|
|
498
499
|
|
|
499
500
|
/**
|
|
@@ -553,7 +554,7 @@ export interface OrmWriter extends OrmReader {
|
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
/** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
|
|
556
|
-
export type StorageBucketName = ${k};${
|
|
557
|
+
export type StorageBucketName = ${k};${R}${Ae}${Ie}${Le}${Ee}${Pe}
|
|
557
558
|
|
|
558
559
|
/**
|
|
559
560
|
* Project-typed contexts. The base contexts from \`@lunora/server\` are
|
|
@@ -622,16 +623,16 @@ export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${H}${me}${
|
|
|
622
623
|
readonly storage: ReadOnlyStorage<StorageBucketName>;${de}${U}${ie}${K}${Q}${le}${fe}${he}
|
|
623
624
|
}
|
|
624
625
|
|
|
625
|
-
export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${H}${
|
|
626
|
+
export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${H}${we}${me}${ge}> {
|
|
626
627
|
readonly db: Omit<DatabaseWriter, "asId" | "query" | "get"> & DatabaseWriterFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
|
|
627
628
|
readonly orm: OrmWriter;
|
|
628
|
-
readonly storage: ReadOnlyStorage<StorageBucketName>;${Z}${U}${ie}${K}${Q}${le}${fe}${
|
|
629
|
+
readonly storage: ReadOnlyStorage<StorageBucketName>;${Z}${U}${ie}${K}${Q}${le}${fe}${xe}${ve}${$e}${he}
|
|
629
630
|
}
|
|
630
631
|
|
|
631
|
-
export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${H}${
|
|
632
|
+
export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${H}${we}${me}${ge}> {
|
|
632
633
|
readonly db: Omit<DatabaseWriter, "asId" | "query" | "get"> & DatabaseWriterFacade & { asId: TypedAsId; query: TypedTableQuery; get: TypedTableGet };
|
|
633
634
|
readonly orm: OrmWriter;
|
|
634
|
-
readonly storage: StorageBase<StorageBucketName>;${Z}${U}${oe}${ae}${W}${X}${ie}${K}${Q}${Se}${Ne}${Te}${le}${ce}${V}${fe}${
|
|
635
|
+
readonly storage: StorageBase<StorageBucketName>;${Z}${U}${oe}${ae}${W}${X}${ie}${K}${Q}${Se}${Ne}${Te}${le}${ce}${V}${fe}${xe}${ve}${$e}${he}
|
|
635
636
|
}
|
|
636
637
|
|
|
637
638
|
/**
|
|
@@ -664,7 +665,7 @@ export const internalAction = lunoraBuilders.internalAction as unknown as Intern
|
|
|
664
665
|
/**
|
|
665
666
|
* \`defineMutator\` bound to this project's typed {@link MutationCtx} — declare
|
|
666
667
|
* custom mutators (\`lunora/mutators.ts\`) with it instead of importing from
|
|
667
|
-
* \`${
|
|
668
|
+
* \`${v.server}\`, and the authoritative \`server\` impl's \`ctx\` carries the
|
|
668
669
|
* schema-typed \`ctx.db\` (\`ctx.db.query("nodes")\` resolves \`Doc<"nodes">\`,
|
|
669
670
|
* \`ctx.db.patch(id, …)\` takes an \`Id<"nodes">\`) with no hand-written ctx type and
|
|
670
671
|
* no \`as unknown as\` cast. Runtime-identical to the package export; only the
|
|
@@ -706,13 +707,13 @@ export const definePolicy = createPolicyDsl<DataModel, Relations${De}>();
|
|
|
706
707
|
export const v = vBase as unknown as Omit<typeof vBase, "id"> & {
|
|
707
708
|
id: <T extends TableName>(table: T) => ColumnValidator<IdOfTable<T>, IdOfTable<T>>;
|
|
708
709
|
};
|
|
709
|
-
`},
|
|
710
|
-
`);let I=p;
|
|
711
|
-
${
|
|
710
|
+
`},tr=e=>{const t={connect:[],disconnect:[],init:[],reactor:[]};for(const n of e)n.lifecycle&&t[n.lifecycle].push(`${N(n.filePath)}:${n.exportName}`);return t},Xr=e=>{const{agents:t=[],functions:n,migrations:r=[],mutators:o=[],shapes:s=[],useUmbrella:i=!1,usesSandbox:a=!1}=e,c=n.length>0,d=ne(i),{dispatchBody:p,importBlock:h,installBlock:y,migrationBody:u,mutatorPaths:l,shapeBody:f}=Xn(n,r,o,s),w=Qn(t,n),$=Vn(a,n),x=[w.lines,$.lines].filter(R=>R.length>0).join(`
|
|
711
|
+
`);let I=p;x.length>0&&(I=p.length>0?`${p.trimEnd()}
|
|
712
|
+
${x}
|
|
712
713
|
`:`
|
|
713
|
-
${
|
|
714
|
-
`);const
|
|
715
|
-
`:""
|
|
714
|
+
${x}
|
|
715
|
+
`);const T=tr(n),_=s.length>0?`import type { RegisteredShape } from "${d.server}";
|
|
716
|
+
`:"",v=s.length>0?`
|
|
716
717
|
/**
|
|
717
718
|
* Replication-shape registry — one entry per \`defineShape\` in \`lunora/shapes.ts\`.
|
|
718
719
|
* The generated ShardDO's \`resolveShape\` override looks a shape up by name and
|
|
@@ -726,25 +727,25 @@ export const LUNORA_SHAPES: Record<string, RegisteredShape> = {${f}};
|
|
|
726
727
|
* ShardDO's \`isCustomMutator\` override routes through the client-watermark push
|
|
727
728
|
* protocol (\`x-lunora-client-id\`/\`x-lunora-client-seq\` ordering).
|
|
728
729
|
*/
|
|
729
|
-
export const LUNORA_MUTATOR_PATHS: ReadonlySet<string> = new Set([${l.map(
|
|
730
|
-
`:"",M=
|
|
731
|
-
`:"",oe=
|
|
730
|
+
export const LUNORA_MUTATOR_PATHS: ReadonlySet<string> = new Set([${l.map(R=>JSON.stringify(R)).join(", ")}]);
|
|
731
|
+
`:"",M=y.length>0?`import { DEFER_VALIDATION as DEFER, installCompiledValidatorMap } from "${d.values}";
|
|
732
|
+
`:"",oe=y.length>0?`
|
|
732
733
|
/**
|
|
733
734
|
* AOT-compiled argument validators (Worker-safe, no \`eval\`). Each is installed
|
|
734
735
|
* onto its function's live \`.args\` object and consulted by the interpreted
|
|
735
736
|
* parser as a zero-allocation fast path; anything it can't model is deferred.
|
|
736
737
|
*/
|
|
737
|
-
${
|
|
738
|
-
`:"",B=
|
|
738
|
+
${y}
|
|
739
|
+
`:"",B=Zn(n),ae=B.types?`
|
|
739
740
|
${B.types}
|
|
740
741
|
`:"",se=B.implementation?`
|
|
741
742
|
${B.implementation}
|
|
742
|
-
`:"",W=Ke(B.types),
|
|
743
|
-
`:"",X=c?"context":"_context",
|
|
743
|
+
`:"",W=Ke(B.types),C=W.length>0?`import type { ${W.join(", ")} } from "./dataModel.js";
|
|
744
|
+
`:"",X=c?"context":"_context",L=c?`${Yn}
|
|
744
745
|
|
|
745
|
-
`:"";return
|
|
746
|
+
`:"";return ft(`${S}${h}${w.importLine}${$.importLine}${M}${_}import { LunoraError } from "${d.server}";
|
|
746
747
|
import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
|
|
747
|
-
${
|
|
748
|
+
${C}
|
|
748
749
|
/**
|
|
749
750
|
* Single registered function, narrowed to the shape \`handleRpc\` needs.
|
|
750
751
|
* The real argument validators / return types are checked elsewhere — at
|
|
@@ -766,25 +767,37 @@ export interface RegisteredLunoraFunction {
|
|
|
766
767
|
* \`AbortSignal\` as a third argument — the runtime drives it frame-by-frame.
|
|
767
768
|
*/
|
|
768
769
|
handler: ((context: unknown, args: Record<string, unknown>) => Promise<unknown> | unknown) | ((context: unknown, args: Record<string, unknown>, signal?: AbortSignal) => AsyncIterable<unknown>);
|
|
770
|
+
/**
|
|
771
|
+
* The lifecycle moment a hook fires on, when this registration came from
|
|
772
|
+
* \`onConnect\`/\`onDisconnect\`/\`onShardInit\`/\`onQueryChange\`. Read at
|
|
773
|
+
* dispatch to decide whether the function runs system-trusted: \`init\` and
|
|
774
|
+
* \`reactor\` have no caller identity, so RLS has no user to scope to.
|
|
775
|
+
*/
|
|
776
|
+
lifecycle?: "connect" | "disconnect" | "init" | "reactor";
|
|
769
777
|
/** \`"internal"\` functions are rejected on the external RPC path; absence === public. */
|
|
770
778
|
visibility?: "internal" | "public";
|
|
771
779
|
}
|
|
772
|
-
${
|
|
780
|
+
${w.prelude}${$.prelude}
|
|
773
781
|
/**
|
|
774
782
|
* Static dispatch table. The key matches the \`__lunoraRef\` the client
|
|
775
783
|
* emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
|
|
776
784
|
*/
|
|
777
785
|
export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${I}};
|
|
778
|
-
${oe}${
|
|
786
|
+
${oe}${v}${k}
|
|
779
787
|
/**
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
*
|
|
788
|
+
* Lifecycle manifest: the function paths the generated ShardDO dispatches when a
|
|
789
|
+
* client's WebSocket connects (\`connect\`) or disconnects (\`disconnect\`), once
|
|
790
|
+
* per Durable Object instance before any handler runs (\`init\`), and after a
|
|
791
|
+
* write flush when a watched read's result changed (\`reactor\`). Each path also
|
|
792
|
+
* resolves through {@link LUNORA_FUNCTIONS}. The socket sides run under the
|
|
793
|
+
* socket's verified identity; \`init\` and \`reactor\` have no caller, so they run
|
|
794
|
+
* anonymous — all via system dispatch.
|
|
784
795
|
*/
|
|
785
|
-
export const LUNORA_LIFECYCLE_HOOKS: { connect: readonly string[]; disconnect: readonly string[] } = {
|
|
786
|
-
connect: [${
|
|
787
|
-
disconnect: [${
|
|
796
|
+
export const LUNORA_LIFECYCLE_HOOKS: { connect: readonly string[]; disconnect: readonly string[]; init: readonly string[]; reactor: readonly string[] } = {
|
|
797
|
+
connect: [${T.connect.map(R=>JSON.stringify(R)).join(", ")}],
|
|
798
|
+
disconnect: [${T.disconnect.map(R=>JSON.stringify(R)).join(", ")}],
|
|
799
|
+
init: [${T.init.map(R=>JSON.stringify(R)).join(", ")}],
|
|
800
|
+
reactor: [${T.reactor.map(R=>JSON.stringify(R)).join(", ")}],
|
|
788
801
|
};
|
|
789
802
|
|
|
790
803
|
/**
|
|
@@ -817,7 +830,7 @@ export type CallerCtx = ActionCtx | MutationCtx | QueryCtx;
|
|
|
817
830
|
*/
|
|
818
831
|
export interface Caller {${ae}}
|
|
819
832
|
|
|
820
|
-
${
|
|
833
|
+
${L}/** Build a {@link Caller} bound to \`context\` (typically a handler's \`ctx\`). */
|
|
821
834
|
export const createCaller = (${X}: CallerCtx): Caller => ({${se}});
|
|
822
835
|
|
|
823
836
|
/**
|
|
@@ -838,7 +851,7 @@ export interface RegisteredDataMigration {
|
|
|
838
851
|
* shard DO's admin RPC and the CLI resolve migrations to run through this map.
|
|
839
852
|
*/
|
|
840
853
|
export const LUNORA_MIGRATIONS: Record<string, RegisteredDataMigration> = {${u}};
|
|
841
|
-
`,i)},
|
|
854
|
+
`,i)},nr=e=>{const t={};for(const n of e.tables){const r={};for(const[o,s]of Object.entries(n.shape)){const i=re(s);i.kind==="id"&&i.tableName!==void 0&&(r[o]=i.tableName)}Object.keys(r).length>0&&(t[n.name]=r)}return t},rr=e=>{const t={};for(const n of e.tables){const r=[];for(const[o,s]of Object.entries(n.shape))re(s).kind==="storage"&&r.push(o);r.length>0&&(t[n.name]=r)}return t},or=e=>{const t={};for(const n of e.tables){const r=[...n.indexes.map(o=>({fields:[...o.fields],name:o.name,type:"index",...o.unique===!0?{unique:!0}:{}})),...n.searchIndexes.map(o=>({fields:[o.field,...o.filterFields??[]],name:o.name,type:"search"})),...(n.geoIndexes??[]).map(o=>({fields:[o.field],name:o.name,type:"geo"})),...n.rankIndexes.map(o=>({fields:o.sortBy.map(s=>s.field),name:o.name,type:"rank"})),...n.vectorIndexes.map(o=>({fields:o.field===void 0?[]:[o.field],name:o.name,type:"vector"}))];r.length>0&&(t[n.name]=r)}return t},ar=e=>{const t=[];for(const n of e.tables)!n.ttl||n.shardMode==="global"||t.push({...n.ttl.after===void 0?{}:{after:n.ttl.after},field:n.ttl.field,...n.softDelete?{softDeleteField:n.softDelete.field}:{},table:n.name});return t},sr=e=>{if(e.kind!=="union"||e.members===void 0||e.members.length===0)return;const t=[];for(const n of e.members){if(n.kind!=="literal"||n.literalValue===void 0)return;try{const r=JSON.parse(n.literalValue);if(typeof r!="string")return;t.push(r)}catch{return}}return t},ir=(e,t)=>{const n=re(t),r={name:e,optional:mt(t),type:n.kind};n.kind==="id"&&n.tableName!==void 0&&(r.ref=n.tableName),n.kind==="storage"&&(r.isStorage=!0,n.bucket!==void 0&&(r.bucket=n.bucket));const o=sr(n);return o!==void 0&&(r.enumValues=o),n.column?.notNull===!1&&(r.nullable=!0),r},lr=e=>{const t={};for(const n of e.tables){const r=[{name:"_id",optional:!1,pk:!0,type:"id"},{name:"_creationTime",optional:!1,type:"number"},...n.commitOrdered===!0?[{name:"_commitSeq",optional:!1,type:"number"}]:[]];for(const[o,s]of Object.entries(n.shape))r.push(ir(o,s));t[n.name]=r}return t},cr=e=>e?{importFragment:"serveRelationFanout, ",override:`
|
|
842
855
|
protected override async runRelationFanoutRead(functionPath: string, args: Record<string, unknown>): Promise<unknown> {
|
|
843
856
|
this.ensureMigrated();
|
|
844
857
|
|
|
@@ -846,7 +859,7 @@ export const LUNORA_MIGRATIONS: Record<string, RegisteredDataMigration> = {${u}}
|
|
|
846
859
|
|
|
847
860
|
return serveRelationFanout(schema as unknown as SchemaLike, db, functionPath, args);
|
|
848
861
|
}
|
|
849
|
-
`}:{importFragment:"",override:""},
|
|
862
|
+
`}:{importFragment:"",override:""},dr=e=>{if(!e)return{build:"",configField:"",contextField:"",stub:""};const t='throw new Error("ctx.ai: no AI binding found. Add an \\`ai\\` binding (env.AI) to wrangler.jsonc, or pass \\`ai\\` to createShardDO().");';return{build:`
|
|
850
863
|
const aiBinding = config.ai?.(env) ?? (env as Record<string, unknown>).AI;
|
|
851
864
|
// Correlate AI-Gateway-routed calls with the Lunora trace: thread the
|
|
852
865
|
// function path + trace id into createAi, which folds them into the
|
|
@@ -877,19 +890,19 @@ const aiStub: LunoraAi = {
|
|
|
877
890
|
${t}
|
|
878
891
|
}) as unknown as LunoraAi["workersai"],
|
|
879
892
|
};
|
|
880
|
-
`}},
|
|
893
|
+
`}},O=(e,t,n,{cast:r="",sync:o=[]}={})=>{const s=n.map(i=>` ${i}: ${o.includes(i)?"":"async "}() => {
|
|
881
894
|
${t}
|
|
882
895
|
},`).join(`
|
|
883
896
|
`);return`
|
|
884
897
|
const ${e} = {
|
|
885
898
|
${s}
|
|
886
899
|
}${r};
|
|
887
|
-
`},
|
|
900
|
+
`},F={build:"",configField:"",contextField:"",importLines:[],stub:""},ur=e=>e?{build:`
|
|
888
901
|
const kvBinding = config.kv?.(env) ?? (env as Record<string, unknown>).KV;
|
|
889
902
|
const kv: Kv = kvBinding ? createKv({ namespace: kvBinding as KVNamespaceLike }) : kvStub;
|
|
890
903
|
`,configField:`
|
|
891
904
|
kv?: (env: Record<string, unknown>) => KVNamespaceLike;`,contextField:`
|
|
892
|
-
kv,`,importLines:['import type { Kv, KVNamespaceLike } from "@lunora/bindings/kv";','import { createKv } from "@lunora/bindings/kv";'],stub:
|
|
905
|
+
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"])}:F,pr=(e,t)=>e?{build:`
|
|
893
906
|
const flags: import("${t}").LunoraFlags = createFlags({
|
|
894
907
|
hooks: flagsConfig.hooks,
|
|
895
908
|
logger: flagsConfig.logger,
|
|
@@ -898,17 +911,17 @@ ${s}
|
|
|
898
911
|
});
|
|
899
912
|
`,configField:`
|
|
900
913
|
flags?: (env: Record<string, unknown>) => import("${t}").Provider;`,contextField:`
|
|
901
|
-
flags,`,importLines:[`import { createFlags } from "${t}";`,'import flagsConfig from "../flags.js";'],stub:""}:
|
|
914
|
+
flags,`,importLines:[`import { createFlags } from "${t}";`,'import flagsConfig from "../flags.js";'],stub:""}:F,mr=e=>e?{build:`
|
|
902
915
|
const { notify, push } = createNotify(notifyConfig, env, { log, metrics });
|
|
903
916
|
`,configField:"",contextField:`
|
|
904
917
|
notify,
|
|
905
|
-
push,`,importLines:['import { createNotify } from "@lunora/notify";','import notifyConfig from "../notify.js";'],stub:""}:
|
|
918
|
+
push,`,importLines:['import { createNotify } from "@lunora/notify";','import notifyConfig from "../notify.js";'],stub:""}:F,hr=e=>e?{build:`
|
|
906
919
|
const envConfig = lunoraEnvContract.${e.exportName}(env);
|
|
907
920
|
`,configField:"",contextField:`
|
|
908
|
-
env: envConfig,`,importLines:['import * as lunoraEnvContract from "../env.js";'],stub:""}:
|
|
921
|
+
env: envConfig,`,importLines:['import * as lunoraEnvContract from "../env.js";'],stub:""}:F,gr=e=>e?{build:`
|
|
909
922
|
const access = accessFacade(identity, userId);
|
|
910
923
|
`,configField:"",contextField:`
|
|
911
|
-
access,`,importLines:['import { accessFacade } from "@lunora/cloudflare-access/context";'],stub:""}:
|
|
924
|
+
access,`,importLines:['import { accessFacade } from "@lunora/cloudflare-access/context";'],stub:""}:F,fr=(e,t,n)=>{if(!t)return{constant:"",evaluateOverride:"",subscriptionOverride:""};const r=a=>`
|
|
912
925
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
913
926
|
const flags: import("${n}").LunoraFlags = createFlags({
|
|
914
927
|
hooks: flagsConfig.hooks,
|
|
@@ -972,25 +985,25 @@ const LUNORA_FLAG_KEYS: ReadonlyArray<{ key: string; type: "boolean" | "number"
|
|
|
972
985
|
|
|
973
986
|
return flags.boolean(key, typeof args.default === "boolean" ? args.default : false, context);
|
|
974
987
|
}
|
|
975
|
-
`;return{constant:o,evaluateOverride:s,subscriptionOverride:i}},
|
|
988
|
+
`;return{constant:o,evaluateOverride:s,subscriptionOverride:i}},yr=e=>e?{build:`
|
|
976
989
|
const analyticsBinding = config.analytics?.(env) ?? (env as Record<string, unknown>).ANALYTICS;
|
|
977
990
|
const analytics: AnalyticsClient = analyticsBinding ? createAnalytics(analyticsBinding as AnalyticsEngineDatasetLike) : analyticsStub;
|
|
978
991
|
`,configField:`
|
|
979
992
|
analytics?: (env: Record<string, unknown>) => AnalyticsEngineDatasetLike;`,contextField:`
|
|
980
|
-
analytics,`,importLines:['import type { AnalyticsClient, AnalyticsEngineDatasetLike } from "@lunora/bindings/analytics";','import { createAnalytics } from "@lunora/bindings/analytics";'],stub:
|
|
993
|
+
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"]})}:F,br=e=>e?{build:`
|
|
981
994
|
const imagesBinding = config.images?.(env) ?? (env as Record<string, unknown>).IMAGES;
|
|
982
995
|
const images: Images = imagesBinding ? createImages({ binding: imagesBinding as ImagesBindingLike }) : imagesStub;
|
|
983
996
|
`,configField:`
|
|
984
997
|
images?: (env: Record<string, unknown>) => ImagesBindingLike;`,contextField:`
|
|
985
|
-
images,`,importLines:['import type { Images, ImagesBindingLike } from "@lunora/bindings/images";','import { createImages } from "@lunora/bindings/images";'],stub:
|
|
998
|
+
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"])}:F,kr=e=>e?{build:`
|
|
986
999
|
const sql: SqlClient = config.sql ? config.sql(env) : sqlStub;
|
|
987
1000
|
`,configField:`
|
|
988
1001
|
sql?: (env: Record<string, unknown>) => SqlClient;`,contextField:`
|
|
989
|
-
sql,`,importLines:['import type { SqlClient } from "@lunora/hyperdrive";'],stub:
|
|
1002
|
+
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"])}:F,wr=e=>e?{build:`
|
|
990
1003
|
const browser: Browser = config.browser ? config.browser(env) : browserStub;
|
|
991
1004
|
`,configField:`
|
|
992
1005
|
browser?: (env: Record<string, unknown>) => Browser;`,contextField:`
|
|
993
|
-
browser,`,importLines:['import type { Browser } from "@lunora/browser";'],stub:
|
|
1006
|
+
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"])}:F,xr=e=>e?{build:`
|
|
994
1007
|
const r2sqlEnv = env as Record<string, unknown>;
|
|
995
1008
|
const r2sqlAccountId = (r2sqlEnv.R2_SQL_ACCOUNT_ID ?? r2sqlEnv.CLOUDFLARE_ACCOUNT_ID) as string | undefined;
|
|
996
1009
|
const r2sqlToken = r2sqlEnv.R2_SQL_TOKEN as string | undefined;
|
|
@@ -1001,18 +1014,18 @@ const LUNORA_FLAG_KEYS: ReadonlyArray<{ key: string; type: "boolean" | "number"
|
|
|
1001
1014
|
? createR2Sql({ accountId: r2sqlAccountId, apiToken: r2sqlToken, bucket: r2sqlBucket })
|
|
1002
1015
|
: r2sqlStub;
|
|
1003
1016
|
`,configField:`
|
|
1004
|
-
r2sql?: (env: Record<string, unknown>) => R2SqlClient;`,contextField:"",importLines:['import type { R2SqlClient } from "@lunora/bindings/r2sql";','import { createR2Sql } from "@lunora/bindings/r2sql";'],stub:
|
|
1017
|
+
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"]})}:F,vr=e=>e?{build:`
|
|
1005
1018
|
const pipelinesBinding = config.pipelines?.(env) ?? (env as Record<string, unknown>).PIPELINES;
|
|
1006
1019
|
const pipelines: PipelineClient = pipelinesBinding ? createPipelines({ binding: pipelinesBinding as PipelineBindingLike }) : pipelinesStub;
|
|
1007
1020
|
`,configField:`
|
|
1008
|
-
pipelines?: (env: Record<string, unknown>) => PipelineBindingLike;`,contextField:"",importLines:['import type { PipelineBindingLike, PipelineClient } from "@lunora/bindings/pipelines";','import { createPipelines } from "@lunora/bindings/pipelines";'],stub:
|
|
1021
|
+
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"])}:F,Zr=(e,t)=>{if(e.length===0)return"";const n=t?`, ${JSON.stringify(t)}`:"",r=e.map(s=>(g(s.exportName,`container export "${s.exportName}"`),g(s.className,`container class "${s.className}"`),`/** Container DO for the \`${s.exportName}\` definition (binding \`${s.bindingName}\`). */
|
|
1009
1022
|
export class ${s.className} extends LunoraContainer {
|
|
1010
1023
|
public constructor(ctx: ConstructorParameters<typeof LunoraContainer>[0], env: Record<string, unknown>) {
|
|
1011
1024
|
super(ctx, env, ${s.exportName}, "${s.exportName}"${n});
|
|
1012
1025
|
}
|
|
1013
1026
|
}
|
|
1014
1027
|
`)).join(`
|
|
1015
|
-
`),o=e.map(s=>s.exportName).join(", ");return`${
|
|
1028
|
+
`),o=e.map(s=>s.exportName).join(", ");return`${S}/**
|
|
1016
1029
|
* Container-enabled Durable Object classes for the containers declared in
|
|
1017
1030
|
* \`lunora/containers.ts\`. Re-export them from your worker entry — wrangler
|
|
1018
1031
|
* requires each \`containers[].class_name\` to be exported by the worker:
|
|
@@ -1030,7 +1043,7 @@ import { ${o} } from "../containers.js";
|
|
|
1030
1043
|
|
|
1031
1044
|
export { ContainerProxy } from "@lunora/container/do";
|
|
1032
1045
|
|
|
1033
|
-
${r}`}
|
|
1046
|
+
${r}`},$r=(e,t)=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const r of e)g(r.exportName,`container export "${r.exportName}"`),g(r.bindingName,`container binding "${r.bindingName}"`);const n=e.map(r=>{const o=r.maxInstances===void 0?"":`, maxInstances: ${String(r.maxInstances)}`;return` { binding: "${r.bindingName}", exportName: "${r.exportName}"${o} },`}).join(`
|
|
1034
1047
|
`);return{build:`
|
|
1035
1048
|
const containers = createContainerContext(env, LUNORA_CONTAINERS, ${t?JSON.stringify(t):"undefined"}, this.getCurrentTraceparent());
|
|
1036
1049
|
`,contextField:`
|
|
@@ -1039,14 +1052,14 @@ ${r}`},wr=(e,t)=>{if(e.length===0)return{build:"",contextField:"",importLines:[]
|
|
|
1039
1052
|
const LUNORA_CONTAINERS: ReadonlyArray<ContainerBindingSpec> = [
|
|
1040
1053
|
${n}
|
|
1041
1054
|
];
|
|
1042
|
-
`}},
|
|
1055
|
+
`}},eo=e=>{if(e.length===0)return"";const t=e.map(r=>(g(r.exportName,`workflow export "${r.exportName}"`),g(r.className,`workflow class "${r.className}"`),`/** WorkflowEntrypoint for the \`${r.exportName}\` definition (binding \`${r.bindingName}\`). */
|
|
1043
1056
|
export class ${r.className} extends LunoraWorkflow<WorkflowParamsOf<typeof ${r.exportName}>, WorkflowOutputOf<typeof ${r.exportName}>> {
|
|
1044
1057
|
public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
|
|
1045
1058
|
super(ctx, env, ${r.exportName}, "${r.exportName}");
|
|
1046
1059
|
}
|
|
1047
1060
|
}
|
|
1048
1061
|
`)).join(`
|
|
1049
|
-
`),n=e.map(r=>r.exportName).join(", ");return`${
|
|
1062
|
+
`),n=e.map(r=>r.exportName).join(", ");return`${S}/**
|
|
1050
1063
|
* WorkflowEntrypoint classes for the workflows declared in
|
|
1051
1064
|
* \`lunora/workflows.ts\`. Re-export them from your worker entry — wrangler
|
|
1052
1065
|
* requires each \`workflows[].class_name\` to be exported by the worker:
|
|
@@ -1067,7 +1080,7 @@ type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params
|
|
|
1067
1080
|
/** Output type carried by a \`defineWorkflow\` definition (its phantom \`__output\`), so each entrypoint keeps the authored return type. */
|
|
1068
1081
|
type WorkflowOutputOf<Definition> = Definition extends { __output?: infer Output } ? Output : unknown;
|
|
1069
1082
|
|
|
1070
|
-
${t}`},
|
|
1083
|
+
${t}`},to=e=>{if(e.length===0)return"";const n=e.filter(a=>a.voice).length>0,r=e.map(a=>{g(a.exportName,`agent export "${a.exportName}"`),g(a.className,`agent class "${a.className}"`);const c=`/** WorkflowEntrypoint for the \`${a.exportName}\` agent (binding \`${a.bindingName}\`). */
|
|
1071
1084
|
export class ${a.className} extends LunoraWorkflow<AgentRunInput, AgentRunResult> {
|
|
1072
1085
|
public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
|
|
1073
1086
|
super(ctx, env, compileAgentWorkflow(${a.exportName}, "${a.exportName}"), "${a.exportName}");
|
|
@@ -1081,7 +1094,7 @@ export class ${d} extends VoiceSessionDO {
|
|
|
1081
1094
|
}
|
|
1082
1095
|
}
|
|
1083
1096
|
`;return`${c}${h}`}).join(`
|
|
1084
|
-
`),o=e.map(a=>a.exportName).join(", ");return`${
|
|
1097
|
+
`),o=e.map(a=>a.exportName).join(", ");return`${S}/**
|
|
1085
1098
|
* WorkflowEntrypoint classes for the agents declared in \`lunora/agents.ts\`.
|
|
1086
1099
|
* Each \`defineAgent\` compiles a replay-safe tool-loop onto a Cloudflare
|
|
1087
1100
|
* Workflow. Re-export them from your worker entry — wrangler requires each
|
|
@@ -1095,8 +1108,8 @@ import type { AgentRunInput, AgentRunResult } from "@lunora/agent";
|
|
|
1095
1108
|
|
|
1096
1109
|
import { ${o} } from "../agents.js";
|
|
1097
1110
|
|
|
1098
|
-
${r}`},
|
|
1099
|
-
`);return`${
|
|
1111
|
+
${r}`},no=e=>{const t=e.filter(o=>o.mode==="push");if(t.length===0)return"";for(const o of t)g(o.exportName,`queue export "${o.exportName}"`);const n=t.map(o=>o.exportName).join(", "),r=t.map(o=>` ${JSON.stringify(o.name)}: { definition: ${o.exportName}, exportName: ${JSON.stringify(o.exportName)} },`).join(`
|
|
1112
|
+
`);return`${S}/**
|
|
1100
1113
|
* Push-consumer registry for the queues declared in \`lunora/queues.ts\`. The
|
|
1101
1114
|
* composed worker's \`queue(batch, env, ctx)\` entry routes each delivered batch
|
|
1102
1115
|
* by \`batch.queue\` to the matching \`defineQueue\` handler. Wired automatically
|
|
@@ -1110,7 +1123,7 @@ import { ${n} } from "../queues.js";
|
|
|
1110
1123
|
export const LUNORA_QUEUE_REGISTRY: QueueRegistry = {
|
|
1111
1124
|
${r}
|
|
1112
1125
|
};
|
|
1113
|
-
`},
|
|
1126
|
+
`},Rr=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)g(n.exportName,`workflow export "${n.exportName}"`),g(n.bindingName,`workflow binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}" },`).join(`
|
|
1114
1127
|
`);return{build:`
|
|
1115
1128
|
const workflows = createWorkflowContext(env, LUNORA_WORKFLOWS);
|
|
1116
1129
|
`,contextField:`
|
|
@@ -1119,7 +1132,7 @@ ${r}
|
|
|
1119
1132
|
const LUNORA_WORKFLOWS: ReadonlyArray<WorkflowBindingSpec> = [
|
|
1120
1133
|
${t}
|
|
1121
1134
|
];
|
|
1122
|
-
`}}
|
|
1135
|
+
`}},Sr=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)g(n.exportName,`queue export "${n.exportName}"`),g(n.bindingName,`queue binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}", name: ${JSON.stringify(n.name)} },`).join(`
|
|
1123
1136
|
`);return{build:`
|
|
1124
1137
|
const queues = createQueueContext(env, LUNORA_QUEUES);
|
|
1125
1138
|
`,contextField:`
|
|
@@ -1128,7 +1141,7 @@ ${t}
|
|
|
1128
1141
|
const LUNORA_QUEUES: ReadonlyArray<QueueBindingSpec> = [
|
|
1129
1142
|
${t}
|
|
1130
1143
|
];
|
|
1131
|
-
`}},
|
|
1144
|
+
`}},Nr=e=>{if(e.length===0)return{build:"",contextField:"",importLines:[],specs:""};for(const n of e)g(n.exportName,`agent export "${n.exportName}"`),g(n.bindingName,`agent binding "${n.bindingName}"`);const t=e.map(n=>` { binding: "${n.bindingName}", exportName: "${n.exportName}"${n.publicRun===!0?", publicRun: true":""} },`).join(`
|
|
1132
1145
|
`);return{build:`
|
|
1133
1146
|
const agents = createAgentContext(env, LUNORA_AGENTS);
|
|
1134
1147
|
`,contextField:`
|
|
@@ -1144,21 +1157,21 @@ const ${e}: ${t} = ${JSON.stringify(r,void 0,4)};
|
|
|
1144
1157
|
protected override ${n}(): ${t} {
|
|
1145
1158
|
return ${e};
|
|
1146
1159
|
}
|
|
1147
|
-
`}),
|
|
1160
|
+
`}),Tr=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 bt("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. */")},Ar=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 bt("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. */")},Or=e=>e?{imports:['import type { LunoraDatabaseLike as LunoraPaymentDbLike, LunoraPayment, PaymentsFromContextOptions } from "@lunora/payment";','import { paymentsFromContext } from "@lunora/payment";'],build:`
|
|
1148
1161
|
const payments: LunoraPayment = config.payment
|
|
1149
1162
|
? paymentsFromContext({ auth: { userId: userId ?? null }, db: db as unknown as LunoraPaymentDbLike }, config.payment(env))
|
|
1150
1163
|
: paymentStub;
|
|
1151
1164
|
`,configField:`
|
|
1152
1165
|
payment?: (env: Record<string, unknown>) => PaymentsFromContextOptions;`,contextField:`
|
|
1153
|
-
payments,`,stub:
|
|
1166
|
+
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:""},Ir=e=>e?{imports:['import type { X402Pay, X402PayConfig } from "@lunora/x402/pay";','import { lazyX402Pay } from "@lunora/x402/pay";'],build:`
|
|
1154
1167
|
const x402: X402Pay = config.x402
|
|
1155
1168
|
? lazyX402Pay(config.x402(env), { getSecret: (name: string) => secrets.get(name) })
|
|
1156
1169
|
: x402Stub;
|
|
1157
1170
|
`,configField:`
|
|
1158
|
-
x402?: (env: Record<string, unknown>) => X402PayConfig;`,stub:
|
|
1171
|
+
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:""},Cr=(e,t,n,r)=>["AdvisorProcedure","AdvisoryFinding","DatabaseWriterLike","DataMigrationLike","ExportRow",...r?["FlagsResult"]:[],"ImportShardResult","KeyRange","MaskPoliciesResult","MigrationRunResult",...n?["QueuesResult"]:[],"RunShardApplyCdcArgs","RunShardExportArgs","RunShardImportArgs","RunShardMigrationArgs","RlsPoliciesResult","RunShardRankBeforeArgs","RunShardRankPageArgs","RunShardWriteArgs","RunShardWriteResult","SchedulerLike","TransactionHeadroomTracker","SchemaLike","ShardDOState","ShardRankPageResult","SqlExec","StorageRulesResult","StudioFeaturesResult","SystemReaderStorageLike","TelemetrySink",...t?["WorkflowsResult"]:[],...e?["WriteHook"]:[]],ro=({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:h=!1,hasImages:y=!1,hasKv:u=!1,hasNotify:l=!1,hasPayments:f=!1,hasPipelines:w=!1,hasR2sql:$=!1,hasX402:x=!1,maskMetadata:I,mutators:T=[],queues:_=[],rlsMetadata:v,schema:k,schemaSnapshot:M,shapes:oe=[],storageRules:B,studioFeatures:ae,useUmbrella:se=!1,workflows:W=[]})=>{const C=ne(se),X=T.length>0,L=oe.length>0,R=M===void 0?"":fn(M),j=M===void 0?"":`
|
|
1159
1172
|
/** 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. */
|
|
1160
|
-
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(
|
|
1161
|
-
`,ie=M===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",{build:le,configField:Se,contextField:Ne,stub:Te}=
|
|
1173
|
+
const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.stringify(yn(M))}, json: ${JSON.stringify(R)} };
|
|
1174
|
+
`,ie=M===void 0?"":", schemaSnapshot: LUNORA_SCHEMA_SNAPSHOT",{build:le,configField:Se,contextField:Ne,stub:Te}=dr(a),ce=gr(i),V=ur(u),P=pr(p,C.flags),H=fr(s,p,C.flags),Z=mr(l),de=hr(o),J=yr(c),U=br(y),K=kr(h),Q=wr(d),q=xr($),ee=vr(w),{build:Ae,contextField:we,importLines:xe,specs:ue}=Sr(_),{build:Oe,contextField:Ie,importLines:ve,specs:pe}=$r(r,k.jurisdiction),{build:Ce,contextField:Le,importLines:$e,specs:_e}=Rr(W),{build:Ee,contextField:me,importLines:he,specs:De}=Nr(n),{build:Fe,configField:Pe,contextField:ge,imports:fe,stub:He}=Or(f),{build:m,configField:Me,imports:wt,stub:xt}=Ir(x),vt=e,$t=t,Rt=v??{policies:[],roles:[]},St=I??{columns:[]},Nt=B??{rules:[]},Tt=ae??{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:At,override:Ot}=Tr(W),{constant:It,override:Ct}=Ar(_),G=k.vectorIndexes.length>0,Je=new Set(k.tables.filter(b=>typeof b.shardMode=="object"&&b.shardMode.kind==="shardBy").map(b=>b.name)),Re=k.vectorIndexes.some(b=>Je.has(b.table)),ye=k.tables.some(b=>b.shardMode==="global"),Be=k.tables.some(b=>b.shardMode==="global"&&b.globalBackend==="hyperdrive"),Ge=k.tables.some(b=>b.shardMode==="global"&&b.globalBackend!=="hyperdrive"),z=k.tables.some(b=>b.externalSource!==void 0),ze=k.tables.some(b=>b.memory===!0);if(Ge&&Be)throw new te("INTERNAL",'lunora codegen: mixing `.global()` (D1) and `.global({ backend: "hyperdrive" })` tables in one app is not supported yet — use a single global backend.');const je=k.tables.length>0,Lt=nr(k),_t=or(k),Et=lr(k),Dt=rr(k),Ye=ar(k),Xe=Cr(G,W.length>0,_.length>0,p);L&&Xe.push("WhereInput");const Ze=cr(ye),Ft=L?`
|
|
1162
1175
|
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 {
|
|
1163
1176
|
const shape = LUNORA_SHAPES[name];
|
|
1164
1177
|
|
|
@@ -1211,45 +1224,57 @@ const LUNORA_SCHEMA_SNAPSHOT: { hash: string; json: string } = { hash: ${JSON.st
|
|
|
1211
1224
|
|
|
1212
1225
|
return { columns: shape.columns, effectiveWhere, global: isGlobal, table: shape.table };
|
|
1213
1226
|
}
|
|
1214
|
-
`:"",
|
|
1227
|
+
`:"",Pt=X?`
|
|
1215
1228
|
protected override isCustomMutator(functionPath: string): boolean {
|
|
1216
1229
|
return LUNORA_MUTATOR_PATHS.has(functionPath);
|
|
1217
1230
|
}
|
|
1218
|
-
`:"",
|
|
1219
|
-
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",
|
|
1220
|
-
d1?: (env: Record<string, unknown>, request?: { bookmark?: string; identity?: Record<string, unknown>; onBookmark?: (bookmark: string | undefined) => void; userId?: string }) => DatabaseWriterLike | undefined;`:"",
|
|
1221
|
-
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",
|
|
1222
|
-
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",
|
|
1231
|
+
`:"",Mt=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":"",Bt=L?"assertShapeShardable, ":"",be=[`import type { ${Xe.join(", ")} } from "${C.do}";`,`import { applyCdcChanges, buildReprojectionMigration, ${ze?"clearMemoryTables, ":""}${Bt}createReadFootprint, createShardCtxDb, exportShardRows, importShardRows, ${z?"isSourceDue, pullExternalSourceIncrementalTick, pullExternalSourceTick, ":""}${Re?"ROOT_SHARD_NAME, ":""}runDataMigration, runShardMigrations, ${Ze.importFragment}ShardDO as ShardDOBase } from "${C.do}";`,...z?[`import type { ExternalSourceLike, SourceClientLike, TraceRefLike } from "${C.do}";`]:[],L?`import { asBucketStorage, buildRlsReadRegistry, composeShapeReadWhere, createSecrets, LunoraError } from "${C.server}";`:`import { asBucketStorage, createSecrets, LunoraError } from "${C.server}";`];je&&be.push(`import { bindOrm, bindTableFacade } from "${C.server}";`),G&&be.push('import type { SchemaLike as VectorSchemaLike, VectorizeIndexLike, VectorSearchLike } from "@lunora/bindings/vectors";','import { createContextVectors, createVectors, createVectorSyncHook } from "@lunora/bindings/vectors";'),a&&be.push('import type { AiBindingLike, LunoraAi } from "@lunora/ai";','import { createAi } from "@lunora/ai";'),be.push(...ce.importLines,...V.importLines,...P.importLines,...Z.importLines,...de.importLines,...J.importLines,...U.importLines,...K.importLines,...Q.importLines,...q.importLines,...ee.importLines,...ve,...$e,...xe,...he,...fe,...wt,"",'import schema from "../schema.js";',`import { ${["LUNORA_FUNCTIONS","LUNORA_LIFECYCLE_HOOKS","LUNORA_MIGRATIONS",...X?["LUNORA_MUTATOR_PATHS"]:[],...L?["LUNORA_SHAPES"]:[]].join(", ")} } from "./functions.js";`);const jt=G?`
|
|
1232
|
+
vectors?: (env: Record<string, unknown>) => Record<string, VectorizeIndexLike>;`:"",qt=Ge?`
|
|
1233
|
+
d1?: (env: Record<string, unknown>, request?: { bookmark?: string; identity?: Record<string, unknown>; onBookmark?: (bookmark: string | undefined) => void; userId?: string }) => DatabaseWriterLike | undefined;`:"",Wt=Be?`
|
|
1234
|
+
hyperdriveGlobal?: (env: Record<string, unknown>, request?: { identity?: Record<string, unknown>; userId?: string }) => DatabaseWriterLike | undefined;`:"",Ut=z?`
|
|
1235
|
+
sourceClient?: (env: Record<string, unknown>, binding: string) => { query: <Row = Record<string, unknown>>(text: string, params?: readonly unknown[]) => Promise<Row[]> } | undefined;`:"",Kt='throw new Error("ctx.db.<globalTable>: no global backend configured. Pass `d1` or `hyperdriveGlobal` to createShardDO().");',Qt='throw new Error("ctx.vectors: no vectors configured. Pass `vectors` to createShardDO().");',Vt='throw new Error("ctx.scheduler: no scheduler configured. Pass `scheduler` to createShardDO().");',Ht='throw new Error("ctx.storage: no storage configured. Pass `storage` to createShardDO().");',Jt=ye?O("globalDbStub: DatabaseWriterLike",Kt,["aggregate","count","delete","findFirst","findFirstOrThrow","findMany","get","groupBy","insert","normalizeId","patch","query","rank","rankPage","replace"],{sync:["normalizeId","query"]}):"",Gt=G?O("vectorsStub: VectorSearchLike",Qt,["deleteByIds","getByIds","query","upsert","upsertNow"]):"",zt=Re?`
|
|
1223
1236
|
const vectorShardKey = this.currentShardKey();
|
|
1224
|
-
`:"",
|
|
1237
|
+
`:"",Yt=Re?"namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, ":"",Xt=Re?`, { namespace: vectorShardKey === ROOT_SHARD_NAME ? undefined : vectorShardKey, shardedIndexNames: [${k.vectorIndexes.filter(b=>Je.has(b.table)).map(b=>JSON.stringify(b.name)).join(", ")}] }`:"",Zt=G?`
|
|
1225
1238
|
let vectors: VectorSearchLike;
|
|
1226
1239
|
let onWrite: WriteHook | undefined;
|
|
1227
1240
|
|
|
1228
1241
|
if (config.vectors) {
|
|
1229
1242
|
const lunora = createVectors({ indexes: config.vectors(env) });
|
|
1230
|
-
${
|
|
1231
|
-
vectors = createContextVectors(lunora${
|
|
1232
|
-
onWrite = createVectorSyncHook({ ${
|
|
1243
|
+
${zt}
|
|
1244
|
+
vectors = createContextVectors(lunora${Xt});
|
|
1245
|
+
onWrite = createVectorSyncHook({ ${Yt}schema: schema as unknown as VectorSchemaLike, vectors });
|
|
1233
1246
|
} else {
|
|
1234
1247
|
vectors = vectorsStub;
|
|
1235
1248
|
onWrite = undefined;
|
|
1236
1249
|
}
|
|
1237
|
-
`:"",
|
|
1250
|
+
`:"",en=`{
|
|
1238
1251
|
auth: { identity: identity ?? null, userId: userId ?? null },
|
|
1239
1252
|
broadcast: (delta) => {
|
|
1240
1253
|
this.recordChangedTable(delta.table, delta.indexKeys);
|
|
1241
1254
|
},
|
|
1242
1255
|
cdc: config.cdc ?? false,
|
|
1243
|
-
|
|
1256
|
+
// A dispatch with NO caller identity runs system-trusted: RLS scopes
|
|
1257
|
+
// rows to a user, and these have no user to scope to. This is the tier
|
|
1258
|
+
// migrations, imports and the external-source poll loop already run in
|
|
1259
|
+
// (see \`adminWriterPrelude\`). Only \`onShardInit\` and \`onQueryChange\`
|
|
1260
|
+
// reach it; \`onConnect\`/\`onDisconnect\` carry a verified identity and
|
|
1261
|
+
// stay guarded like any request.
|
|
1262
|
+
enforceRls: options.trusted !== true,
|
|
1244
1263
|
headroom: options.headroom ?? this.transactionHeadroom(),
|
|
1264
|
+
// A live predicate, not a flag: the transaction opens AFTER this ctx
|
|
1265
|
+
// is built (a mutation dispatch wraps the handler), so only a call-time
|
|
1266
|
+
// read reports the truth. \`_commitSeq\` reuses one sequence across
|
|
1267
|
+
// writes only while they commit together — an action is not wrapped, so
|
|
1268
|
+
// each of its writes allocates its own.
|
|
1269
|
+
inTransaction: () => this.isInTransaction(),
|
|
1245
1270
|
onIndexUse: this.getCtxDbIndexUseHook(),
|
|
1246
1271
|
onRead: options.onRead ?? this.getCtxDbReadHook(),
|
|
1247
|
-
onReadRange: options.onReadRange,${
|
|
1272
|
+
onReadRange: options.onReadRange,${G?`
|
|
1248
1273
|
onWrite,`:""}
|
|
1249
1274
|
scheduler,
|
|
1250
1275
|
schema: schema as unknown as SchemaLike,
|
|
1251
1276
|
sql: this.sql as SqlExec,
|
|
1252
|
-
storage,${
|
|
1277
|
+
storage,${ye?`
|
|
1253
1278
|
globalDb,`:""}
|
|
1254
1279
|
}`,Y=` const env = (this.env ?? {}) as Record<string, unknown>;
|
|
1255
1280
|
const scheduler = (config.scheduler?.(env) ?? schedulerStub) as SchedulerLike;
|
|
@@ -1258,21 +1283,23 @@ ${Jt}
|
|
|
1258
1283
|
this.recordChangedTable(delta.table, delta.indexKeys);
|
|
1259
1284
|
},
|
|
1260
1285
|
cdc: config.cdc ?? false,
|
|
1286
|
+
// Live predicate, same as the user-facing ctx — see \`databaseOptions\`.
|
|
1287
|
+
inTransaction: () => this.isInTransaction(),
|
|
1261
1288
|
scheduler,
|
|
1262
1289
|
schema: schema as unknown as SchemaLike,
|
|
1263
1290
|
sql: this.sql as SqlExec,
|
|
1264
|
-
});`,
|
|
1265
|
-
vectors,`:"",
|
|
1266
|
-
orm: bindOrm(facade),`:"",
|
|
1267
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1268
|
-
`:"",
|
|
1291
|
+
});`,tn=G?`
|
|
1292
|
+
vectors,`:"",nn=je?`
|
|
1293
|
+
orm: bindOrm(facade),`:"",et=Be?"config.hyperdriveGlobal":"config.d1",rn=ye?` const globalRequest = { bookmark: this.getInboundBookmark(), identity, onBookmark: (bookmarkValue: string | undefined) => { this.setOutboundBookmark(bookmarkValue); }, userId };
|
|
1294
|
+
const globalDb: DatabaseWriterLike = ${et}?.(env, globalRequest) ?? globalDbStub;
|
|
1295
|
+
`:"",on=L&&ye?`
|
|
1269
1296
|
protected override async readGlobalShapeRows(resolved: { columns?: readonly string[]; effectiveWhere?: WhereInput; global?: boolean; table: string }, identity?: { identity?: Record<string, unknown>; userId?: string }): Promise<Array<{ doc: Record<string, unknown>; id: string }>> {
|
|
1270
1297
|
const env = this.env as Record<string, unknown>;
|
|
1271
1298
|
// A shape read performs no writes, so the widened request only needs
|
|
1272
1299
|
// the inbound bookmark (pin the membership drain to the caller's own
|
|
1273
1300
|
// prior writes) — no \`onBookmark\` here.
|
|
1274
1301
|
const globalRequest = { ...identity, bookmark: this.getInboundBookmark() };
|
|
1275
|
-
const globalDb: DatabaseWriterLike = ${
|
|
1302
|
+
const globalDb: DatabaseWriterLike = ${et}?.(env, globalRequest) ?? globalDbStub;
|
|
1276
1303
|
const rows: Array<{ doc: Record<string, unknown>; id: string }> = [];
|
|
1277
1304
|
|
|
1278
1305
|
let cursor: null | string = null;
|
|
@@ -1299,10 +1326,17 @@ ${Jt}
|
|
|
1299
1326
|
|
|
1300
1327
|
return rows;
|
|
1301
1328
|
}
|
|
1302
|
-
`:"",
|
|
1329
|
+
`:"",an=z?`
|
|
1303
1330
|
const sourceClientCache = new WeakMap<object, Map<string, SourceClientLike>>();
|
|
1304
1331
|
const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
1305
|
-
`:"",
|
|
1332
|
+
`:"",sn=`
|
|
1333
|
+
protected override async runShardInit(): Promise<void> {
|
|
1334
|
+
this.ensureMigrated();
|
|
1335
|
+
${ze?` clearMemoryTables(this.sql as SqlExec, schema as unknown as SchemaLike);
|
|
1336
|
+
`:""}
|
|
1337
|
+
await this.dispatchShardInit();
|
|
1338
|
+
}
|
|
1339
|
+
`,ln=z?`
|
|
1306
1340
|
protected override async pollExternalSources(trace?: TraceRefLike): Promise<number | undefined> {
|
|
1307
1341
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
1308
1342
|
const sourced = Object.entries((schema as unknown as SchemaLike).tables)
|
|
@@ -1437,7 +1471,7 @@ const sourcePollAtCache = new WeakMap<object, Map<string, number>>();
|
|
|
1437
1471
|
|
|
1438
1472
|
return nextDueAt;
|
|
1439
1473
|
}
|
|
1440
|
-
`:"",
|
|
1474
|
+
`:"",tt=Ye.length>0,cn=z||tt?`
|
|
1441
1475
|
public constructor(state: ShardDOState, env: unknown) {
|
|
1442
1476
|
super(state, env);
|
|
1443
1477
|
${z?`
|
|
@@ -1450,27 +1484,27 @@ ${z?`
|
|
|
1450
1484
|
if (autoSourced) {
|
|
1451
1485
|
void this.scheduleSourcePoll();
|
|
1452
1486
|
}
|
|
1453
|
-
`:""}${
|
|
1487
|
+
`:""}${tt?`
|
|
1454
1488
|
if (LUNORA_TTL_SWEEPS.length > 0) {
|
|
1455
1489
|
void this.scheduleTtlSweep();
|
|
1456
1490
|
}
|
|
1457
1491
|
`:""} }
|
|
1458
|
-
`:"",
|
|
1492
|
+
`:"",dn=je?`
|
|
1459
1493
|
const facade = db as unknown as Record<string, ReturnType<typeof bindTableFacade>>;
|
|
1460
|
-
${k.tables.map(
|
|
1494
|
+
${k.tables.map(b=>` facade[${JSON.stringify(b.name)}] = bindTableFacade(db, ${JSON.stringify(b.name)});`).join(`
|
|
1461
1495
|
`)}
|
|
1462
|
-
`:"",
|
|
1496
|
+
`:"",un=`${ce.build}${V.build}${P.build}${J.build}${de.build}
|
|
1463
1497
|
const secrets = createSecrets(env);
|
|
1464
|
-
`,
|
|
1465
|
-
secrets,`,
|
|
1498
|
+
`,pn=`${ce.contextField}${V.contextField}${P.contextField}${Z.contextField}${J.contextField}${de.contextField}
|
|
1499
|
+
secrets,`,mn=Z.build,nt=y||h||d||$||w||x,hn=nt?`
|
|
1466
1500
|
// ActionCtx-only helpers (external, non-deterministic I/O): constructed
|
|
1467
1501
|
// and attached only for an \`action\` so query/mutation ctx never carry them.
|
|
1468
1502
|
if (isAction) {
|
|
1469
|
-
${U.build}${K.build}${Q.build}${q.build}${ee.build}${m}${[...
|
|
1503
|
+
${U.build}${K.build}${Q.build}${q.build}${ee.build}${m}${[...y?[" ctx.images = images;"]:[],...h?[" ctx.sql = sql;"]:[],...d?[" ctx.browser = browser;"]:[],...$?[" ctx.r2sql = r2sql;"]:[],...w?[" ctx.pipelines = pipelines;"]:[],...x?[" ctx.x402 = x402;"]:[]].join(`
|
|
1470
1504
|
`)}
|
|
1471
1505
|
}
|
|
1472
|
-
`:"",
|
|
1473
|
-
`:"";return`${
|
|
1506
|
+
`:"",gn=nt?` const isAction = LUNORA_FUNCTIONS[options.functionPath ?? ""]?.kind === "action";
|
|
1507
|
+
`:"";return`${S}${be.join(`
|
|
1474
1508
|
`)}
|
|
1475
1509
|
|
|
1476
1510
|
type FunctionKind = "action" | "mutation" | "query";
|
|
@@ -1480,50 +1514,50 @@ interface FunctionReference {
|
|
|
1480
1514
|
}
|
|
1481
1515
|
|
|
1482
1516
|
/** Foreign-key columns per table (\`v.id("target")\` fields) for the data browser. */
|
|
1483
|
-
const LUNORA_TABLE_REFS: Record<string, Record<string, string>> = ${JSON.stringify(
|
|
1517
|
+
const LUNORA_TABLE_REFS: Record<string, Record<string, string>> = ${JSON.stringify(Lt,void 0,4)};
|
|
1484
1518
|
|
|
1485
1519
|
/** Declared indexes per table (secondary, search, geo, rank, vector) for the schema viewer. */
|
|
1486
|
-
const LUNORA_TABLE_INDEXES: Record<string, Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }>> = ${JSON.stringify(
|
|
1520
|
+
const LUNORA_TABLE_INDEXES: Record<string, Array<{ fields: string[]; name: string; type: "geo" | "index" | "rank" | "search" | "vector"; unique?: boolean }>> = ${JSON.stringify(_t,void 0,4)};
|
|
1487
1521
|
|
|
1488
1522
|
/** Columns per table (typed, with PK/FK markers) for the studio's schema diagram, served via \`__lunora_admin__:describeTable\`. */
|
|
1489
1523
|
const LUNORA_TABLE_COLUMNS: Record<
|
|
1490
1524
|
string,
|
|
1491
1525
|
Array<{ bucket?: string; enumValues?: string[]; isStorage?: boolean; name: string; nullable?: boolean; optional: boolean; pk?: boolean; ref?: string; type: string }>
|
|
1492
|
-
> = ${JSON.stringify(
|
|
1526
|
+
> = ${JSON.stringify(Et,void 0,4)};
|
|
1493
1527
|
|
|
1494
1528
|
/** Storage-key columns per table (\`v.storage(...)\` fields) for the file browser's records↔files join. */
|
|
1495
|
-
const LUNORA_STORAGE_COLUMNS: Record<string, string[]> = ${JSON.stringify(
|
|
1529
|
+
const LUNORA_STORAGE_COLUMNS: Record<string, string[]> = ${JSON.stringify(Dt,void 0,4)};
|
|
1496
1530
|
|
|
1497
1531
|
/** Declarative TTL policies (\`.ttl(field, { after? })\`) the DO alarm sweep auto-expires rows for. */
|
|
1498
|
-
const LUNORA_TTL_SWEEPS: Array<{ after?: number; field: string; softDeleteField?: string; table: string }> = ${JSON.stringify(
|
|
1532
|
+
const LUNORA_TTL_SWEEPS: Array<{ after?: number; field: string; softDeleteField?: string; table: string }> = ${JSON.stringify(Ye,void 0,4)};
|
|
1499
1533
|
|
|
1500
1534
|
/** Static schema advisories (computed by @lunora/advisor at codegen time) served via \`__lunora_admin__:getAdvisories\`. */
|
|
1501
|
-
const LUNORA_ADVISORIES: AdvisoryFinding[] = ${JSON.stringify(
|
|
1535
|
+
const LUNORA_ADVISORIES: AdvisoryFinding[] = ${JSON.stringify(vt,void 0,4)};
|
|
1502
1536
|
|
|
1503
1537
|
/** Every declared procedure (discovered by @lunora/codegen) served via \`__lunora_admin__:getAdvisorProcedures\` — the health map's denominator. */
|
|
1504
|
-
const LUNORA_ADVISOR_PROCEDURES: AdvisorProcedure[] = ${JSON.stringify(
|
|
1538
|
+
const LUNORA_ADVISOR_PROCEDURES: AdvisorProcedure[] = ${JSON.stringify($t,void 0,4)};
|
|
1505
1539
|
|
|
1506
1540
|
/** Read-only RLS metadata (policies + roles discovered from \`.use(rls(...))\` chains) served via \`__lunora_admin__:rlsPolicies\` for the studio's RLS inspector. */
|
|
1507
|
-
const LUNORA_RLS_METADATA: RlsPoliciesResult = ${JSON.stringify(
|
|
1541
|
+
const LUNORA_RLS_METADATA: RlsPoliciesResult = ${JSON.stringify(Rt,void 0,4)};
|
|
1508
1542
|
|
|
1509
1543
|
/** 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. */
|
|
1510
|
-
const LUNORA_MASK_METADATA: MaskPoliciesResult = ${JSON.stringify(
|
|
1544
|
+
const LUNORA_MASK_METADATA: MaskPoliciesResult = ${JSON.stringify(St,void 0,4)};
|
|
1511
1545
|
|
|
1512
1546
|
/** Read-only storage access-rule metadata (discovered from \`.use(storageRules(...))\` chains) served via \`__lunora_admin__:storageRules\` for the studio's access-rules view. */
|
|
1513
|
-
const LUNORA_STORAGE_RULES: StorageRulesResult = ${JSON.stringify(
|
|
1547
|
+
const LUNORA_STORAGE_RULES: StorageRulesResult = ${JSON.stringify(Nt,void 0,4)};
|
|
1514
1548
|
|
|
1515
1549
|
/** 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. */
|
|
1516
|
-
const LUNORA_STUDIO_FEATURES: StudioFeaturesResult = ${JSON.stringify(
|
|
1517
|
-
${j}${H.constant}${
|
|
1550
|
+
const LUNORA_STUDIO_FEATURES: StudioFeaturesResult = ${JSON.stringify(Tt,void 0,4)};
|
|
1551
|
+
${j}${H.constant}${Mt}${At}${It}${pe}${_e}${ue}${De}
|
|
1518
1552
|
export interface ShardDOConfig {
|
|
1519
1553
|
/** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
|
|
1520
1554
|
cdc?: boolean;
|
|
1521
1555
|
/** Optional telemetry sink. When supplied, each \`ctx.log.*\` call is forwarded to \`sink.onLog\`. Pass the SAME sink you give \`createWorker({ observability })\` (which drives \`onRpc\`) to route both RPC and log events. */
|
|
1522
1556
|
observability?: (env: Record<string, unknown>) => TelemetrySink | undefined;
|
|
1523
1557
|
scheduler?: (env: Record<string, unknown>) => unknown;
|
|
1524
|
-
storage?: (env: Record<string, unknown>) => unknown;${
|
|
1558
|
+
storage?: (env: Record<string, unknown>) => unknown;${jt}${Se}${V.configField}${P.configField}${J.configField}${U.configField}${K.configField}${Q.configField}${q.configField}${ee.configField}${Pe}${Me}${qt}${Wt}${Ut}
|
|
1525
1559
|
}
|
|
1526
|
-
${
|
|
1560
|
+
${O("schedulerStub",Vt,["cancel","runAfter","runAt"])}${O("storageStub",Ht,["delete","download","getMetadata","getSignedUrl","getUrl","list","upload"],{sync:["getUrl"]})}${Jt}${an}${Gt}${Te}${V.stub}${P.stub}${J.stub}${U.stub}${K.stub}${Q.stub}${q.stub}${ee.stub}${He}${xt}
|
|
1527
1561
|
// Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
|
|
1528
1562
|
// referencing call fails loudly with a clear error instead of overflowing the
|
|
1529
1563
|
// stack. Tracked across the awaited handler chain (one DO invocation is
|
|
@@ -1560,7 +1594,7 @@ const dispatchRun = async (expected: FunctionKind, functionPath: string, args: R
|
|
|
1560
1594
|
* from the worker entry so wrangler binds it by name.
|
|
1561
1595
|
*/
|
|
1562
1596
|
export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOState, env: unknown) => ShardDOBase =>
|
|
1563
|
-
class extends ShardDOBase {${
|
|
1597
|
+
class extends ShardDOBase {${cn}
|
|
1564
1598
|
private migrated = false;
|
|
1565
1599
|
|
|
1566
1600
|
public override async handleRpc(functionPath: string, args: Record<string, unknown>, headroom?: TransactionHeadroomTracker): Promise<unknown> {
|
|
@@ -1582,7 +1616,7 @@ export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOSt
|
|
|
1582
1616
|
// The main \`/rpc\` path always supplies one; \`dispatchLifecycle\` /
|
|
1583
1617
|
// \`handleRunAs\` don't mint their own and omit it, so they keep the
|
|
1584
1618
|
// prior fallback behavior unchanged.
|
|
1585
|
-
const ctx = this.buildCtx({ functionPath, headroom });
|
|
1619
|
+
const ctx = this.buildCtx({ functionPath, headroom, trusted: registered.lifecycle === "init" });
|
|
1586
1620
|
|
|
1587
1621
|
// A mutation's writes must commit all-or-nothing: wrap its dispatch in
|
|
1588
1622
|
// the DO's BEGIN/COMMIT span so any throw (a validator, an RLS denial,
|
|
@@ -1618,7 +1652,7 @@ export const createShardDO = (config: ShardDOConfig = {}): new (state: ShardDOSt
|
|
|
1618
1652
|
protected override isMutationFunction(functionPath: string): boolean {
|
|
1619
1653
|
return LUNORA_FUNCTIONS[functionPath]?.kind === "mutation";
|
|
1620
1654
|
}
|
|
1621
|
-
${
|
|
1655
|
+
${Ze.override}
|
|
1622
1656
|
protected override async executeSubscription(functionPath: string, args: Record<string, unknown>, identity?: { identity?: Record<string, unknown>; userId?: string }): Promise<{ ranges?: Map<string, KeyRange[]>; result: unknown; tables: Set<string> } | null> {
|
|
1623
1657
|
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
1624
1658
|
|
|
@@ -1654,10 +1688,47 @@ ${Xe.override}
|
|
|
1654
1688
|
iterator: (signal) => (registered.handler as (context: unknown, args: Record<string, unknown>, signal: AbortSignal) => AsyncIterable<unknown>)(this.buildCtx({ functionPath }), args, signal),
|
|
1655
1689
|
};
|
|
1656
1690
|
}
|
|
1657
|
-
${
|
|
1658
|
-
protected override lifecycleHookPaths(event: "connect" | "disconnect"): readonly string[] {
|
|
1691
|
+
${Pt}${Ft}${on}${ln}
|
|
1692
|
+
protected override lifecycleHookPaths(event: "connect" | "disconnect" | "init" | "reactor"): readonly string[] {
|
|
1659
1693
|
return LUNORA_LIFECYCLE_HOOKS[event];
|
|
1660
1694
|
}
|
|
1695
|
+
${sn}
|
|
1696
|
+
// One \`onQueryChange\` dispatch. Mirrors \`executeSubscription\` — the
|
|
1697
|
+
// socket-terminated half of the same reactivity — and differs only in who
|
|
1698
|
+
// consumes the result: the base's \`dispatchReactors\` stores the digest as
|
|
1699
|
+
// the new baseline and the footprint as the "should I even re-run this"
|
|
1700
|
+
// gate, instead of pushing a frame down a socket.
|
|
1701
|
+
//
|
|
1702
|
+
// Wrapped in \`runInTransaction\` because a reactor handler is a mutation
|
|
1703
|
+
// and its writes must commit all-or-nothing. Safe to open here: the refresh
|
|
1704
|
+
// drain runs OUTSIDE any dispatch transaction (it is post-flush background
|
|
1705
|
+
// work), so this never nests.
|
|
1706
|
+
//
|
|
1707
|
+
// NO identity is threaded, and the ctx is built \`trusted\`. A reactor fires
|
|
1708
|
+
// because data moved, not because anyone asked, so there is no user for RLS
|
|
1709
|
+
// to scope to — it runs in the same system tier as migrations and the
|
|
1710
|
+
// external-source poll loop. Inheriting the shared per-request identity
|
|
1711
|
+
// would instead run an app's reactor as whichever user happened to write
|
|
1712
|
+
// last, which is worse than running it as nobody.
|
|
1713
|
+
protected override async runReactor(functionPath: string, previousDigest?: string): Promise<{ digest: string; ran: boolean; tables: readonly string[] } | undefined> {
|
|
1714
|
+
const registered = LUNORA_FUNCTIONS[functionPath];
|
|
1715
|
+
|
|
1716
|
+
if (!registered || registered.kind !== "mutation") {
|
|
1717
|
+
return undefined;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
this.ensureMigrated();
|
|
1721
|
+
|
|
1722
|
+
const footprint = createReadFootprint();
|
|
1723
|
+
const ctx = this.buildCtx({ functionPath, headroom: this.subscriptionHeadroom(), onRead: footprint.onRead, onReadRange: footprint.onReadRange, trusted: true });
|
|
1724
|
+
const outcome = (await this.runInTransaction(async () => registered.handler(ctx, { previousDigest } as unknown as Record<string, unknown>))) as {
|
|
1725
|
+
digest: string;
|
|
1726
|
+
ran: boolean;
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
return { digest: outcome.digest, ran: outcome.ran, tables: [...footprint.tables] };
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1661
1732
|
|
|
1662
1733
|
protected override tableRefs(table: string): Record<string, string> | undefined {
|
|
1663
1734
|
return LUNORA_TABLE_REFS[table];
|
|
@@ -1696,7 +1767,7 @@ ${Ft}${Dt}${rn}${an}
|
|
|
1696
1767
|
protected override studioFeatures(): StudioFeaturesResult {
|
|
1697
1768
|
return LUNORA_STUDIO_FEATURES;
|
|
1698
1769
|
}
|
|
1699
|
-
${H.evaluateOverride}${H.subscriptionOverride}${
|
|
1770
|
+
${H.evaluateOverride}${H.subscriptionOverride}${Ot}${Ct}
|
|
1700
1771
|
protected override advisories(): AdvisoryFinding[] {
|
|
1701
1772
|
return LUNORA_ADVISORIES;
|
|
1702
1773
|
}
|
|
@@ -1920,7 +1991,7 @@ ${Y}
|
|
|
1920
1991
|
this.migrated = true;
|
|
1921
1992
|
}
|
|
1922
1993
|
|
|
1923
|
-
private buildCtx(options: { functionPath?: string; headroom?: TransactionHeadroomTracker; identity?: { identity?: Record<string, unknown>; userId?: string }; onRead?: (table: string, idOrScan?: string) => void; onReadRange?: (range: KeyRange) => void } = {}): unknown {
|
|
1994
|
+
private buildCtx(options: { functionPath?: string; headroom?: TransactionHeadroomTracker; identity?: { identity?: Record<string, unknown>; userId?: string }; onRead?: (table: string, idOrScan?: string) => void; onReadRange?: (range: KeyRange) => void; trusted?: boolean } = {}): unknown {
|
|
1924
1995
|
const env = (this.env ?? {}) as Record<string, unknown>;
|
|
1925
1996
|
// When the caller threads an explicit identity (subscription seed /
|
|
1926
1997
|
// refresh — both run in deferred/interleaved contexts), use it by
|
|
@@ -1929,7 +2000,7 @@ ${Y}
|
|
|
1929
2000
|
// dispatch path) fall back to the per-request fields as before.
|
|
1930
2001
|
const userId = options.identity ? options.identity.userId : this.getCurrentUserId();
|
|
1931
2002
|
const identity = options.identity ? options.identity.identity : this.getCurrentIdentity();
|
|
1932
|
-
${
|
|
2003
|
+
${Zt}${le}${un}${Oe}${Ce}${Ae}${Ee}
|
|
1933
2004
|
const scheduler = (config.scheduler?.(env) ?? schedulerStub) as SchedulerLike;
|
|
1934
2005
|
// Build the storage adapter once and share it between \`ctx.storage\`
|
|
1935
2006
|
// and \`ctx.db.system._storage\` so both read the same R2 binding. The
|
|
@@ -1956,13 +2027,13 @@ ${Xt}${le}${cn}${Oe}${Ce}${Ae}${Ee}
|
|
|
1956
2027
|
// so both write to the same trace.
|
|
1957
2028
|
const span = this.makeDispatchSpan(traceAnchor, observability);
|
|
1958
2029
|
|
|
1959
|
-
${
|
|
2030
|
+
${rn} // \`ctx.db\`, wrapped in automatic instrumentation: by default this
|
|
1960
2031
|
// adds aggregate counters (call count, total time, per-operation
|
|
1961
2032
|
// breakdown) to the wide event rather than a span per call, so a
|
|
1962
2033
|
// handler making hundreds of queries stays readable. See
|
|
1963
2034
|
// \`instrumentDatabase\` for the \`"spans"\` / \`"off"\` levels.
|
|
1964
|
-
const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${
|
|
1965
|
-
${
|
|
2035
|
+
const db: DatabaseWriterLike = this.instrumentDb(createShardCtxDb(${en}), logFunctionPath, traceAnchor, observability);
|
|
2036
|
+
${dn}${Fe}
|
|
1966
2037
|
|
|
1967
2038
|
// \`ctx.trace\` / \`ctx.metrics\`: spans and measurements to the same sink.
|
|
1968
2039
|
// The trace anchor is threaded explicitly for the same reason \`identity\`
|
|
@@ -1970,7 +2041,7 @@ ${ln}${Fe}
|
|
|
1970
2041
|
// writing mutation's trace from the shared per-request field.
|
|
1971
2042
|
const trace = this.makeTracer(logFunctionPath, observability, traceAnchor);
|
|
1972
2043
|
const metrics = this.makeMetrics(logFunctionPath, observability);
|
|
1973
|
-
${
|
|
2044
|
+
${mn}
|
|
1974
2045
|
// \`ctx.now\`: the wall-clock instant (epoch ms) this function began,
|
|
1975
2046
|
// captured ONCE so the whole handler body sees a single stable value.
|
|
1976
2047
|
// Query/mutation handlers must be deterministic (they may be re-run on
|
|
@@ -1993,35 +2064,53 @@ ${un}
|
|
|
1993
2064
|
ip: this.getCurrentIp(),
|
|
1994
2065
|
log,
|
|
1995
2066
|
metrics,
|
|
1996
|
-
now,${
|
|
2067
|
+
now,${nn}
|
|
1997
2068
|
scheduler,
|
|
1998
2069
|
span,
|
|
1999
2070
|
storage,
|
|
2000
|
-
trace,${
|
|
2071
|
+
trace,${tn}${Ne}${pn}${ge}${Ie}${Le}${we}${me}
|
|
2001
2072
|
};
|
|
2002
|
-
${
|
|
2073
|
+
${gn}${hn}
|
|
2003
2074
|
ctx.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, ctx);
|
|
2004
2075
|
ctx.runMutation = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("mutation", reference.__lunoraRef, fnArgs, ctx);
|
|
2005
|
-
ctx.runQuery
|
|
2076
|
+
// \`ctx.runQuery(ref, args, { untracked: true })\` runs the sub-query on
|
|
2077
|
+
// its OWN context, built without the read-footprint hooks — so its
|
|
2078
|
+
// reads never enter this subscription's footprint and a write to the
|
|
2079
|
+
// tables it touched does not re-run us. Everything else is inherited:
|
|
2080
|
+
// \`functionPath\` (log/metric attribution), \`headroom\` (the sub-query
|
|
2081
|
+
// must not escape this dispatch's resource ceiling), and — load-bearing
|
|
2082
|
+
// — the identity BY VALUE. Omitting identity would let \`buildCtx\` fall
|
|
2083
|
+
// back to the shared per-request fields, which a concurrent RPC may
|
|
2084
|
+
// have re-set, and an RLS-scoped sub-query would then read as the wrong
|
|
2085
|
+
// user. A tracked call keeps sharing \`ctx\` exactly as before.
|
|
2086
|
+
ctx.runQuery = (reference: FunctionReference, fnArgs: Record<string, unknown>, runOptions?: { untracked?: boolean }) =>
|
|
2087
|
+
dispatchRun(
|
|
2088
|
+
"query",
|
|
2089
|
+
reference.__lunoraRef,
|
|
2090
|
+
fnArgs,
|
|
2091
|
+
runOptions?.untracked === true
|
|
2092
|
+
? this.buildCtx({ functionPath: options.functionPath, headroom: options.headroom, identity: { identity, userId } })
|
|
2093
|
+
: ctx,
|
|
2094
|
+
);
|
|
2006
2095
|
|
|
2007
2096
|
return ctx;
|
|
2008
2097
|
}
|
|
2009
2098
|
};
|
|
2010
|
-
`},Ve=e=>{switch(e.kind){case"array":case"object":case"record":case"union":return{builder:"text",mode:"json",notNull:!0,typeAnnotation:
|
|
2099
|
+
`},Ve=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?Ve(e.inner):{builder:"text",notNull:!1},notNull:!1};case"storage":case"string":return{builder:"text",notNull:!0};default:return{builder:"text",notNull:!0}}},kt=(e,t)=>{const n=re(e);return n.kind==="id"&&n.tableName!==void 0&&t.has(n.tableName)?n.tableName:void 0},Lr=(e,t,n)=>{g(e,"drizzle column name");const r=Ve(t),o=r.mode?`, { mode: "${r.mode}" }`:"";let s=`${r.builder}("${e}"${o})`;r.typeAnnotation&&(s+=`.$type<${r.typeAnnotation}>()`);const i=kt(t,n);return i!==void 0&&(s+=`.references((): AnySQLiteColumn => ${i}._id)`),r.notNull&&(s+=".notNull()"),s},_r=e=>{g(e.name,"drizzle index name");const t=e.unique?"uniqueIndex":"index",n=e.fields.map(r=>(g(r,"drizzle index field"),`t.${r}`)).join(", ");return` ${e.name}: ${t}("${e.name}").on(${n}),`},Er=(e,t)=>{g(e.name,"table name");const n=[' _id: text("_id").primaryKey(),',' _creationTime: integer("_creationTime").notNull(),',...Object.entries(e.shape).map(([o,s])=>` ${o}: ${Lr(o,s,t)},`)].join(`
|
|
2011
2100
|
`),r=e.indexes.length>0?`, (t) => ({
|
|
2012
|
-
${e.indexes.map(o=>
|
|
2101
|
+
${e.indexes.map(o=>_r(o)).join(`
|
|
2013
2102
|
`)}
|
|
2014
2103
|
})`:"";return`export const ${e.name} = sqliteTable("${e.name}", {
|
|
2015
2104
|
${n}
|
|
2016
|
-
}${r});`},
|
|
2017
|
-
`;const n=ne(t),r=new Set(e.map(h=>h.name)),{columns:o,indexes:s,needsAnyColumn:i,needsId:a}=
|
|
2105
|
+
}${r});`},Dr=e=>e.typeAnnotation?.includes('Id<"')??!1,Fr=(e,t)=>{const n=new Set(["integer","sqliteTable","text"]),r=new Set;let o=!1,s=!1;for(const i of e){for(const a of Object.values(i.shape)){const c=Ve(a);n.add(c.builder),s=s||Dr(c),o=o||kt(a,t)!==void 0}for(const a of i.indexes)r.add(a.unique?"uniqueIndex":"index")}return{columns:[...n].toSorted((i,a)=>i.localeCompare(a)),indexes:[...r].toSorted((i,a)=>i.localeCompare(a)),needsAnyColumn:o,needsId:s}},ot=(e,t=!1)=>{if(e.length===0)return`${S}export {};
|
|
2106
|
+
`;const n=ne(t),r=new Set(e.map(h=>h.name)),{columns:o,indexes:s,needsAnyColumn:i,needsId:a}=Fr(e,r),c=[...s,...o].toSorted((h,y)=>h.localeCompare(y)),d=e.map(h=>Er(h,r)).join(`
|
|
2018
2107
|
|
|
2019
2108
|
`),p=[i?`import type { AnySQLiteColumn } from "${n.serverDrizzle}";
|
|
2020
2109
|
`:"",a?`import type { Id } from "./dataModel.js";
|
|
2021
|
-
`:""].join("");return`${
|
|
2110
|
+
`:""].join("");return`${S}import { ${c.join(", ")} } from "${n.serverDrizzle}";
|
|
2022
2111
|
${p}
|
|
2023
2112
|
${d}
|
|
2024
|
-
`},
|
|
2113
|
+
`},oo=(e,t=!1)=>{const n=e.tables.filter(o=>o.shardMode==="global"),r=e.tables.filter(o=>o.shardMode!=="global");return{global:D(ot(n,t),Ue),shard:D(ot(r,t),Ue)}},ao=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(`
|
|
2025
2114
|
`),r=n.length>0?`
|
|
2026
2115
|
${n}
|
|
2027
2116
|
`:"",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(`
|
|
@@ -2030,7 +2119,7 @@ ${c}
|
|
|
2030
2119
|
],`}).join(`
|
|
2031
2120
|
`),s=o.length>0?`
|
|
2032
2121
|
${o}
|
|
2033
|
-
`:"";return`${
|
|
2122
|
+
`:"";return`${S}/**
|
|
2034
2123
|
* One scheduled cron invocation. Exactly one target is set: \`functionPath\` is
|
|
2035
2124
|
* the \`namespace:fn\` dispatch ref (matches \`__lunoraRef\`), invoked on the
|
|
2036
2125
|
* shard; \`workflow\` is a \`WORKFLOW_*\` binding name whose durable workflow is
|
|
@@ -2055,10 +2144,10 @@ export const LUNORA_CRON_TRIGGERS: ReadonlyArray<string> = [${r}];
|
|
|
2055
2144
|
* looks up \`event.cron\` here and dispatches every job in the list.
|
|
2056
2145
|
*/
|
|
2057
2146
|
export const LUNORA_CRONS: Record<string, ReadonlyArray<LunoraCronJob>> = {${s}};
|
|
2058
|
-
`},
|
|
2147
|
+
`},so=e=>{const t=[...e].toSorted((r,o)=>r.name.localeCompare(o.name)).map(r=>{const o=[`name: ${JSON.stringify(r.name)}`,`table: ${JSON.stringify(r.table)}`];return r.field!==void 0&&o.push(`field: ${JSON.stringify(r.field)}`),r.dimensions!==void 0&&o.push(`dimensions: ${JSON.stringify(r.dimensions)}`),r.metric!==void 0&&o.push(`metric: ${JSON.stringify(r.metric)}`),r.metadata!==void 0&&o.push(`metadata: ${JSON.stringify(r.metadata)}`),` { ${o.join(", ")} },`}).join(`
|
|
2059
2148
|
`),n=t.length>0?`
|
|
2060
2149
|
${t}
|
|
2061
|
-
`:"";return`${
|
|
2150
|
+
`:"";return`${S}/**
|
|
2062
2151
|
* One vector index declared in \`schema.ts\` — an inline \`.vectorize()\` column
|
|
2063
2152
|
* or a standalone \`defineVectorIndex()\`. \`field\` is the source column (absent
|
|
2064
2153
|
* for a \`select\`-derived Shape B index); \`metadata\` lists the row fields
|
|
@@ -2079,4 +2168,4 @@ export interface LunoraVectorIndex {
|
|
|
2079
2168
|
* \`createWorker({ vectorIntrospector })\` to back the studio's vector browser.
|
|
2080
2169
|
*/
|
|
2081
2170
|
export const LUNORA_VECTOR_INDEXES: ReadonlyArray<LunoraVectorIndex> = [${n}];
|
|
2082
|
-
`},
|
|
2171
|
+
`},io=e=>[...new Set(e.map(t=>t.cron))];export{Vr as A,bn as C,S as G,Mn as U,Jr as a,zr as b,Zr as c,ao as d,to as e,Hr as f,oo as g,Xr as h,Yr as i,ro as j,so as k,eo as l,io as m,rr as n,no as o,Gr as p};
|