@lunora/codegen 1.0.0-alpha.31 → 1.0.0-alpha.32

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
@@ -287,6 +287,18 @@ interface IdentityIR {
287
287
  exportName: string;
288
288
  }
289
289
  /**
290
+ * The single `defineEnv({...})` contract discovered in `lunora/env.ts`. Like
291
+ * {@link IdentityIR}, only the export binding is lifted — the emitted
292
+ * `_generated/server.ts` recovers the validated shape from the declaration
293
+ * itself (`ReturnType` over the accessor's `typeof`), and the generated ShardDO
294
+ * applies the same accessor to the worker `env` at ctx-build time to populate
295
+ * `ctx.env`. Exactly one per app; absent ⇒ generated output is byte-identical.
296
+ */
297
+ interface EnvIR {
298
+ /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
299
+ exportName: string;
300
+ }
301
+ /**
290
302
  * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
291
303
  * (local-first sync engine, Phase 7). The emitted registry registers the
292
304
  * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
@@ -1851,6 +1863,13 @@ declare const emitCollections: (shapes: ReadonlyArray<ShapeIR>, hasDatabase: boo
1851
1863
  interface EmitServerOptions {
1852
1864
  containers?: ReadonlyArray<ContainerIR>;
1853
1865
  /**
1866
+ * The single `defineEnv(...)` contract declared in `lunora/env.ts`. When
1867
+ * present, `ctx.env` is typed as the validated `InferEnv` shape (recovered
1868
+ * via `ReturnType` over the accessor's `typeof`). `undefined` leaves `ctx.env`
1869
+ * the base optional binding record — byte-identical to today.
1870
+ */
1871
+ env?: EnvIR;
1872
+ /**
1854
1873
  * A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access
1855
1874
  * facade (`@lunora/cloudflare-access/context`) onto every ctx. Distinct from
1856
1875
  * `emitApp`'s `hasAccess` (which gates the worker's `.access()` resolveIdentity
@@ -1894,6 +1913,7 @@ interface EmitServerOptions {
1894
1913
  }
1895
1914
  declare const emitServer: ({
1896
1915
  containers,
1916
+ env,
1897
1917
  hasAccessFacade,
1898
1918
  hasAi,
1899
1919
  hasAnalytics,
@@ -1951,6 +1971,8 @@ declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
1951
1971
  interface EmitShardOptions {
1952
1972
  advisories?: ReadonlyArray<Finding>;
1953
1973
  containers?: ReadonlyArray<ContainerIR>;
1974
+ /** The single `defineEnv(...)` contract declared in `lunora/env.ts` — applies the accessor to the worker `env` to populate `ctx.env`. */
1975
+ env?: EnvIR;
1954
1976
  /** Statically-discovered `ctx.flags.&lt;type>("key")` reads — the studio Flags page + reactive evaluation iterate these. */
1955
1977
  flagKeys?: ReadonlyArray<{
1956
1978
  key: string;
@@ -1994,6 +2016,7 @@ interface EmitShardOptions {
1994
2016
  declare const emitShard: ({
1995
2017
  advisories,
1996
2018
  containers,
2019
+ env,
1997
2020
  flagKeys,
1998
2021
  hasAccessFacade,
1999
2022
  hasAi,
package/dist/index.d.ts CHANGED
@@ -287,6 +287,18 @@ interface IdentityIR {
287
287
  exportName: string;
288
288
  }
289
289
  /**
290
+ * The single `defineEnv({...})` contract discovered in `lunora/env.ts`. Like
291
+ * {@link IdentityIR}, only the export binding is lifted — the emitted
292
+ * `_generated/server.ts` recovers the validated shape from the declaration
293
+ * itself (`ReturnType` over the accessor's `typeof`), and the generated ShardDO
294
+ * applies the same accessor to the worker `env` at ctx-build time to populate
295
+ * `ctx.env`. Exactly one per app; absent ⇒ generated output is byte-identical.
296
+ */
297
+ interface EnvIR {
298
+ /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
299
+ exportName: string;
300
+ }
301
+ /**
290
302
  * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
291
303
  * (local-first sync engine, Phase 7). The emitted registry registers the
292
304
  * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
@@ -1851,6 +1863,13 @@ declare const emitCollections: (shapes: ReadonlyArray<ShapeIR>, hasDatabase: boo
1851
1863
  interface EmitServerOptions {
1852
1864
  containers?: ReadonlyArray<ContainerIR>;
1853
1865
  /**
1866
+ * The single `defineEnv(...)` contract declared in `lunora/env.ts`. When
1867
+ * present, `ctx.env` is typed as the validated `InferEnv` shape (recovered
1868
+ * via `ReturnType` over the accessor's `typeof`). `undefined` leaves `ctx.env`
1869
+ * the base optional binding record — byte-identical to today.
1870
+ */
1871
+ env?: EnvIR;
1872
+ /**
1854
1873
  * A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access
1855
1874
  * facade (`@lunora/cloudflare-access/context`) onto every ctx. Distinct from
1856
1875
  * `emitApp`'s `hasAccess` (which gates the worker's `.access()` resolveIdentity
@@ -1894,6 +1913,7 @@ interface EmitServerOptions {
1894
1913
  }
1895
1914
  declare const emitServer: ({
1896
1915
  containers,
1916
+ env,
1897
1917
  hasAccessFacade,
1898
1918
  hasAi,
1899
1919
  hasAnalytics,
@@ -1951,6 +1971,8 @@ declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
1951
1971
  interface EmitShardOptions {
1952
1972
  advisories?: ReadonlyArray<Finding>;
1953
1973
  containers?: ReadonlyArray<ContainerIR>;
1974
+ /** The single `defineEnv(...)` contract declared in `lunora/env.ts` — applies the accessor to the worker `env` to populate `ctx.env`. */
1975
+ env?: EnvIR;
1954
1976
  /** Statically-discovered `ctx.flags.&lt;type>("key")` reads — the studio Flags page + reactive evaluation iterate these. */
1955
1977
  flagKeys?: ReadonlyArray<{
1956
1978
  key: string;
@@ -1994,6 +2016,7 @@ interface EmitShardOptions {
1994
2016
  declare const emitShard: ({
1995
2017
  advisories,
1996
2018
  containers,
2019
+ env,
1997
2020
  flagKeys,
1998
2021
  hasAccessFacade,
1999
2022
  hasAi,
package/dist/index.mjs CHANGED
@@ -19,11 +19,11 @@ export { default as discoverSchema } from './packem_shared/discoverSchema-CeXJWV
19
19
  export { SHAPES_FILENAME, discoverShapes } from './packem_shared/SHAPES_FILENAME-DOhPGi-6.mjs';
20
20
  export { default as discoverStorageRulesMetadata } from './packem_shared/discoverStorageRulesMetadata-CFHByjIl.mjs';
21
21
  export { WORKFLOWS_FILENAME, discoverWorkflows } from './packem_shared/WORKFLOWS_FILENAME-CCC_42o3.mjs';
22
- export { GENERATED_HEADER, emitApi, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-f6nMvllu.mjs';
23
- export { emitApp } from './packem_shared/emitApp-BUCrHOeI.mjs';
24
- export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-AgKWmlry.mjs';
25
- export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-u5SqDDGk.mjs';
26
- export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-Ca-TlVEc.mjs';
22
+ export { G as GENERATED_HEADER, e as emitApi, a as emitCollections, b as emitContainers, c as emitCrons, d as emitDataModel, f as emitDrizzleSchema, g as emitFunctions, h as emitServer, i as emitShard, j as emitVectors, k as emitWorkflows, l as emitWranglerCronTriggers } from './packem_shared/emit-CBwEvCXQ.mjs';
23
+ export { emitApp } from './packem_shared/emitApp-nQ2oC-8G.mjs';
24
+ export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-bp1pYSV6.mjs';
25
+ export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-4YIeMkOI.mjs';
26
+ export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-OBXQBJV0.mjs';
27
27
  export { SCHEMA_SNAPSHOT_VERSION, SchemaSnapshotParseError, buildSchemaSnapshot, diffSchemaSnapshots, evaluateSchemaDrift, parseSchemaSnapshot, serializeSchemaSnapshot } from './packem_shared/SCHEMA_SNAPSHOT_VERSION-D0ARY6rL.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';
@@ -0,0 +1,3 @@
1
+ import '@lunora/errors';
2
+ export { G as GENERATED_HEADER, m as buildStorageColumns, e as emitApi, a as emitCollections, b as emitContainers, c as emitCrons, d as emitDataModel, f as emitDrizzleSchema, g as emitFunctions, n as emitQueues, o as emitSeed, h as emitServer, i as emitShard, j as emitVectors, k as emitWorkflows, l as emitWranglerCronTriggers } from './emit-CBwEvCXQ.mjs';
3
+ import './paths-BRd6JHuF.mjs';
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-f6nMvllu.mjs';
1
+ import { G as GENERATED_HEADER } from './emit-CBwEvCXQ.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
 
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
3
  import { performance } from 'node:perf_hooks';
4
4
  import { LunoraError } from '@lunora/errors';
@@ -8,9 +8,10 @@ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall, inlin
8
8
  import discoverAuthApiCalls from './discoverAuthApiCalls-URDoTADN.mjs';
9
9
  import { discoverContainers } from './CONTAINERS_FILENAME-DjpXMqhp.mjs';
10
10
  import discoverCrons from './discoverCrons-DdZOeroL.mjs';
11
+ import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
12
+ import { C as CAPABILITIES, m as buildStorageColumns, d as emitDataModel, e as emitApi, h as emitServer, g as emitFunctions, i as emitShard, a as emitCollections, b as emitContainers, k as emitWorkflows, n as emitQueues, c as emitCrons, j as emitVectors, f as emitDrizzleSchema, o as emitSeed, l as emitWranglerCronTriggers } from './emit-CBwEvCXQ.mjs';
11
13
  import { discoverFlagKeys } from './FLAGS_FILENAME-BjpS5w08.mjs';
12
14
  import discoverHttpRoutes from './discoverHttpRoutes-CcwP-Rkr.mjs';
13
- import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
14
15
  import discoverInserts from './discoverInserts-BpFhM32L.mjs';
15
16
  import discoverMaskProcedures, { discoverMaskStrategies, discoverMaskMetadata } from './discoverMaskProcedures-DT-v8wvj.mjs';
16
17
  import discoverMigrations from './discoverMigrations-cG1idBVM.mjs';
@@ -26,10 +27,9 @@ import { discoverShapes } from './SHAPES_FILENAME-DOhPGi-6.mjs';
26
27
  import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-CFHByjIl.mjs';
27
28
  import { e as enclosingExportName$2 } from './discover-ast-CT6BgBr4.mjs';
28
29
  import { discoverWorkflows } from './WORKFLOWS_FILENAME-CCC_42o3.mjs';
29
- import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitCollections, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-f6nMvllu.mjs';
30
- import { emitApp } from './emitApp-BUCrHOeI.mjs';
31
- import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-AgKWmlry.mjs';
32
- import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-u5SqDDGk.mjs';
30
+ import { emitApp } from './emitApp-nQ2oC-8G.mjs';
31
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-bp1pYSV6.mjs';
32
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-4YIeMkOI.mjs';
33
33
  import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-D0ARY6rL.mjs';
34
34
 
35
35
  const HTTP_VERBS$1 = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
@@ -878,6 +878,89 @@ const discoverContainerOverrides = (project, lunoraDirectory) => {
878
878
  return rows;
879
879
  };
880
880
 
881
+ const ENV_FILENAME = "env.ts";
882
+ const ENV_MODULE_SPECIFIERS = /* @__PURE__ */ new Set(["@lunora/server", "lunorash/server"]);
883
+ const isDefineEnv = (identifier) => {
884
+ const symbol = identifier.getSymbol();
885
+ if (!symbol) {
886
+ return identifier.getText() === "defineEnv";
887
+ }
888
+ for (const declaration of symbol.getDeclarations()) {
889
+ if (!Node.isImportSpecifier(declaration)) {
890
+ continue;
891
+ }
892
+ if (!ENV_MODULE_SPECIFIERS.has(declaration.getImportDeclaration().getModuleSpecifierValue())) {
893
+ return false;
894
+ }
895
+ return declaration.getNameNode().getText() === "defineEnv";
896
+ }
897
+ return false;
898
+ };
899
+ const isEnvNamespaceImport = (identifier) => {
900
+ const symbol = identifier.getSymbol();
901
+ if (!symbol) {
902
+ return false;
903
+ }
904
+ for (const declaration of symbol.getDeclarations()) {
905
+ if (!Node.isNamespaceImport(declaration)) {
906
+ continue;
907
+ }
908
+ const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
909
+ return importDeclaration !== void 0 && ENV_MODULE_SPECIFIERS.has(importDeclaration.getModuleSpecifierValue());
910
+ }
911
+ return false;
912
+ };
913
+ const isDefineEnvCallee = (callee) => {
914
+ if (Node.isIdentifier(callee)) {
915
+ return isDefineEnv(callee);
916
+ }
917
+ if (Node.isPropertyAccessExpression(callee)) {
918
+ const object = callee.getExpression();
919
+ return callee.getName() === "defineEnv" && Node.isIdentifier(object) && isEnvNamespaceImport(object);
920
+ }
921
+ return false;
922
+ };
923
+ const environmentsFromSource = (source) => {
924
+ const environments = [];
925
+ for (const declaration of source.getVariableDeclarations()) {
926
+ if (!declaration.isExported()) {
927
+ continue;
928
+ }
929
+ const initializer = declaration.getInitializer();
930
+ if (initializer?.getKind() !== SyntaxKind.CallExpression) {
931
+ continue;
932
+ }
933
+ const callExpression = initializer;
934
+ if (!isDefineEnvCallee(callExpression.getExpression())) {
935
+ continue;
936
+ }
937
+ const nameNode = declaration.getNameNode();
938
+ if (!Node.isIdentifier(nameNode)) {
939
+ throw diagnosticAt(nameNode, "defineEnv exports must be plain named exports (no destructuring)");
940
+ }
941
+ environments.push({ exportName: nameNode.getText() });
942
+ }
943
+ return environments;
944
+ };
945
+ const discoverEnv = (project, lunoraDirectory) => {
946
+ const envPath = join(lunoraDirectory, ENV_FILENAME);
947
+ if (!existsSync(envPath)) {
948
+ return void 0;
949
+ }
950
+ const source = project.getSourceFile(envPath) ?? project.addSourceFileAtPath(envPath);
951
+ const environments = environmentsFromSource(source);
952
+ if (environments.length === 0) {
953
+ return void 0;
954
+ }
955
+ if (environments.length > 1) {
956
+ throw diagnosticAt(
957
+ source,
958
+ `lunora/env.ts declares ${environments.length.toString()} defineEnv() contracts (${environments.map((env) => env.exportName).join(", ")}); exactly one is allowed`
959
+ );
960
+ }
961
+ return environments[0];
962
+ };
963
+
881
964
  const OPTIONS_ARGUMENT_INDEX$1 = /* @__PURE__ */ new Map([
882
965
  ["dbRateLimit", 2],
883
966
  ["rateLimit", 2],
@@ -932,35 +1015,6 @@ const discoverFailOpenGuards = (project, lunoraDirectory) => {
932
1015
  return guards;
933
1016
  };
934
1017
 
935
- const PROBES = {
936
- // The middleware (`accessContext()` / `accessRoles()`) imports the `/context`
937
- // and `/roles` subpaths, NOT the bare `@lunora/cloudflare-access` specifier —
938
- // so the per-procedure middleware never trips the global `ctx.access` wiring.
939
- // A handler reading `ctx.access` is the signal that wires it onto every ctx.
940
- access: { contextProperty: "access", moduleSpecifier: "@lunora/cloudflare-access" },
941
- ai: { contextProperty: "ai", moduleSpecifier: "@lunora/ai" },
942
- analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/bindings/analytics" },
943
- browser: { contextProperty: "browser", moduleSpecifier: "@lunora/browser" },
944
- // `lunora/containers.ts` imports `defineContainer` from `@lunora/container`,
945
- // and handlers reach live instances via `ctx.containers` — either signals the
946
- // app wires containers, so the studio should show the Containers page.
947
- container: { contextProperty: "containers", moduleSpecifier: "@lunora/container" },
948
- flags: { contextProperty: "flags", moduleSpecifier: "@lunora/flags" },
949
- hyperdrive: { contextProperty: "sql", moduleSpecifier: "@lunora/hyperdrive" },
950
- images: { contextProperty: "images", moduleSpecifier: "@lunora/bindings/images" },
951
- kv: { contextProperty: "kv", moduleSpecifier: "@lunora/bindings/kv" },
952
- mail: { moduleSpecifier: "@lunora/mail" },
953
- payments: { contextProperty: "payments", moduleSpecifier: "@lunora/payment" },
954
- // Pipelines is its own `@lunora/bindings/pipelines` subpath (distinct from
955
- // `/analytics`), so a real import is a clean signal that won't be flipped by a
956
- // plain analytics import; `ctx.pipelines` reads flip it too.
957
- pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/bindings/pipelines" },
958
- r2sql: { contextProperty: "r2sql", moduleSpecifier: "@lunora/bindings/r2sql" },
959
- scheduler: { contextProperty: "scheduler", moduleSpecifier: "@lunora/scheduler" },
960
- storage: { contextProperty: "storage", moduleSpecifier: "@lunora/storage" },
961
- vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/bindings/vectors" },
962
- workflows: { contextProperty: "workflows", moduleSpecifier: "@lunora/workflow" }
963
- };
964
1018
  const contextPropertiesRead = (sourceFile) => {
965
1019
  const reachesContext = (receiver) => Node.isIdentifier(receiver) && receiver.getText() === "ctx";
966
1020
  const names = /* @__PURE__ */ new Set();
@@ -985,44 +1039,24 @@ const contextPropertiesRead = (sourceFile) => {
985
1039
  return names;
986
1040
  };
987
1041
  const discoverFeatureUsage = (project, lunoraDirectory) => {
988
- const usage = {
989
- access: false,
990
- ai: false,
991
- analytics: false,
992
- browser: false,
993
- container: false,
994
- flags: false,
995
- hyperdrive: false,
996
- images: false,
997
- kv: false,
998
- mail: false,
999
- payments: false,
1000
- pipelines: false,
1001
- r2sql: false,
1002
- scheduler: false,
1003
- storage: false,
1004
- vectors: false,
1005
- workflows: false
1006
- };
1007
- const keys = Object.keys(PROBES);
1042
+ const usage = Object.fromEntries(CAPABILITIES.map((capability) => [capability.key, false]));
1008
1043
  for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
1009
1044
  const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
1010
1045
  const importSpecifiers = new Set(sourceFile.getImportDeclarations().map((declaration) => declaration.getModuleSpecifierValue()));
1011
1046
  const contextProperties = contextPropertiesRead(sourceFile);
1012
- for (const key of keys) {
1013
- if (usage[key]) {
1047
+ for (const capability of CAPABILITIES) {
1048
+ if (usage[capability.key]) {
1014
1049
  continue;
1015
1050
  }
1016
- const probe = PROBES[key];
1017
- if (importSpecifiers.has(probe.moduleSpecifier)) {
1018
- usage[key] = true;
1051
+ if (importSpecifiers.has(capability.moduleSpecifier)) {
1052
+ usage[capability.key] = true;
1019
1053
  continue;
1020
1054
  }
1021
- if (probe.contextProperty !== void 0 && contextProperties.has(probe.contextProperty)) {
1022
- usage[key] = true;
1055
+ if (capability.contextProperty !== void 0 && contextProperties.has(capability.contextProperty)) {
1056
+ usage[capability.key] = true;
1023
1057
  }
1024
1058
  }
1025
- if (keys.every((key) => usage[key])) {
1059
+ if (CAPABILITIES.every((capability) => usage[capability.key])) {
1026
1060
  break;
1027
1061
  }
1028
1062
  }
@@ -3134,9 +3168,11 @@ const writeIfChanged = (filePath, content) => {
3134
3168
  writeFileSync(filePath, content, "utf8");
3135
3169
  };
3136
3170
  const writeIfPresent = (filePath, content) => {
3137
- if (content !== "") {
3138
- writeIfChanged(filePath, content);
3171
+ if (content === "") {
3172
+ rmSync(filePath, { force: true });
3173
+ return;
3139
3174
  }
3175
+ writeIfChanged(filePath, content);
3140
3176
  };
3141
3177
  const readProjectVersion = (projectRoot) => {
3142
3178
  const manifestPath = join(projectRoot, "package.json");
@@ -3210,6 +3246,7 @@ const runCodegen = (options) => {
3210
3246
  const shapes = discoverShapes(project, lunoraDirectory);
3211
3247
  const mutators = discoverMutators(project, lunoraDirectory);
3212
3248
  const identity = discoverIdentity(project, lunoraDirectory);
3249
+ const env = discoverEnv(project, lunoraDirectory);
3213
3250
  const workflows = discoverWorkflows(project, lunoraDirectory);
3214
3251
  const queues = discoverQueues(project, lunoraDirectory);
3215
3252
  const crons = discoverCrons(project, lunoraDirectory, workflows);
@@ -3296,6 +3333,7 @@ const runCodegen = (options) => {
3296
3333
  const apiContent = emitApi(functions, workflows, useUmbrella);
3297
3334
  const serverContent = emitServer({
3298
3335
  containers,
3336
+ env,
3299
3337
  hasAccessFacade,
3300
3338
  hasAi,
3301
3339
  hasAnalytics,
@@ -3318,6 +3356,7 @@ const runCodegen = (options) => {
3318
3356
  const shardContent = emitShard({
3319
3357
  advisories,
3320
3358
  containers,
3359
+ env,
3321
3360
  flagKeys,
3322
3361
  hasAccessFacade,
3323
3362
  hasAi,
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-f6nMvllu.mjs';
1
+ import { G as GENERATED_HEADER } from './emit-CBwEvCXQ.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,6 +1,170 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
3
3
 
4
+ const CAPABILITY_ROWS = [
5
+ // The middleware (`accessContext()` / `accessRoles()`) imports the `/context`
6
+ // and `/roles` subpaths, NOT the bare `@lunora/cloudflare-access` specifier —
7
+ // so the per-procedure middleware never trips the global `ctx.access` wiring.
8
+ // A handler reading `ctx.access` is the signal that wires it onto every ctx.
9
+ // `access` has a synchronous facade type, so its ctx field stays bespoke in
10
+ // `emit.ts` (no `serverCtxField` here).
11
+ { contextProperty: "access", key: "access", moduleSpecifier: "@lunora/cloudflare-access" },
12
+ {
13
+ appMethod: { configKey: "ai", doc: "Override the Workers AI binding backing `ctx.ai` (defaults to `env.AI`).", method: "ai" },
14
+ contextProperty: "ai",
15
+ key: "ai",
16
+ moduleSpecifier: "@lunora/ai"
17
+ },
18
+ {
19
+ appMethod: {
20
+ configKey: "analytics",
21
+ doc: "Override the Analytics Engine dataset backing `ctx.analytics` (defaults to `env.ANALYTICS`).",
22
+ method: "analytics"
23
+ },
24
+ contextProperty: "analytics",
25
+ key: "analytics",
26
+ moduleSpecifier: "@lunora/bindings/analytics",
27
+ // `ctx.analytics` — Analytics Engine write helper. EVERY ctx: a write-only,
28
+ // fire-and-forget side effect, not a determinism hazard for reads.
29
+ serverCtxField: {
30
+ field: `
31
+ /** Analytics Engine telemetry sink. Fire-and-forget and sampled; do not read it back in-handler. */
32
+ readonly analytics: import("@lunora/bindings/analytics").AnalyticsClient;`,
33
+ tier: "every"
34
+ }
35
+ },
36
+ {
37
+ appMethod: {
38
+ configKey: "browser",
39
+ doc: "Override the Browser Rendering binding backing `ctx.browser` (defaults to `env.BROWSER`).",
40
+ method: "browser"
41
+ },
42
+ contextProperty: "browser",
43
+ key: "browser",
44
+ moduleSpecifier: "@lunora/browser",
45
+ // `ctx.browser` — Browser Rendering. ActionCtx ONLY: non-deterministic network I/O.
46
+ serverCtxField: {
47
+ field: `
48
+ /** Browser Rendering (screenshots/PDF/scrape). Non-deterministic — available only in actions. */
49
+ readonly browser: import("@lunora/browser").Browser;`,
50
+ tier: "action"
51
+ }
52
+ },
53
+ // `lunora/containers.ts` imports `defineContainer` from `@lunora/container`,
54
+ // and handlers reach live instances via `ctx.containers` — either signals the
55
+ // app wires containers, so the studio should show the Containers page. The ctx
56
+ // field is a per-declaration emitter (kept bespoke), so no `serverCtxField`.
57
+ { contextProperty: "containers", key: "container", moduleSpecifier: "@lunora/container" },
58
+ // `ctx.flags` — OpenFeature. Umbrella-aware specifier + its own provider
59
+ // client, so both the ctx field and the shard fragment stay bespoke.
60
+ { contextProperty: "flags", key: "flags", moduleSpecifier: "@lunora/flags" },
61
+ {
62
+ appMethod: {
63
+ configKey: "sql",
64
+ doc: "Wire the Hyperdrive SQL client backing `ctx.sql` — build it with `createHyperdrive` + `fromPostgresJs`/`fromNodePg`/`fromMysql2`.",
65
+ method: "hyperdrive"
66
+ },
67
+ contextProperty: "sql",
68
+ key: "hyperdrive",
69
+ moduleSpecifier: "@lunora/hyperdrive",
70
+ // `ctx.sql` — Hyperdrive (external Postgres/MySQL). ActionCtx ONLY: external,
71
+ // non-deterministic I/O whose writes are invisible to Lunora live queries.
72
+ serverCtxField: {
73
+ field: `
74
+ /**
75
+ * External database access via Hyperdrive. Non-deterministic — available only in actions. Writes here are NOT tracked by Lunora live queries; subscriptions will not re-run on external DB changes.
76
+ */
77
+ readonly sql: import("@lunora/hyperdrive").SqlClient;`,
78
+ tier: "action"
79
+ }
80
+ },
81
+ {
82
+ appMethod: { configKey: "images", doc: "Override the Images binding backing `ctx.images` (defaults to `env.IMAGES`).", method: "images" },
83
+ contextProperty: "images",
84
+ key: "images",
85
+ moduleSpecifier: "@lunora/bindings/images",
86
+ // `ctx.images` — Cloudflare Images binding transforms. ActionCtx ONLY: non-deterministic compute/network I/O.
87
+ serverCtxField: {
88
+ field: `
89
+ /** Cloudflare Images transforms (resize/format/optimize). Non-deterministic — available only in actions. */
90
+ readonly images: import("@lunora/bindings/images").Images;`,
91
+ tier: "action"
92
+ }
93
+ },
94
+ {
95
+ appMethod: { configKey: "kv", doc: "Override the Workers KV binding backing `ctx.kv` (defaults to `env.KV`).", method: "kv" },
96
+ contextProperty: "kv",
97
+ key: "kv",
98
+ moduleSpecifier: "@lunora/bindings/kv",
99
+ // `ctx.kv` — Workers KV. Typed on EVERY ctx (a KV read is allowed in a
100
+ // deterministic read path the way `ctx.db` is; the binding is user-named).
101
+ serverCtxField: { field: `
102
+ readonly kv: import("@lunora/bindings/kv").Kv;`, tier: "every" }
103
+ },
104
+ // `mail` is import-only — no `ctx.mail` helper (mail is reached through its own
105
+ // client), so only a `@lunora/mail` import flips it.
106
+ { key: "mail", moduleSpecifier: "@lunora/mail" },
107
+ {
108
+ appMethod: { configKey: "payment", doc: "Wire the payment options backing `ctx.payments`.", method: "payment" },
109
+ contextProperty: "payments",
110
+ key: "payments",
111
+ moduleSpecifier: "@lunora/payment"
112
+ },
113
+ // Pipelines is its own `@lunora/bindings/pipelines` subpath (distinct from
114
+ // `/analytics`), so a real import is a clean signal that won't be flipped by a
115
+ // plain analytics import; `ctx.pipelines` reads flip it too.
116
+ {
117
+ contextProperty: "pipelines",
118
+ key: "pipelines",
119
+ moduleSpecifier: "@lunora/bindings/pipelines",
120
+ // `ctx.pipelines` — Pipelines (R2-backed) ingestion sink. ActionCtx ONLY
121
+ // (write-only fire-and-forget, but external I/O — kept off query/mutation).
122
+ serverCtxField: {
123
+ field: `
124
+ /** Pipelines ingestion sink (durable, R2-backed). Fire-and-forget and batched; do not read it back in-handler. */
125
+ readonly pipelines: import("@lunora/bindings/pipelines").PipelineClient;`,
126
+ tier: "action"
127
+ }
128
+ },
129
+ {
130
+ appMethod: {
131
+ configKey: "r2sql",
132
+ doc: "Wire the R2 SQL client backing `ctx.r2sql` — build it with `createR2Sql({ accountId, apiToken, bucket })` (defaults to env `R2_SQL_TOKEN` / `R2_SQL_ACCOUNT_ID` / `R2_SQL_BUCKET`).",
133
+ method: "r2sql"
134
+ },
135
+ contextProperty: "r2sql",
136
+ key: "r2sql",
137
+ moduleSpecifier: "@lunora/bindings/r2sql",
138
+ // `ctx.r2sql` — R2 SQL (serverless query engine over Apache Iceberg tables).
139
+ // ActionCtx ONLY: external REST I/O, non-deterministic, and non-reactive
140
+ // (reads are not tracked by Lunora live queries).
141
+ serverCtxField: {
142
+ field: `
143
+ /**
144
+ * 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.
145
+ */
146
+ readonly r2sql: import("@lunora/bindings/r2sql").R2SqlClient;`,
147
+ tier: "action"
148
+ }
149
+ },
150
+ { contextProperty: "scheduler", key: "scheduler", moduleSpecifier: "@lunora/scheduler" },
151
+ { contextProperty: "storage", key: "storage", moduleSpecifier: "@lunora/storage" },
152
+ {
153
+ appMethod: { configKey: "vectors", doc: "Wire the Vectorize index map backing `ctx.vectors`.", method: "vectors" },
154
+ contextProperty: "vectors",
155
+ key: "vectors",
156
+ moduleSpecifier: "@lunora/bindings/vectors"
157
+ },
158
+ { contextProperty: "workflows", key: "workflows", moduleSpecifier: "@lunora/workflow" }
159
+ ];
160
+ const CAPABILITIES = CAPABILITY_ROWS;
161
+ const SERVER_CTX_FIELDS = new Map(
162
+ CAPABILITIES.flatMap((capability) => capability.serverCtxField ? [[capability.key, capability.serverCtxField]] : [])
163
+ );
164
+ const APP_METHOD_CAPABILITIES = CAPABILITY_ROWS.flatMap(
165
+ (capability) => "appMethod" in capability ? [{ appMethod: capability.appMethod, key: capability.key }] : []
166
+ );
167
+
4
168
  const LITERAL_VALUE_RE = /^(?:"[^"\\]*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u;
5
169
 
6
170
  const PASS_THROUGH_KINDS = /* @__PURE__ */ new Set(["any", "bigint", "boolean", "date", "id", "null", "number", "storage", "string", "timestamp"]);
@@ -755,6 +919,7 @@ const buildStorageBucketNames = (schema, ruleBuckets = []) => {
755
919
  };
756
920
  const emitServer = ({
757
921
  containers = [],
922
+ env,
758
923
  hasAccessFacade = false,
759
924
  hasAi = false,
760
925
  hasAnalytics = false,
@@ -827,36 +992,20 @@ ${envBindingFields}` : ""}
827
992
 
828
993
  /** Alias for {@link CloudflareBindings} — the typed shape of \`env\`. */
829
994
  export type Env = CloudflareBindings;`;
830
- const kvContextField = hasKv ? `
831
- readonly kv: import("@lunora/bindings/kv").Kv;` : "";
995
+ const serverCapabilityField = (key, enabled) => enabled ? SERVER_CTX_FIELDS.get(key)?.field ?? "" : "";
996
+ const kvContextField = serverCapabilityField("kv", hasKv);
997
+ const analyticsContextField = serverCapabilityField("analytics", hasAnalytics);
998
+ const hyperdriveActionField = serverCapabilityField("hyperdrive", hasHyperdrive);
999
+ const browserActionField = serverCapabilityField("browser", hasBrowser);
1000
+ const imagesActionField = serverCapabilityField("images", hasImages);
1001
+ const pipelinesActionField = serverCapabilityField("pipelines", hasPipelines);
1002
+ const r2sqlActionField = serverCapabilityField("r2sql", hasR2sql);
832
1003
  const accessContextField = hasAccessFacade ? `
833
1004
  /** Verified Cloudflare Access identity — a synchronous facade over the resolved claims (email / groups / hasGroup / claims). Anonymous when no Access token is present. */
834
1005
  readonly access: import("@lunora/cloudflare-access/context").AccessFacade;` : "";
835
1006
  const flagsContextField = hasFlags ? `
836
1007
  /** Feature-flag evaluation (OpenFeature). Reads are memoized per request; evaluations never throw — a provider error resolves to the supplied default. */
837
1008
  readonly flags: import("${base.flags}").LunoraFlags;` : "";
838
- const hyperdriveActionField = hasHyperdrive ? `
839
- /**
840
- * External database access via Hyperdrive. Non-deterministic — available only in actions. Writes here are NOT tracked by Lunora live queries; subscriptions will not re-run on external DB changes.
841
- */
842
- readonly sql: import("@lunora/hyperdrive").SqlClient;` : "";
843
- const browserActionField = hasBrowser ? `
844
- /** Browser Rendering (screenshots/PDF/scrape). Non-deterministic — available only in actions. */
845
- readonly browser: import("@lunora/browser").Browser;` : "";
846
- const imagesActionField = hasImages ? `
847
- /** Cloudflare Images transforms (resize/format/optimize). Non-deterministic — available only in actions. */
848
- readonly images: import("@lunora/bindings/images").Images;` : "";
849
- const analyticsContextField = hasAnalytics ? `
850
- /** Analytics Engine telemetry sink. Fire-and-forget and sampled; do not read it back in-handler. */
851
- readonly analytics: import("@lunora/bindings/analytics").AnalyticsClient;` : "";
852
- const pipelinesActionField = hasPipelines ? `
853
- /** Pipelines ingestion sink (durable, R2-backed). Fire-and-forget and batched; do not read it back in-handler. */
854
- readonly pipelines: import("@lunora/bindings/pipelines").PipelineClient;` : "";
855
- const r2sqlActionField = hasR2sql ? `
856
- /**
857
- * 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.
858
- */
859
- readonly r2sql: import("@lunora/bindings/r2sql").R2SqlClient;` : "";
860
1009
  const hasWorkflows = workflows.length > 0;
861
1010
  const workflowsTypeImport = hasWorkflows ? `import type { WorkflowHandle } from "@lunora/workflow";
862
1011
  import type * as lunoraWorkflowDefinitions from "../workflows.js";
@@ -906,6 +1055,16 @@ type NarrowedAuth = Omit<QueryCtxBase["auth"], "getIdentity"> & { getIdentity: (
906
1055
  const authContextField = identity ? `
907
1056
  readonly auth: NarrowedAuth;` : "";
908
1057
  const policyIdentityArgument = identity ? ", Identity" : "";
1058
+ const envTypeImport = env ? `import type * as lunoraEnvContract from "../env.js";
1059
+ ` : "";
1060
+ const envTypeBlock = env ? `
1061
+
1062
+ /** This app's declared env contract (\`defineEnv\` in \`lunora/env.ts\`) — the validated, coercion-aware shape of \`ctx.env\`. */
1063
+ export type LunoraEnv = ReturnType<typeof lunoraEnvContract.${env.exportName}>;` : "";
1064
+ const envOmit = env ? ` | "env"` : "";
1065
+ const envContextField = env ? `
1066
+ /** 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. */
1067
+ readonly env: LunoraEnv;` : "";
909
1068
  const server = `${GENERATED_HEADER}import { createPolicyDsl, initLunora, v as vBase } from "${base.server}";
910
1069
  import type {
911
1070
  ActionBuilder,
@@ -927,11 +1086,11 @@ import type {
927
1086
  } from "${base.server}";
928
1087
 
929
1088
  import type { DataModel, DatabaseReaderFacade, DatabaseWriterFacade, Doc, Id as IdOfTable, OrmReader, OrmWriter, Relations, TableName } from "./dataModel.js";
930
- ${aiTypeImport}${paymentsTypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}${identityTypeImport}
1089
+ ${aiTypeImport}${paymentsTypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}${identityTypeImport}${envTypeImport}
931
1090
  export type { DataModel, Doc, Id, TableName } from "./dataModel.js";
932
1091
 
933
1092
  /** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
934
- export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}${identityTypeBlock}
1093
+ export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}${identityTypeBlock}${envTypeBlock}
935
1094
 
936
1095
  /**
937
1096
  * Project-typed contexts. The base contexts from \`@lunora/server\` are
@@ -960,22 +1119,22 @@ type TypedTableQuery = (<T extends TableName>(table: T) => TableReader<Doc<T>>)
960
1119
  */
961
1120
  type TypedTableGet = <T extends TableName>(id: IdOfTable<T>) => Promise<Doc<T> | null>;
962
1121
 
963
- export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${authOmit}> {
1122
+ export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${authOmit}${envOmit}> {
964
1123
  readonly db: Omit<DatabaseReader, "query" | "get"> & DatabaseReaderFacade & { query: TypedTableQuery; get: TypedTableGet };
965
1124
  readonly orm: OrmReader;
966
- readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${authContextField}
1125
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${envContextField}${authContextField}
967
1126
  }
968
1127
 
969
- export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}${authOmit}> {
1128
+ export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}${authOmit}${envOmit}> {
970
1129
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
971
1130
  readonly orm: OrmWriter;
972
- readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${workflowsContextField}${queuesContextField}${authContextField}
1131
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${envContextField}${workflowsContextField}${queuesContextField}${authContextField}
973
1132
  }
974
1133
 
975
- export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}${authOmit}> {
1134
+ export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}${authOmit}${envOmit}> {
976
1135
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
977
1136
  readonly orm: OrmWriter;
978
- readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}${queuesContextField}${authContextField}
1137
+ readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${envContextField}${workflowsContextField}${queuesContextField}${authContextField}
979
1138
  }
980
1139
 
981
1140
  /**
@@ -1386,6 +1545,21 @@ const emitFlagsFragments = (hasFlags, flagsSpecifier) => {
1386
1545
  stub: ""
1387
1546
  };
1388
1547
  };
1548
+ const emitEnvFragments = (env) => {
1549
+ if (!env) {
1550
+ return EMPTY_HELPER_FRAGMENTS;
1551
+ }
1552
+ return {
1553
+ build: `
1554
+ const envConfig = lunoraEnvContract.${env.exportName}(env);
1555
+ `,
1556
+ configField: "",
1557
+ contextField: `
1558
+ env: envConfig,`,
1559
+ importLines: [`import * as lunoraEnvContract from "../env.js";`],
1560
+ stub: ""
1561
+ };
1562
+ };
1389
1563
  const emitAccessFragments = (hasAccessFacade) => {
1390
1564
  if (!hasAccessFacade) {
1391
1565
  return EMPTY_HELPER_FRAGMENTS;
@@ -1996,6 +2170,7 @@ const buildDoTypeImports = (hasVectors, hasWorkflows, hasQueues, hasFlags) => [
1996
2170
  const emitShard = ({
1997
2171
  advisories = [],
1998
2172
  containers = [],
2173
+ env,
1999
2174
  flagKeys = [],
2000
2175
  hasAccessFacade = false,
2001
2176
  hasAi = false,
@@ -2027,6 +2202,7 @@ const emitShard = ({
2027
2202
  const kvFragments = emitKvFragments(hasKv);
2028
2203
  const flagsFragments = emitFlagsFragments(hasFlags, base.flags);
2029
2204
  const flagsOverrides = emitFlagsOverrides(flagKeys, hasFlags, base.flags);
2205
+ const envFragments = emitEnvFragments(env);
2030
2206
  const analyticsFragments = emitAnalyticsFragments(hasAnalytics);
2031
2207
  const imagesFragments = emitImagesFragments(hasImages);
2032
2208
  const hyperdriveFragments = emitHyperdriveFragments(hasHyperdrive);
@@ -2181,6 +2357,7 @@ const LUNORA_RLS_READ_REGISTRY = buildRlsReadRegistry(Object.values(LUNORA_FUNCT
2181
2357
  ...accessFragments.importLines,
2182
2358
  ...kvFragments.importLines,
2183
2359
  ...flagsFragments.importLines,
2360
+ ...envFragments.importLines,
2184
2361
  ...analyticsFragments.importLines,
2185
2362
  ...imagesFragments.importLines,
2186
2363
  ...hyperdriveFragments.importLines,
@@ -2476,8 +2653,8 @@ ${schema.tables.map(
2476
2653
  const secretsBuild = `
2477
2654
  const secrets = createSecrets(env);
2478
2655
  `;
2479
- const everyContextBuild = `${accessFragments.build}${kvFragments.build}${flagsFragments.build}${analyticsFragments.build}${secretsBuild}`;
2480
- const everyContextField = `${accessFragments.contextField}${kvFragments.contextField}${flagsFragments.contextField}${analyticsFragments.contextField}
2656
+ const everyContextBuild = `${accessFragments.build}${kvFragments.build}${flagsFragments.build}${analyticsFragments.build}${envFragments.build}${secretsBuild}`;
2657
+ const everyContextField = `${accessFragments.contextField}${kvFragments.contextField}${flagsFragments.contextField}${analyticsFragments.contextField}${envFragments.contextField}
2481
2658
  secrets,`;
2482
2659
  const actionOnlyHasAny = hasImages || hasHyperdrive || hasBrowser || hasR2sql || hasPipelines;
2483
2660
  const actionOnlyBlock = actionOnlyHasAny ? `
@@ -3271,4 +3448,4 @@ const emitWranglerCronTriggers = (crons) => {
3271
3448
  return triggers;
3272
3449
  };
3273
3450
 
3274
- export { GENERATED_HEADER, buildStorageColumns, emitApi, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitQueues, emitSeed, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers };
3451
+ export { APP_METHOD_CAPABILITIES as A, CAPABILITIES as C, GENERATED_HEADER as G, emitCollections as a, emitContainers as b, emitCrons as c, emitDataModel as d, emitApi as e, emitDrizzleSchema as f, emitFunctions as g, emitServer as h, emitShard as i, emitVectors as j, emitWorkflows as k, emitWranglerCronTriggers as l, buildStorageColumns as m, emitQueues as n, emitSeed as o };
@@ -1,26 +1,9 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-f6nMvllu.mjs';
2
-
3
- const LONG_TAIL = [
4
- ["hasAi", "ai", "ai", "Override the Workers AI binding backing `ctx.ai` (defaults to `env.AI`)."],
5
- ["hasAnalytics", "analytics", "analytics", "Override the Analytics Engine dataset backing `ctx.analytics` (defaults to `env.ANALYTICS`)."],
6
- ["hasBrowser", "browser", "browser", "Override the Browser Rendering binding backing `ctx.browser` (defaults to `env.BROWSER`)."],
7
- [
8
- "hasHyperdrive",
9
- "hyperdrive",
10
- "sql",
11
- "Wire the Hyperdrive SQL client backing `ctx.sql` — build it with `createHyperdrive` + `fromPostgresJs`/`fromNodePg`/`fromMysql2`."
12
- ],
13
- ["hasImages", "images", "images", "Override the Images binding backing `ctx.images` (defaults to `env.IMAGES`)."],
14
- ["hasKv", "kv", "kv", "Override the Workers KV binding backing `ctx.kv` (defaults to `env.KV`)."],
15
- ["hasPayments", "payment", "payment", "Wire the payment options backing `ctx.payments`."],
16
- [
17
- "hasR2sql",
18
- "r2sql",
19
- "r2sql",
20
- "Wire the R2 SQL client backing `ctx.r2sql` — build it with `createR2Sql({ accountId, apiToken, bucket })` (defaults to env `R2_SQL_TOKEN` / `R2_SQL_ACCOUNT_ID` / `R2_SQL_BUCKET`)."
21
- ],
22
- ["hasVectors", "vectors", "vectors", "Wire the Vectorize index map backing `ctx.vectors`."]
23
- ];
1
+ import { A as APP_METHOD_CAPABILITIES, G as GENERATED_HEADER } from './emit-CBwEvCXQ.mjs';
2
+
3
+ const hasFlagKey = (key) => `has${key.charAt(0).toUpperCase()}${key.slice(1)}`;
4
+ const LONG_TAIL = APP_METHOD_CAPABILITIES.map(
5
+ ({ appMethod, key }) => [hasFlagKey(key), appMethod.method, appMethod.configKey, appMethod.doc]
6
+ );
24
7
  const hasAnyLongTail = (options) => LONG_TAIL.some(([flag]) => options[flag]);
25
8
  const buildIdentityImports = (identity) => identity ? [`import * as lunoraIdentityContract from "../identity.js";`] : [];
26
9
  const buildAccessImports = (hasAccess, hasAuth) => hasAccess ? [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/codegen",
3
- "version": "1.0.0-alpha.31",
3
+ "version": "1.0.0-alpha.32",
4
4
  "description": "Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -46,7 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@lunora/advisor": "1.0.0-alpha.17",
49
+ "@lunora/advisor": "1.0.0-alpha.18",
50
50
  "@lunora/container": "1.0.0-alpha.6",
51
51
  "@lunora/errors": "1.0.0-alpha.1",
52
52
  "@lunora/queue": "1.0.0-alpha.4",