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

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 CHANGED
@@ -258,6 +258,19 @@ interface ShapeIR {
258
258
  table?: string;
259
259
  }
260
260
  /**
261
+ * The single `defineIdentity({...})` claim contract discovered in
262
+ * `lunora/identity.ts`. Discovery is **marker-driven** (the `__lunoraIdentity`
263
+ * brand, exactly like {@link ShapeIR}) — no claim metadata is lifted here
264
+ * because the emitted `_generated/server.ts` recovers the claim *type* from the
265
+ * declaration itself (`InferIdentity` over the contract's `typeof`), and the
266
+ * runtime object (`validate`/`onInvalid`) carries the authority at the boundary.
267
+ * Exactly one per app; absent ⇒ generated output is byte-identical to today.
268
+ */
269
+ interface IdentityIR {
270
+ /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
271
+ exportName: string;
272
+ }
273
+ /**
261
274
  * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
262
275
  * (local-first sync engine, Phase 7). The emitted registry registers the
263
276
  * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
@@ -1180,6 +1193,15 @@ interface EmitServerOptions {
1180
1193
  hasPipelines?: boolean;
1181
1194
  /** A `lunora/` source uses `@lunora/bindings/r2sql` / `ctx.r2sql` — wires `ctx.r2sql` onto ActionCtx only. */
1182
1195
  hasR2sql?: boolean;
1196
+ /**
1197
+ * The single `defineIdentity(...)` claim contract declared in
1198
+ * `lunora/identity.ts` (Plan 080). When present, `ctx.auth.getIdentity()`,
1199
+ * the RLS policy `ctx.auth.identity`, and the shard-authorization hooks
1200
+ * narrow to the declared shape (recovered via `InferIdentity` over the
1201
+ * contract's `typeof`). `undefined` keeps the identity an untyped bag —
1202
+ * byte-identical to today.
1203
+ */
1204
+ identity?: IdentityIR;
1183
1205
  /** Queues declared via `defineQueue` exports — wires the typed `ctx.queues` producers onto Mutation/Action contexts. */
1184
1206
  queues?: ReadonlyArray<QueueIR>;
1185
1207
  schema?: SchemaIR;
@@ -1201,6 +1223,7 @@ declare const emitServer: ({
1201
1223
  hasPayments,
1202
1224
  hasPipelines,
1203
1225
  hasR2sql,
1226
+ identity,
1204
1227
  queues,
1205
1228
  schema,
1206
1229
  storageRuleBuckets,
@@ -1407,6 +1430,8 @@ interface EmitAppOptions {
1407
1430
  hasVectors: boolean;
1408
1431
  /** App declares Cloudflare Workflows (`defineWorkflow`) → wire `options.workflowsClient` so the studio's workflow-instance proxy can reach the CF REST API. */
1409
1432
  hasWorkflow: boolean;
1433
+ /** The single `defineIdentity(...)` contract in `lunora/identity.ts` (Plan 080) → import it as a VALUE and wire `options.identity`, so the runtime trust boundary validates every resolved identity before it becomes `ctx.auth`. `undefined` ⇒ no wiring, byte-identical output. */
1434
+ identity?: IdentityIR;
1410
1435
  /** Schema declares `.jurisdiction("…")` → pin every DO the worker reaches (shards, fan-out, scheduler, containers) to the Cloudflare data-residency jurisdiction. */
1411
1436
  jurisdiction?: JurisdictionIR;
1412
1437
  /** Project depends on the unscoped `lunorash` umbrella → import the runtime via `lunorash/runtime` instead of `@lunora/runtime`. */
package/dist/index.d.ts CHANGED
@@ -258,6 +258,19 @@ interface ShapeIR {
258
258
  table?: string;
259
259
  }
260
260
  /**
261
+ * The single `defineIdentity({...})` claim contract discovered in
262
+ * `lunora/identity.ts`. Discovery is **marker-driven** (the `__lunoraIdentity`
263
+ * brand, exactly like {@link ShapeIR}) — no claim metadata is lifted here
264
+ * because the emitted `_generated/server.ts` recovers the claim *type* from the
265
+ * declaration itself (`InferIdentity` over the contract's `typeof`), and the
266
+ * runtime object (`validate`/`onInvalid`) carries the authority at the boundary.
267
+ * Exactly one per app; absent ⇒ generated output is byte-identical to today.
268
+ */
269
+ interface IdentityIR {
270
+ /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
271
+ exportName: string;
272
+ }
273
+ /**
261
274
  * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
262
275
  * (local-first sync engine, Phase 7). The emitted registry registers the
263
276
  * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
@@ -1180,6 +1193,15 @@ interface EmitServerOptions {
1180
1193
  hasPipelines?: boolean;
1181
1194
  /** A `lunora/` source uses `@lunora/bindings/r2sql` / `ctx.r2sql` — wires `ctx.r2sql` onto ActionCtx only. */
1182
1195
  hasR2sql?: boolean;
1196
+ /**
1197
+ * The single `defineIdentity(...)` claim contract declared in
1198
+ * `lunora/identity.ts` (Plan 080). When present, `ctx.auth.getIdentity()`,
1199
+ * the RLS policy `ctx.auth.identity`, and the shard-authorization hooks
1200
+ * narrow to the declared shape (recovered via `InferIdentity` over the
1201
+ * contract's `typeof`). `undefined` keeps the identity an untyped bag —
1202
+ * byte-identical to today.
1203
+ */
1204
+ identity?: IdentityIR;
1183
1205
  /** Queues declared via `defineQueue` exports — wires the typed `ctx.queues` producers onto Mutation/Action contexts. */
1184
1206
  queues?: ReadonlyArray<QueueIR>;
1185
1207
  schema?: SchemaIR;
@@ -1201,6 +1223,7 @@ declare const emitServer: ({
1201
1223
  hasPayments,
1202
1224
  hasPipelines,
1203
1225
  hasR2sql,
1226
+ identity,
1204
1227
  queues,
1205
1228
  schema,
1206
1229
  storageRuleBuckets,
@@ -1407,6 +1430,8 @@ interface EmitAppOptions {
1407
1430
  hasVectors: boolean;
1408
1431
  /** App declares Cloudflare Workflows (`defineWorkflow`) → wire `options.workflowsClient` so the studio's workflow-instance proxy can reach the CF REST API. */
1409
1432
  hasWorkflow: boolean;
1433
+ /** The single `defineIdentity(...)` contract in `lunora/identity.ts` (Plan 080) → import it as a VALUE and wire `options.identity`, so the runtime trust boundary validates every resolved identity before it becomes `ctx.auth`. `undefined` ⇒ no wiring, byte-identical output. */
1434
+ identity?: IdentityIR;
1410
1435
  /** Schema declares `.jurisdiction("…")` → pin every DO the worker reaches (shards, fan-out, scheduler, containers) to the Cloudflare data-residency jurisdiction. */
1411
1436
  jurisdiction?: JurisdictionIR;
1412
1437
  /** Project depends on the unscoped `lunorash` umbrella → import the runtime via `lunorash/runtime` instead of `@lunora/runtime`. */
package/dist/index.mjs CHANGED
@@ -19,11 +19,11 @@ export { default as discoverSchema } from './packem_shared/discoverSchema-KYq55q
19
19
  export { SHAPES_FILENAME, discoverShapes } from './packem_shared/SHAPES_FILENAME-ChV7MqgE.mjs';
20
20
  export { default as discoverStorageRulesMetadata } from './packem_shared/discoverStorageRulesMetadata-Da8BKXcI.mjs';
21
21
  export { WORKFLOWS_FILENAME, discoverWorkflows } from './packem_shared/WORKFLOWS_FILENAME-CCisG0Vy.mjs';
22
- export { GENERATED_HEADER, emitApi, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-CI272wRm.mjs';
23
- export { emitApp } from './packem_shared/emitApp-cOSypPuO.mjs';
24
- export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-Dx5QftUn.mjs';
25
- export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-t5pV2NIN.mjs';
26
- export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-BMez-jgr.mjs';
22
+ export { GENERATED_HEADER, emitApi, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-DUkXYD5R.mjs';
23
+ export { emitApp } from './packem_shared/emitApp-DD0i1ks0.mjs';
24
+ export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-WxYTLh11.mjs';
25
+ export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-CjBUaolx.mjs';
26
+ export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-BUcGCXi3.mjs';
27
27
  export { SCHEMA_SNAPSHOT_VERSION, SchemaSnapshotParseError, buildSchemaSnapshot, diffSchemaSnapshots, evaluateSchemaDrift, parseSchemaSnapshot, serializeSchemaSnapshot } from './packem_shared/SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
28
28
  export { schemaFromIr } from './packem_shared/schemaFromIr-DTYsLBaA.mjs';
29
29
  export { LUNORA_ERROR_CODES, validatorIrToJsonSchema } from './packem_shared/LUNORA_ERROR_CODES-CySpQPD3.mjs';
@@ -760,6 +760,7 @@ const emitServer = ({
760
760
  hasPayments = false,
761
761
  hasPipelines = false,
762
762
  hasR2sql = false,
763
+ identity,
763
764
  queues = [],
764
765
  schema,
765
766
  storageRuleBuckets = [],
@@ -885,6 +886,20 @@ ${queues.map((queue) => ` readonly ${queue.exportName}: QueueProducer<QueueBo
885
886
  }` : "";
886
887
  const queuesContextField = hasQueues ? `
887
888
  readonly queues: LunoraQueues;` : "";
889
+ const identityTypeImport = identity ? `import type { InferIdentity } from "${base.server}";
890
+ import type * as lunoraIdentityContract from "../identity.js";
891
+ ` : "";
892
+ const identityTypeBlock = identity ? `
893
+
894
+ /** 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. */
895
+ export type Identity = InferIdentity<typeof lunoraIdentityContract.${identity.exportName}>;
896
+
897
+ /** \`ctx.auth\` narrowed so \`getIdentity()\` resolves the declared {@link Identity} contract instead of the untyped claim bag. */
898
+ type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: () => Promise<Identity | null> };` : "";
899
+ const authOmit = identity ? ` | "auth"` : "";
900
+ const authContextField = identity ? `
901
+ readonly auth: NarrowedAuth;` : "";
902
+ const policyIdentityArgument = identity ? ", Identity" : "";
888
903
  const server = `${GENERATED_HEADER}import { createPolicyDsl, initLunora, v as vBase } from "${base.server}";
889
904
  import type {
890
905
  ActionBuilder,
@@ -906,11 +921,11 @@ import type {
906
921
  } from "${base.server}";
907
922
 
908
923
  import type { DataModel, DatabaseReaderFacade, DatabaseWriterFacade, Doc, Id as IdOfTable, OrmReader, OrmWriter, Relations, TableName } from "./dataModel.js";
909
- ${aiTypeImport}${paymentsTypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}
924
+ ${aiTypeImport}${paymentsTypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}${identityTypeImport}
910
925
  export type { DataModel, Doc, Id, TableName } from "./dataModel.js";
911
926
 
912
927
  /** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
913
- export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}
928
+ export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}${identityTypeBlock}
914
929
 
915
930
  /**
916
931
  * Project-typed contexts. The base contexts from \`@lunora/server\` are
@@ -939,22 +954,22 @@ type TypedTableQuery = (<T extends TableName>(table: T) => TableReader<Doc<T>>)
939
954
  */
940
955
  type TypedTableGet = <T extends TableName>(id: IdOfTable<T>) => Promise<Doc<T> | null>;
941
956
 
942
- export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"> {
957
+ export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${authOmit}> {
943
958
  readonly db: Omit<DatabaseReader, "query" | "get"> & DatabaseReaderFacade & { query: TypedTableQuery; get: TypedTableGet };
944
959
  readonly orm: OrmReader;
945
- readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}
960
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${authContextField}
946
961
  }
947
962
 
948
- export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}> {
963
+ export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}${authOmit}> {
949
964
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
950
965
  readonly orm: OrmWriter;
951
- readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${workflowsContextField}${queuesContextField}
966
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${workflowsContextField}${queuesContextField}${authContextField}
952
967
  }
953
968
 
954
- export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}> {
969
+ export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}${authOmit}> {
955
970
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
956
971
  readonly orm: OrmWriter;
957
- readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}${queuesContextField}
972
+ readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}${queuesContextField}${authContextField}
958
973
  }
959
974
 
960
975
  /**
@@ -994,7 +1009,7 @@ export const internalAction = lunoraBuilders.internalAction as unknown as Intern
994
1009
  * Runtime-identical to \`@lunora/server\`'s \`definePolicy\`; only the types narrow,
995
1010
  * so the \`rls()\` chain discovers a policy authored either way the same.
996
1011
  */
997
- export const definePolicy = createPolicyDsl<DataModel, Relations>();
1012
+ export const definePolicy = createPolicyDsl<DataModel, Relations${policyIdentityArgument}>();
998
1013
 
999
1014
  /**
1000
1015
  * The validator builder \`v\`, with \`v.id(...)\` constrained to THIS schema's
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-CI272wRm.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-DUkXYD5R.mjs';
2
2
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
3
3
  import { argsObjectSchema, LUNORA_ERROR_CODES } from './LUNORA_ERROR_CODES-CySpQPD3.mjs';
4
4
 
@@ -9,6 +9,7 @@ import { discoverContainers } from './CONTAINERS_FILENAME-DlP6YM_Q.mjs';
9
9
  import discoverCrons from './discoverCrons-Cev7RRAf.mjs';
10
10
  import { discoverFlagKeys } from './FLAGS_FILENAME-B1vE0LIo.mjs';
11
11
  import discoverHttpRoutes from './discoverHttpRoutes-CfP6cMzt.mjs';
12
+ import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
12
13
  import discoverInserts from './discoverInserts-C7zxXkUf.mjs';
13
14
  import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-Cm81kwrb.mjs';
14
15
  import discoverMigrations from './discoverMigrations-Bi5nJ0mJ.mjs';
@@ -23,10 +24,10 @@ import { discoverShapes } from './SHAPES_FILENAME-ChV7MqgE.mjs';
23
24
  import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
24
25
  import { e as enclosingExportName$1 } from './discover-ast-CT6BgBr4.mjs';
25
26
  import { discoverWorkflows } from './WORKFLOWS_FILENAME-CCisG0Vy.mjs';
26
- import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitCollections, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-CI272wRm.mjs';
27
- import { emitApp } from './emitApp-cOSypPuO.mjs';
28
- import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-Dx5QftUn.mjs';
29
- import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-t5pV2NIN.mjs';
27
+ import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitCollections, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-DUkXYD5R.mjs';
28
+ import { emitApp } from './emitApp-DD0i1ks0.mjs';
29
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-WxYTLh11.mjs';
30
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-CjBUaolx.mjs';
30
31
  import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
31
32
 
32
33
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
@@ -337,6 +338,89 @@ const buildStudioFeatures = (usage, signals) => {
337
338
  };
338
339
  };
339
340
 
341
+ const IDENTITY_FILENAME = "identity.ts";
342
+ const IDENTITY_MODULE_SPECIFIERS = /* @__PURE__ */ new Set(["@lunora/server", "lunorash/server"]);
343
+ const isDefineIdentity = (identifier) => {
344
+ const symbol = identifier.getSymbol();
345
+ if (!symbol) {
346
+ return identifier.getText() === "defineIdentity";
347
+ }
348
+ for (const declaration of symbol.getDeclarations()) {
349
+ if (!Node.isImportSpecifier(declaration)) {
350
+ continue;
351
+ }
352
+ if (!IDENTITY_MODULE_SPECIFIERS.has(declaration.getImportDeclaration().getModuleSpecifierValue())) {
353
+ return false;
354
+ }
355
+ return declaration.getNameNode().getText() === "defineIdentity";
356
+ }
357
+ return false;
358
+ };
359
+ const isIdentityNamespaceImport = (identifier) => {
360
+ const symbol = identifier.getSymbol();
361
+ if (!symbol) {
362
+ return false;
363
+ }
364
+ for (const declaration of symbol.getDeclarations()) {
365
+ if (!Node.isNamespaceImport(declaration)) {
366
+ continue;
367
+ }
368
+ const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
369
+ return importDeclaration !== void 0 && IDENTITY_MODULE_SPECIFIERS.has(importDeclaration.getModuleSpecifierValue());
370
+ }
371
+ return false;
372
+ };
373
+ const isDefineIdentityCallee = (callee) => {
374
+ if (Node.isIdentifier(callee)) {
375
+ return isDefineIdentity(callee);
376
+ }
377
+ if (Node.isPropertyAccessExpression(callee)) {
378
+ const object = callee.getExpression();
379
+ return callee.getName() === "defineIdentity" && Node.isIdentifier(object) && isIdentityNamespaceImport(object);
380
+ }
381
+ return false;
382
+ };
383
+ const identitiesFromSource = (source) => {
384
+ const identities = [];
385
+ for (const declaration of source.getVariableDeclarations()) {
386
+ if (!declaration.isExported()) {
387
+ continue;
388
+ }
389
+ const initializer = declaration.getInitializer();
390
+ if (initializer?.getKind() !== SyntaxKind.CallExpression) {
391
+ continue;
392
+ }
393
+ const callExpression = initializer;
394
+ if (!isDefineIdentityCallee(callExpression.getExpression())) {
395
+ continue;
396
+ }
397
+ const nameNode = declaration.getNameNode();
398
+ if (!Node.isIdentifier(nameNode)) {
399
+ throw diagnosticAt(nameNode, "defineIdentity exports must be plain named exports (no destructuring)");
400
+ }
401
+ identities.push({ exportName: nameNode.getText() });
402
+ }
403
+ return identities;
404
+ };
405
+ const discoverIdentity = (project, lunoraDirectory) => {
406
+ const identityPath = join(lunoraDirectory, IDENTITY_FILENAME);
407
+ if (!existsSync(identityPath)) {
408
+ return void 0;
409
+ }
410
+ const source = project.getSourceFile(identityPath) ?? project.addSourceFileAtPath(identityPath);
411
+ const identities = identitiesFromSource(source);
412
+ if (identities.length === 0) {
413
+ return void 0;
414
+ }
415
+ if (identities.length > 1) {
416
+ throw diagnosticAt(
417
+ source,
418
+ `lunora/identity.ts declares ${identities.length.toString()} defineIdentity() contracts (${identities.map((identity) => identity.exportName).join(", ")}); exactly one is allowed`
419
+ );
420
+ }
421
+ return identities[0];
422
+ };
423
+
340
424
  const isDatabaseReplaceCall = (call) => {
341
425
  const callee = call.getExpression();
342
426
  if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "replace") {
@@ -808,6 +892,7 @@ const runCodegen = (options) => {
808
892
  const migrations = discoverMigrations(project, lunoraDirectory);
809
893
  const shapes = discoverShapes(project, lunoraDirectory);
810
894
  const mutators = discoverMutators(project, lunoraDirectory);
895
+ const identity = discoverIdentity(project, lunoraDirectory);
811
896
  const workflows = discoverWorkflows(project, lunoraDirectory);
812
897
  const queues = discoverQueues(project, lunoraDirectory);
813
898
  const crons = discoverCrons(project, lunoraDirectory, workflows);
@@ -875,6 +960,7 @@ const runCodegen = (options) => {
875
960
  hasPayments,
876
961
  hasPipelines,
877
962
  hasR2sql,
963
+ identity,
878
964
  queues,
879
965
  schema,
880
966
  storageRuleBuckets: storageRulesMetadata.rules.map((rule) => rule.bucket),
@@ -943,6 +1029,11 @@ const runCodegen = (options) => {
943
1029
  hasStorage: studioFeatures.storage,
944
1030
  hasVectors: schema.vectorIndexes.length > 0,
945
1031
  hasWorkflow: workflows.length > 0,
1032
+ // The single `defineIdentity(...)` contract (Plan 080). Wires
1033
+ // `options.identity` so the runtime trust boundary validates every
1034
+ // resolved identity before it becomes `ctx.auth`; `undefined` keeps the
1035
+ // emitted app.ts byte-identical to before this feature.
1036
+ identity,
946
1037
  // Schema `.jurisdiction("…")` → pin the generated worker's DOs to the region.
947
1038
  jurisdiction: schema.jurisdiction,
948
1039
  useUmbrella,
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-CI272wRm.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-DUkXYD5R.mjs';
2
2
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
3
3
  import { LUNORA_ERROR_CODES, objectSchema, argsObjectSchema, validatorIrToJsonSchema } from './LUNORA_ERROR_CODES-CySpQPD3.mjs';
4
4
 
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-CI272wRm.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-DUkXYD5R.mjs';
2
2
 
3
3
  const LONG_TAIL = [
4
4
  ["hasAi", "ai", "ai", "Override the Workers AI binding backing `ctx.ai` (defaults to `env.AI`)."],
@@ -22,6 +22,7 @@ const LONG_TAIL = [
22
22
  ["hasVectors", "vectors", "vectors", "Wire the Vectorize index map backing `ctx.vectors`."]
23
23
  ];
24
24
  const hasAnyLongTail = (options) => LONG_TAIL.some(([flag]) => options[flag]);
25
+ const buildIdentityImports = (identity) => identity ? [`import * as lunoraIdentityContract from "../identity.js";`] : [];
25
26
  const buildAccessImports = (hasAccess, hasAuth) => hasAccess ? [
26
27
  `import type { CreateAccessResolverOptions } from "@lunora/cloudflare-access";`,
27
28
  `import { createAccessResolver${hasAuth ? ", composeResolvers" : ""} } from "@lunora/cloudflare-access";`
@@ -75,6 +76,7 @@ const buildImportLines = (options) => {
75
76
  `import type { ${[...runtimeTypeImports].toSorted((a, b) => a.localeCompare(b)).join(", ")} } from "${runtimeModule}";`,
76
77
  `import { ${runtimeValueImports} } from "${runtimeModule}";`,
77
78
  ``,
79
+ ...buildIdentityImports(options.identity),
78
80
  ...hasGlobal || hasHyperdriveGlobal ? [`import schema from "../schema.js";`] : [],
79
81
  `import { LUNORA_CRONS } from "./crons.js";`,
80
82
  `import { LUNORA_FUNCTIONS } from "./functions.js";`,
@@ -391,6 +393,11 @@ const buildWorkerOptionLines = (options) => [
391
393
  const buildBaseWorkerOptions = (options) => [
392
394
  ` cronJobs: LUNORA_CRONS,`,
393
395
  ` functions: LUNORA_FUNCTIONS,`,
396
+ // The declared `defineIdentity(...)` contract — wires the runtime trust
397
+ // boundary so `wrapResolverWithContract` validates every resolved identity
398
+ // against it before it becomes `ctx.auth`. Emitted only when the app declares
399
+ // a contract, so apps without one keep unchanged output.
400
+ ...options.identity ? [` identity: lunoraIdentityContract.${options.identity.exportName},`] : [],
394
401
  // Schema `.jurisdiction("…")` pins every DO the worker reaches to the
395
402
  // Cloudflare data-residency region. Emitted only when declared, so apps
396
403
  // without it keep the un-pinned global namespace (and unchanged output).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/codegen",
3
- "version": "1.0.0-alpha.21",
3
+ "version": "1.0.0-alpha.23",
4
4
  "description": "Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -46,10 +46,10 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@lunora/advisor": "1.0.0-alpha.11",
49
+ "@lunora/advisor": "1.0.0-alpha.13",
50
50
  "@lunora/container": "1.0.0-alpha.5",
51
51
  "@lunora/queue": "1.0.0-alpha.1",
52
- "@lunora/scheduler": "1.0.0-alpha.3",
52
+ "@lunora/scheduler": "1.0.0-alpha.4",
53
53
  "@lunora/values": "1.0.0-alpha.3",
54
54
  "@lunora/workflow": "1.0.0-alpha.4",
55
55
  "ts-morph": "^28.0.0"