@lunora/codegen 1.0.0-alpha.14 → 1.0.0-alpha.15

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
@@ -343,6 +343,30 @@ interface QueueIR {
343
343
  };
344
344
  }
345
345
  /**
346
+ * The feature-flag provider declared by the default export of `lunora/flags.ts`
347
+ * (`defineFlags({ provider, … })`). Discovery is **metadata-only** — codegen
348
+ * imports the real module at runtime for the provider value; this IR exists so
349
+ * the config layer can reconcile/validate the wrangler `flagship` binding when
350
+ * the app uses Flagship in binding mode. A `custom` provider (any other
351
+ * OpenFeature factory) carries no binding to reconcile.
352
+ */
353
+ interface FlagsIR {
354
+ /**
355
+ * The wrangler `flagship[].binding` name — set **only** for a flagship
356
+ * `provider` in binding mode (`flagshipProvider({ binding: "FLAGS" })`). The
357
+ * config layer hints/validates a matching `flagship` binding from this.
358
+ */
359
+ bindingName?: string;
360
+ /**
361
+ * Flagship operating mode — `"binding"` (wrangler binding, needs a
362
+ * `flagship` entry) or `"http"` (no binding); `undefined` for a `custom`
363
+ * provider or when the mode can't be read statically.
364
+ */
365
+ mode?: "binding" | "http";
366
+ /** `"flagship"` when the provider is `flagshipProvider(...)`, else `"custom"` (any other OpenFeature provider factory). */
367
+ provider: "custom" | "flagship";
368
+ }
369
+ /**
346
370
  * A `ctx.workflows.get("name")…` call discovered in a function body — the
347
371
  * use-site analog of {@link WorkflowIR} (which is the declaration side). Feeds
348
372
  * the `workflow_unused` lint (a declared workflow with zero call sites) and the
@@ -817,6 +841,21 @@ declare const discoverContainers: (project: Project, lunoraDirectory: string) =>
817
841
  * workflow start. Names must be unique across the project.
818
842
  */
819
843
  declare const discoverCrons: (project: Project, lunoraDirectory: string, workflows?: ReadonlyArray<WorkflowIR>) => CronJobIR[];
844
+ /** The only file a feature-flag provider may be declared in — mirrors `lunora/queues.ts`. */
845
+ declare const FLAGS_FILENAME = "flags.ts";
846
+ /** One statically-discovered flag read: the key plus the value type its `ctx.flags.&lt;type>` call implies. */
847
+
848
+ /**
849
+ * Discover the feature-flag provider a project declares in `lunora/flags.ts`.
850
+ * Returns `undefined` when the file doesn't exist (the app has no flags). The
851
+ * read is metadata-only and lenient: codegen wires `ctx.flags` purely from the
852
+ * file's *existence* (`run-codegen.ts`) and imports the real module for the
853
+ * provider value — this IR exists solely so the config layer can reconcile the
854
+ * wrangler `flagship` binding for the Flagship binding-mode provider. Anything
855
+ * it can't read statically degrades to a `custom` provider (no binding), never
856
+ * a thrown error.
857
+ */
858
+ declare const discoverFlags: (project: Project, lunoraDirectory: string) => FlagsIR | undefined;
820
859
  /**
821
860
  * Scan all .ts files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
822
861
  * for top-level `export const x = query/mutation/action({...})` registrations.
@@ -969,6 +1008,8 @@ interface EmitServerOptions {
969
1008
  hasAnalytics?: boolean;
970
1009
  /** A `lunora/` source uses `@lunora/browser` / `ctx.browser` — wires `ctx.browser` onto ActionCtx only. */
971
1010
  hasBrowser?: boolean;
1011
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1012
+ hasFlags?: boolean;
972
1013
  /** A `lunora/` source uses `@lunora/hyperdrive` / `ctx.sql` — wires `ctx.sql` onto ActionCtx only. */
973
1014
  hasHyperdrive?: boolean;
974
1015
  /** A `lunora/` source uses `@lunora/bindings/images` / `ctx.images` — wires `ctx.images` onto ActionCtx only. */
@@ -993,6 +1034,7 @@ declare const emitServer: ({
993
1034
  hasAi,
994
1035
  hasAnalytics,
995
1036
  hasBrowser,
1037
+ hasFlags,
996
1038
  hasHyperdrive,
997
1039
  hasImages,
998
1040
  hasKv,
@@ -1044,11 +1086,18 @@ declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
1044
1086
  interface EmitShardOptions {
1045
1087
  advisories?: ReadonlyArray<Finding>;
1046
1088
  containers?: ReadonlyArray<ContainerIR>;
1089
+ /** Statically-discovered `ctx.flags.&lt;type>("key")` reads — the studio Flags page + reactive evaluation iterate these. */
1090
+ flagKeys?: ReadonlyArray<{
1091
+ key: string;
1092
+ type: "boolean" | "number" | "object" | "string";
1093
+ }>;
1047
1094
  hasAi?: boolean;
1048
1095
  /** A `lunora/` source reads `ctx.analytics` — wires the Analytics Engine write helper onto every ctx. */
1049
1096
  hasAnalytics?: boolean;
1050
1097
  /** A `lunora/` source reads `ctx.browser` — wires `ctx.browser` onto the ActionCtx only. */
1051
1098
  hasBrowser?: boolean;
1099
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1100
+ hasFlags?: boolean;
1052
1101
  /** A `lunora/` source reads `ctx.sql` (Hyperdrive) — wires `ctx.sql` onto the ActionCtx only. */
1053
1102
  hasHyperdrive?: boolean;
1054
1103
  /** A `lunora/` source reads `ctx.images` — wires `ctx.images` onto the ActionCtx only. */
@@ -1074,9 +1123,11 @@ interface EmitShardOptions {
1074
1123
  declare const emitShard: ({
1075
1124
  advisories,
1076
1125
  containers,
1126
+ flagKeys,
1077
1127
  hasAi,
1078
1128
  hasAnalytics,
1079
1129
  hasBrowser,
1130
+ hasFlags,
1080
1131
  hasHyperdrive,
1081
1132
  hasImages,
1082
1133
  hasKv,
@@ -1674,4 +1725,4 @@ declare const LUNORA_SOLUTION_RULES: ReadonlyArray<LunoraSolutionRule>;
1674
1725
  */
1675
1726
  declare const findLunoraSolution: (message: string) => LunoraSolution | undefined;
1676
1727
  declare const VERSION = "0.0.0";
1677
- export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, type FieldSnapshot, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, LUNORA_SOLUTION_RULES, type LunoraSolution, type LunoraSolutionRule, type MaskProcedureIR, type MigrationIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, findLunoraSolution, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
1728
+ export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, LUNORA_SOLUTION_RULES, type LunoraSolution, type LunoraSolutionRule, type MaskProcedureIR, type MigrationIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, findLunoraSolution, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
package/dist/index.d.ts CHANGED
@@ -343,6 +343,30 @@ interface QueueIR {
343
343
  };
344
344
  }
345
345
  /**
346
+ * The feature-flag provider declared by the default export of `lunora/flags.ts`
347
+ * (`defineFlags({ provider, … })`). Discovery is **metadata-only** — codegen
348
+ * imports the real module at runtime for the provider value; this IR exists so
349
+ * the config layer can reconcile/validate the wrangler `flagship` binding when
350
+ * the app uses Flagship in binding mode. A `custom` provider (any other
351
+ * OpenFeature factory) carries no binding to reconcile.
352
+ */
353
+ interface FlagsIR {
354
+ /**
355
+ * The wrangler `flagship[].binding` name — set **only** for a flagship
356
+ * `provider` in binding mode (`flagshipProvider({ binding: "FLAGS" })`). The
357
+ * config layer hints/validates a matching `flagship` binding from this.
358
+ */
359
+ bindingName?: string;
360
+ /**
361
+ * Flagship operating mode — `"binding"` (wrangler binding, needs a
362
+ * `flagship` entry) or `"http"` (no binding); `undefined` for a `custom`
363
+ * provider or when the mode can't be read statically.
364
+ */
365
+ mode?: "binding" | "http";
366
+ /** `"flagship"` when the provider is `flagshipProvider(...)`, else `"custom"` (any other OpenFeature provider factory). */
367
+ provider: "custom" | "flagship";
368
+ }
369
+ /**
346
370
  * A `ctx.workflows.get("name")…` call discovered in a function body — the
347
371
  * use-site analog of {@link WorkflowIR} (which is the declaration side). Feeds
348
372
  * the `workflow_unused` lint (a declared workflow with zero call sites) and the
@@ -817,6 +841,21 @@ declare const discoverContainers: (project: Project, lunoraDirectory: string) =>
817
841
  * workflow start. Names must be unique across the project.
818
842
  */
819
843
  declare const discoverCrons: (project: Project, lunoraDirectory: string, workflows?: ReadonlyArray<WorkflowIR>) => CronJobIR[];
844
+ /** The only file a feature-flag provider may be declared in — mirrors `lunora/queues.ts`. */
845
+ declare const FLAGS_FILENAME = "flags.ts";
846
+ /** One statically-discovered flag read: the key plus the value type its `ctx.flags.&lt;type>` call implies. */
847
+
848
+ /**
849
+ * Discover the feature-flag provider a project declares in `lunora/flags.ts`.
850
+ * Returns `undefined` when the file doesn't exist (the app has no flags). The
851
+ * read is metadata-only and lenient: codegen wires `ctx.flags` purely from the
852
+ * file's *existence* (`run-codegen.ts`) and imports the real module for the
853
+ * provider value — this IR exists solely so the config layer can reconcile the
854
+ * wrangler `flagship` binding for the Flagship binding-mode provider. Anything
855
+ * it can't read statically degrades to a `custom` provider (no binding), never
856
+ * a thrown error.
857
+ */
858
+ declare const discoverFlags: (project: Project, lunoraDirectory: string) => FlagsIR | undefined;
820
859
  /**
821
860
  * Scan all .ts files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
822
861
  * for top-level `export const x = query/mutation/action({...})` registrations.
@@ -969,6 +1008,8 @@ interface EmitServerOptions {
969
1008
  hasAnalytics?: boolean;
970
1009
  /** A `lunora/` source uses `@lunora/browser` / `ctx.browser` — wires `ctx.browser` onto ActionCtx only. */
971
1010
  hasBrowser?: boolean;
1011
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1012
+ hasFlags?: boolean;
972
1013
  /** A `lunora/` source uses `@lunora/hyperdrive` / `ctx.sql` — wires `ctx.sql` onto ActionCtx only. */
973
1014
  hasHyperdrive?: boolean;
974
1015
  /** A `lunora/` source uses `@lunora/bindings/images` / `ctx.images` — wires `ctx.images` onto ActionCtx only. */
@@ -993,6 +1034,7 @@ declare const emitServer: ({
993
1034
  hasAi,
994
1035
  hasAnalytics,
995
1036
  hasBrowser,
1037
+ hasFlags,
996
1038
  hasHyperdrive,
997
1039
  hasImages,
998
1040
  hasKv,
@@ -1044,11 +1086,18 @@ declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
1044
1086
  interface EmitShardOptions {
1045
1087
  advisories?: ReadonlyArray<Finding>;
1046
1088
  containers?: ReadonlyArray<ContainerIR>;
1089
+ /** Statically-discovered `ctx.flags.&lt;type>("key")` reads — the studio Flags page + reactive evaluation iterate these. */
1090
+ flagKeys?: ReadonlyArray<{
1091
+ key: string;
1092
+ type: "boolean" | "number" | "object" | "string";
1093
+ }>;
1047
1094
  hasAi?: boolean;
1048
1095
  /** A `lunora/` source reads `ctx.analytics` — wires the Analytics Engine write helper onto every ctx. */
1049
1096
  hasAnalytics?: boolean;
1050
1097
  /** A `lunora/` source reads `ctx.browser` — wires `ctx.browser` onto the ActionCtx only. */
1051
1098
  hasBrowser?: boolean;
1099
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1100
+ hasFlags?: boolean;
1052
1101
  /** A `lunora/` source reads `ctx.sql` (Hyperdrive) — wires `ctx.sql` onto the ActionCtx only. */
1053
1102
  hasHyperdrive?: boolean;
1054
1103
  /** A `lunora/` source reads `ctx.images` — wires `ctx.images` onto the ActionCtx only. */
@@ -1074,9 +1123,11 @@ interface EmitShardOptions {
1074
1123
  declare const emitShard: ({
1075
1124
  advisories,
1076
1125
  containers,
1126
+ flagKeys,
1077
1127
  hasAi,
1078
1128
  hasAnalytics,
1079
1129
  hasBrowser,
1130
+ hasFlags,
1080
1131
  hasHyperdrive,
1081
1132
  hasImages,
1082
1133
  hasKv,
@@ -1674,4 +1725,4 @@ declare const LUNORA_SOLUTION_RULES: ReadonlyArray<LunoraSolutionRule>;
1674
1725
  */
1675
1726
  declare const findLunoraSolution: (message: string) => LunoraSolution | undefined;
1676
1727
  declare const VERSION = "0.0.0";
1677
- export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, type FieldSnapshot, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, LUNORA_SOLUTION_RULES, type LunoraSolution, type LunoraSolutionRule, type MaskProcedureIR, type MigrationIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, findLunoraSolution, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
1728
+ export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, LUNORA_SOLUTION_RULES, type LunoraSolution, type LunoraSolutionRule, type MaskProcedureIR, type MigrationIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, findLunoraSolution, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ export { CodegenDiagnosticError, diagnosticAt } from './packem_shared/CodegenDia
3
3
  export { default as discoverAuthApiCalls } from './packem_shared/discoverAuthApiCalls-CoirYbg6.mjs';
4
4
  export { CONTAINERS_FILENAME, discoverContainers } from './packem_shared/CONTAINERS_FILENAME-DlP6YM_Q.mjs';
5
5
  export { default as discoverCrons } from './packem_shared/discoverCrons-Cev7RRAf.mjs';
6
+ export { FLAGS_FILENAME, discoverFlags } from './packem_shared/FLAGS_FILENAME-B1vE0LIo.mjs';
6
7
  export { discoverFunctions } from './packem_shared/discoverFunctions-BWMczzBx.mjs';
7
8
  export { default as discoverHttpRoutes } from './packem_shared/discoverHttpRoutes-CfP6cMzt.mjs';
8
9
  export { default as discoverInserts } from './packem_shared/discoverInserts-C7zxXkUf.mjs';
@@ -16,11 +17,11 @@ export { discoverRlsMetadata, default as discoverRlsProcedures } from './packem_
16
17
  export { default as discoverSchema } from './packem_shared/discoverSchema-BnWHHJ4T.mjs';
17
18
  export { default as discoverStorageRulesMetadata } from './packem_shared/discoverStorageRulesMetadata-Da8BKXcI.mjs';
18
19
  export { WORKFLOWS_FILENAME, discoverWorkflows } from './packem_shared/WORKFLOWS_FILENAME-D62dcBGg.mjs';
19
- export { GENERATED_HEADER, emitApi, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-BEA7oWGC.mjs';
20
- export { emitApp } from './packem_shared/emitApp-DiK0QSBn.mjs';
21
- export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-699G4RP4.mjs';
22
- export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-CQ3f8YQ-.mjs';
23
- export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-BXtduJFE.mjs';
20
+ export { GENERATED_HEADER, emitApi, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-CkZUgM4_.mjs';
21
+ export { emitApp } from './packem_shared/emitApp-DGmlt5fq.mjs';
22
+ export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-BSoDzIn8.mjs';
23
+ export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-VINwUAxf.mjs';
24
+ export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-DsBxQ5C_.mjs';
24
25
  export { SCHEMA_SNAPSHOT_VERSION, SchemaSnapshotParseError, buildSchemaSnapshot, diffSchemaSnapshots, evaluateSchemaDrift, parseSchemaSnapshot, serializeSchemaSnapshot } from './packem_shared/SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
25
26
  export { schemaFromIr } from './packem_shared/schemaFromIr-DTYsLBaA.mjs';
26
27
  export { LUNORA_ERROR_CODES, validatorIrToJsonSchema } from './packem_shared/LUNORA_ERROR_CODES-CySpQPD3.mjs';
@@ -0,0 +1,139 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { SyntaxKind, Node } from 'ts-morph';
4
+ import { listLunoraSourceFiles } from './discoverFunctions-BWMczzBx.mjs';
5
+
6
+ const FLAGS_FILENAME = "flags.ts";
7
+ const FLAG_TYPES = /* @__PURE__ */ new Set(["boolean", "number", "object", "string"]);
8
+ const FLAGSHIP_PROVIDER_MODULE = "@lunora/flags/providers/flagship";
9
+ const isFlagshipProvider = (identifier) => {
10
+ const symbol = identifier.getSymbol();
11
+ if (!symbol) {
12
+ return identifier.getText() === "flagshipProvider";
13
+ }
14
+ for (const declaration of symbol.getDeclarations()) {
15
+ if (!Node.isImportSpecifier(declaration)) {
16
+ continue;
17
+ }
18
+ if (declaration.getImportDeclaration().getModuleSpecifierValue() !== FLAGSHIP_PROVIDER_MODULE) {
19
+ return false;
20
+ }
21
+ return declaration.getNameNode().getText() === "flagshipProvider";
22
+ }
23
+ return false;
24
+ };
25
+ const stringPropertyOrUndefined = (argument, property) => {
26
+ const node = argument.getProperty(property);
27
+ if (!node || !Node.isPropertyAssignment(node)) {
28
+ return void 0;
29
+ }
30
+ const initializer = node.getInitializerOrThrow();
31
+ if (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer)) {
32
+ return initializer.getLiteralValue();
33
+ }
34
+ return void 0;
35
+ };
36
+ const flagshipIrFromCall = (call) => {
37
+ const argument = call.getArguments()[0];
38
+ if (argument && Node.isObjectLiteralExpression(argument)) {
39
+ const bindingName = stringPropertyOrUndefined(argument, "binding");
40
+ if (bindingName !== void 0 && bindingName.length > 0) {
41
+ return { bindingName, mode: "binding", provider: "flagship" };
42
+ }
43
+ }
44
+ return { mode: "http", provider: "flagship" };
45
+ };
46
+ const defaultExportExpression = (source) => {
47
+ const assignment = source.getExportAssignment((declaration2) => !declaration2.isExportEquals());
48
+ if (!assignment) {
49
+ return void 0;
50
+ }
51
+ const expression = assignment.getExpression();
52
+ if (!Node.isIdentifier(expression)) {
53
+ return expression;
54
+ }
55
+ const declaration = expression.getSymbol()?.getValueDeclaration();
56
+ if (declaration && Node.isVariableDeclaration(declaration)) {
57
+ return declaration.getInitializer();
58
+ }
59
+ return expression;
60
+ };
61
+ const providerInitializer = (defineFlagsCall) => {
62
+ const argument = defineFlagsCall.getArguments()[0];
63
+ if (!argument || !Node.isObjectLiteralExpression(argument)) {
64
+ return void 0;
65
+ }
66
+ const property = argument.getProperty("provider");
67
+ return property && Node.isPropertyAssignment(property) ? property.getInitializer() : void 0;
68
+ };
69
+ const discoverFlags = (project, lunoraDirectory) => {
70
+ const flagsPath = join(lunoraDirectory, FLAGS_FILENAME);
71
+ if (!existsSync(flagsPath)) {
72
+ return void 0;
73
+ }
74
+ const source = project.getSourceFile(flagsPath) ?? project.addSourceFileAtPath(flagsPath);
75
+ const exported = defaultExportExpression(source);
76
+ if (!exported || !Node.isCallExpression(exported)) {
77
+ return { provider: "custom" };
78
+ }
79
+ const provider = providerInitializer(exported);
80
+ if (provider && Node.isCallExpression(provider)) {
81
+ const callee = provider.getExpression();
82
+ if (Node.isIdentifier(callee) && isFlagshipProvider(callee)) {
83
+ return flagshipIrFromCall(provider);
84
+ }
85
+ }
86
+ return { provider: "custom" };
87
+ };
88
+ const isContextIdentifier = (node) => Node.isIdentifier(node) && node.getText() === "ctx";
89
+ const flagTypeFromAccess = (access) => {
90
+ const name = access.getName();
91
+ if (!FLAG_TYPES.has(name)) {
92
+ return void 0;
93
+ }
94
+ const receiver = access.getExpression();
95
+ if (!Node.isPropertyAccessExpression(receiver)) {
96
+ return void 0;
97
+ }
98
+ if (receiver.getName() === "flags" && isContextIdentifier(receiver.getExpression())) {
99
+ return name;
100
+ }
101
+ const inner = receiver.getExpression();
102
+ if (receiver.getName() === "details" && Node.isPropertyAccessExpression(inner) && inner.getName() === "flags" && isContextIdentifier(inner.getExpression())) {
103
+ return name;
104
+ }
105
+ return void 0;
106
+ };
107
+ const flagKeyFromCall = (call) => {
108
+ const callee = call.getExpression();
109
+ if (!Node.isPropertyAccessExpression(callee)) {
110
+ return void 0;
111
+ }
112
+ const type = flagTypeFromAccess(callee);
113
+ if (type === void 0) {
114
+ return void 0;
115
+ }
116
+ const argument = call.getArguments()[0];
117
+ if (!argument || !(Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument))) {
118
+ return void 0;
119
+ }
120
+ const key = argument.getLiteralValue();
121
+ return key.length > 0 ? { key, type } : void 0;
122
+ };
123
+ const discoverFlagKeys = (project, lunoraDirectory) => {
124
+ const found = /* @__PURE__ */ new Map();
125
+ for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
126
+ const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
127
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
128
+ const candidate = flagKeyFromCall(call);
129
+ if (candidate && !found.has(candidate.key)) {
130
+ found.set(candidate.key, candidate.type);
131
+ }
132
+ }
133
+ }
134
+ return [...found.entries()].map(([key, type]) => {
135
+ return { key, type };
136
+ }).toSorted((a, b) => a.key.localeCompare(b.key));
137
+ };
138
+
139
+ export { FLAGS_FILENAME, discoverFlagKeys, discoverFlags };
@@ -716,6 +716,7 @@ const emitServer = ({
716
716
  hasAi = false,
717
717
  hasAnalytics = false,
718
718
  hasBrowser = false,
719
+ hasFlags = false,
719
720
  hasHyperdrive = false,
720
721
  hasImages = false,
721
722
  hasKv = false,
@@ -784,6 +785,9 @@ ${envBindingFields}` : ""}
784
785
  export type Env = CloudflareBindings;`;
785
786
  const kvContextField = hasKv ? `
786
787
  readonly kv: import("@lunora/bindings/kv").Kv;` : "";
788
+ const flagsContextField = hasFlags ? `
789
+ /** Feature-flag evaluation (OpenFeature). Reads are memoized per request; evaluations never throw — a provider error resolves to the supplied default. */
790
+ readonly flags: import("@lunora/flags").LunoraFlags;` : "";
787
791
  const hyperdriveActionField = hasHyperdrive ? `
788
792
  /**
789
793
  * 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.
@@ -898,19 +902,19 @@ type TypedTableGet = <T extends TableName>(id: IdOfTable<T>) => Promise<Doc<T> |
898
902
  export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"> {
899
903
  readonly db: Omit<DatabaseReader, "query" | "get"> & DatabaseReaderFacade & { query: TypedTableQuery; get: TypedTableGet };
900
904
  readonly orm: OrmReader;
901
- readonly storage: ReadOnlyStorage<StorageBucketName>;${kvContextField}${analyticsContextField}
905
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${kvContextField}${flagsContextField}${analyticsContextField}
902
906
  }
903
907
 
904
908
  export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}> {
905
909
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
906
910
  readonly orm: OrmWriter;
907
- readonly storage: ReadOnlyStorage<StorageBucketName>;${kvContextField}${analyticsContextField}${workflowsContextField}${queuesContextField}
911
+ readonly storage: ReadOnlyStorage<StorageBucketName>;${kvContextField}${flagsContextField}${analyticsContextField}${workflowsContextField}${queuesContextField}
908
912
  }
909
913
 
910
914
  export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}> {
911
915
  readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
912
916
  readonly orm: OrmWriter;
913
- readonly storage: StorageBase<StorageBucketName>;${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}${queuesContextField}
917
+ readonly storage: StorageBase<StorageBucketName>;${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}${queuesContextField}
914
918
  }
915
919
 
916
920
  /**
@@ -1281,6 +1285,98 @@ const kvStub: Kv = {
1281
1285
  `
1282
1286
  };
1283
1287
  };
1288
+ const emitFlagsFragments = (hasFlags) => {
1289
+ if (!hasFlags) {
1290
+ return EMPTY_HELPER_FRAGMENTS;
1291
+ }
1292
+ return {
1293
+ build: `
1294
+ const flags: import("@lunora/flags").LunoraFlags = createFlags({
1295
+ hooks: flagsConfig.hooks,
1296
+ logger: flagsConfig.logger,
1297
+ provider: () => config.flags?.(env) ?? flagsConfig.provider(env),
1298
+ targetingKey: () => flagsConfig.identify?.({ identity: identity ?? null, userId: userId ?? null }),
1299
+ });
1300
+ `,
1301
+ configField: `
1302
+ flags?: (env: Record<string, unknown>) => import("@lunora/flags").Provider;`,
1303
+ contextField: `
1304
+ flags,`,
1305
+ importLines: [`import { createFlags } from "@lunora/flags";`, `import flagsConfig from "../flags.js";`],
1306
+ stub: ""
1307
+ };
1308
+ };
1309
+ const emitFlagsOverrides = (flagKeys, hasFlags) => {
1310
+ if (!hasFlags) {
1311
+ return { constant: "", evaluateOverride: "", subscriptionOverride: "" };
1312
+ }
1313
+ const clientBuild = (targetingKey) => `
1314
+ const env = (this.env ?? {}) as Record<string, unknown>;
1315
+ const flags: import("@lunora/flags").LunoraFlags = createFlags({
1316
+ hooks: flagsConfig.hooks,
1317
+ logger: flagsConfig.logger,
1318
+ provider: () => config.flags?.(env) ?? flagsConfig.provider(env),
1319
+ targetingKey: ${targetingKey},
1320
+ });`;
1321
+ const constant = `
1322
+ /** Statically-discovered feature flags (\`ctx.flags.<type>("key")\` reads) served via \`__lunora_admin__:listFlags\` + the reactive \`__lunora_flags__:\` channel. */
1323
+ const LUNORA_FLAG_KEYS: ReadonlyArray<{ key: string; type: "boolean" | "number" | "object" | "string" }> = ${JSON.stringify(flagKeys, void 0, 4)};
1324
+ `;
1325
+ const evaluateOverride = `
1326
+ protected override async evaluateFlags(context?: Record<string, unknown>): Promise<FlagsResult> {${clientBuild("undefined")}
1327
+ const evalContext = context as import("@lunora/flags").EvaluationContext | undefined;
1328
+ const evaluations: FlagsResult["flags"] = [];
1329
+
1330
+ for (const entry of LUNORA_FLAG_KEYS) {
1331
+ // eslint-disable-next-line no-await-in-loop -- flags evaluate sequentially; each shares the single memoized provider client
1332
+ const details =
1333
+ entry.type === "boolean"
1334
+ ? await flags.details.boolean(entry.key, false, evalContext)
1335
+ : entry.type === "number"
1336
+ ? await flags.details.number(entry.key, 0, evalContext)
1337
+ : entry.type === "string"
1338
+ ? await flags.details.string(entry.key, "", evalContext)
1339
+ : await flags.details.object(entry.key, {}, evalContext);
1340
+
1341
+ evaluations.push({ errorCode: details.errorCode, key: entry.key, reason: details.reason, type: entry.type, value: details.value, variant: details.variant });
1342
+ }
1343
+
1344
+ return { configured: true, flags: evaluations };
1345
+ }
1346
+ `;
1347
+ const subscriptionOverride = `
1348
+ protected override runFlagSubscriptionRead(_functionPath: string, args: Record<string, unknown>, identity?: { identity?: Record<string, unknown>; userId?: string }): Promise<unknown> {${clientBuild("() => flagsConfig.identify?.({ identity: identity?.identity ?? null, userId: identity?.userId ?? null })")}
1349
+ const key = typeof args.key === "string" ? args.key : "";
1350
+ const rawContext = typeof args.context === "object" && args.context !== null ? (args.context as Record<string, unknown>) : undefined;
1351
+ // The reactive channel is public (any socket, no auth required): never
1352
+ // let a subscriber-supplied targetingKey override the verified identity
1353
+ // resolved above, or a client could read another user's flags. Other
1354
+ // (non-identity) targeting attributes pass through unchanged.
1355
+ const { targetingKey: _clientTargetingKey, ...safeContext } = rawContext ?? {};
1356
+ const context = (rawContext === undefined ? undefined : safeContext) as import("@lunora/flags").EvaluationContext | undefined;
1357
+
1358
+ // eslint-disable-next-line unicorn/no-null -- the base hook's "nothing to deliver" sentinel
1359
+ if (key.length === 0) {
1360
+ return Promise.resolve(null);
1361
+ }
1362
+
1363
+ if (args.type === "number") {
1364
+ return flags.number(key, typeof args.default === "number" ? args.default : 0, context);
1365
+ }
1366
+
1367
+ if (args.type === "string") {
1368
+ return flags.string(key, typeof args.default === "string" ? args.default : "", context);
1369
+ }
1370
+
1371
+ if (args.type === "object") {
1372
+ return flags.object(key, (args.default ?? {}) as import("@lunora/flags").JsonValue, context);
1373
+ }
1374
+
1375
+ return flags.boolean(key, typeof args.default === "boolean" ? args.default : false, context);
1376
+ }
1377
+ `;
1378
+ return { constant, evaluateOverride, subscriptionOverride };
1379
+ };
1284
1380
  const emitAnalyticsFragments = (hasAnalytics) => {
1285
1381
  if (!hasAnalytics) {
1286
1382
  return EMPTY_HELPER_FRAGMENTS;
@@ -1772,10 +1868,11 @@ const paymentStub: LunoraPayment = {
1772
1868
  `
1773
1869
  };
1774
1870
  };
1775
- const buildDoTypeImports = (hasVectors, hasWorkflows, hasQueues) => [
1871
+ const buildDoTypeImports = (hasVectors, hasWorkflows, hasQueues, hasFlags) => [
1776
1872
  "AdvisoryFinding",
1777
1873
  "DatabaseWriterLike",
1778
1874
  "DataMigrationLike",
1875
+ ...hasFlags ? ["FlagsResult"] : [],
1779
1876
  "LogSink",
1780
1877
  "MaskPoliciesResult",
1781
1878
  "MigrationRunResult",
@@ -1801,9 +1898,11 @@ const buildDoTypeImports = (hasVectors, hasWorkflows, hasQueues) => [
1801
1898
  const emitShard = ({
1802
1899
  advisories = [],
1803
1900
  containers = [],
1901
+ flagKeys = [],
1804
1902
  hasAi = false,
1805
1903
  hasAnalytics = false,
1806
1904
  hasBrowser = false,
1905
+ hasFlags = false,
1807
1906
  hasHyperdrive = false,
1808
1907
  hasImages = false,
1809
1908
  hasKv = false,
@@ -1822,6 +1921,8 @@ const emitShard = ({
1822
1921
  const base = baseSpecifiers(useUmbrella);
1823
1922
  const { build: aiBuild, configField: aiConfigField, contextField: aiContextField, stub: aiStub } = emitAiFragments(hasAi);
1824
1923
  const kvFragments = emitKvFragments(hasKv);
1924
+ const flagsFragments = emitFlagsFragments(hasFlags);
1925
+ const flagsOverrides = emitFlagsOverrides(flagKeys, hasFlags);
1825
1926
  const analyticsFragments = emitAnalyticsFragments(hasAnalytics);
1826
1927
  const imagesFragments = emitImagesFragments(hasImages);
1827
1928
  const hyperdriveFragments = emitHyperdriveFragments(hasHyperdrive);
@@ -1853,6 +1954,7 @@ const emitShard = ({
1853
1954
  const maskData = maskMetadata ?? { columns: [] };
1854
1955
  const storageRulesData = storageRules ?? { rules: [] };
1855
1956
  const studioFeaturesData = studioFeatures ?? {
1957
+ flags: false,
1856
1958
  mail: false,
1857
1959
  payments: false,
1858
1960
  queues: false,
@@ -1877,7 +1979,7 @@ const emitShard = ({
1877
1979
  const tableIndexes = buildTableIndexes(schema);
1878
1980
  const tableColumns = buildTableColumns(schema);
1879
1981
  const storageColumns = buildStorageColumns(schema);
1880
- const doTypeImports = buildDoTypeImports(hasVectors, workflows.length > 0, queues.length > 0);
1982
+ const doTypeImports = buildDoTypeImports(hasVectors, workflows.length > 0, queues.length > 0, hasFlags);
1881
1983
  const relationFanout = emitRelationFanout(hasGlobalTables);
1882
1984
  const importLines = [
1883
1985
  `import type { ${doTypeImports.join(", ")} } from "${base.do}";`,
@@ -1902,6 +2004,7 @@ const emitShard = ({
1902
2004
  }
1903
2005
  importLines.push(
1904
2006
  ...kvFragments.importLines,
2007
+ ...flagsFragments.importLines,
1905
2008
  ...analyticsFragments.importLines,
1906
2009
  ...imagesFragments.importLines,
1907
2010
  ...hyperdriveFragments.importLines,
@@ -2055,8 +2158,8 @@ ${schema.tables.map(
2055
2158
  const secretsBuild = `
2056
2159
  const secrets = createSecrets(env);
2057
2160
  `;
2058
- const everyContextBuild = `${kvFragments.build}${analyticsFragments.build}${secretsBuild}`;
2059
- const everyContextField = `${kvFragments.contextField}${analyticsFragments.contextField}
2161
+ const everyContextBuild = `${kvFragments.build}${flagsFragments.build}${analyticsFragments.build}${secretsBuild}`;
2162
+ const everyContextField = `${kvFragments.contextField}${flagsFragments.contextField}${analyticsFragments.contextField}
2060
2163
  secrets,`;
2061
2164
  const actionOnlyHasAny = hasImages || hasHyperdrive || hasBrowser || hasR2sql || hasPipelines;
2062
2165
  const actionOnlyBlock = actionOnlyHasAny ? `
@@ -2108,14 +2211,14 @@ const LUNORA_STORAGE_RULES: StorageRulesResult = ${JSON.stringify(storageRulesDa
2108
2211
 
2109
2212
  /** 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. */
2110
2213
  const LUNORA_STUDIO_FEATURES: StudioFeaturesResult = ${JSON.stringify(studioFeaturesData, void 0, 4)};
2111
- ${workflowsMetadataConst}${queuesMetadataConst}${containerSpecs}${workflowSpecs}${queueSpecs}
2214
+ ${flagsOverrides.constant}${workflowsMetadataConst}${queuesMetadataConst}${containerSpecs}${workflowSpecs}${queueSpecs}
2112
2215
  export interface ShardDOConfig {
2113
2216
  /** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
2114
2217
  cdc?: boolean;
2115
2218
  /** 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. */
2116
2219
  observability?: (env: Record<string, unknown>) => LogSink | undefined;
2117
2220
  scheduler?: (env: Record<string, unknown>) => unknown;
2118
- storage?: (env: Record<string, unknown>) => unknown;${vectorsConfigField}${aiConfigField}${kvFragments.configField}${analyticsFragments.configField}${imagesFragments.configField}${hyperdriveFragments.configField}${browserFragments.configField}${r2sqlFragments.configField}${pipelinesFragments.configField}${paymentsConfigField}${d1ConfigField}${hyperdriveGlobalConfigField}
2221
+ storage?: (env: Record<string, unknown>) => unknown;${vectorsConfigField}${aiConfigField}${kvFragments.configField}${flagsFragments.configField}${analyticsFragments.configField}${imagesFragments.configField}${hyperdriveFragments.configField}${browserFragments.configField}${r2sqlFragments.configField}${pipelinesFragments.configField}${paymentsConfigField}${d1ConfigField}${hyperdriveGlobalConfigField}
2119
2222
  }
2120
2223
 
2121
2224
  const schedulerStub = {
@@ -2153,7 +2256,7 @@ const storageStub = {
2153
2256
  throw new Error("ctx.storage: no storage configured. Pass \`storage\` to createShardDO().");
2154
2257
  },
2155
2258
  };
2156
- ${globalDatabaseStub}${vectorsStub}${aiStub}${kvFragments.stub}${analyticsFragments.stub}${imagesFragments.stub}${hyperdriveFragments.stub}${browserFragments.stub}${r2sqlFragments.stub}${pipelinesFragments.stub}${paymentStub}${bindTableHelper}
2259
+ ${globalDatabaseStub}${vectorsStub}${aiStub}${kvFragments.stub}${flagsFragments.stub}${analyticsFragments.stub}${imagesFragments.stub}${hyperdriveFragments.stub}${browserFragments.stub}${r2sqlFragments.stub}${pipelinesFragments.stub}${paymentStub}${bindTableHelper}
2157
2260
  // Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
2158
2261
  // referencing call fails loudly with a clear error instead of overflowing the
2159
2262
  // stack. Tracked across the awaited handler chain (one DO invocation is
@@ -2298,7 +2401,7 @@ ${relationFanout.override}
2298
2401
  protected override studioFeatures(): StudioFeaturesResult {
2299
2402
  return LUNORA_STUDIO_FEATURES;
2300
2403
  }
2301
- ${workflowsMetadataOverride}${queuesMetadataOverride}
2404
+ ${flagsOverrides.evaluateOverride}${flagsOverrides.subscriptionOverride}${workflowsMetadataOverride}${queuesMetadataOverride}
2302
2405
  protected override advisories(): AdvisoryFinding[] {
2303
2406
  return LUNORA_ADVISORIES;
2304
2407
  }
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-BEA7oWGC.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-CkZUgM4_.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
 
@@ -7,6 +7,7 @@ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall, disco
7
7
  import discoverAuthApiCalls from './discoverAuthApiCalls-CoirYbg6.mjs';
8
8
  import { discoverContainers } from './CONTAINERS_FILENAME-DlP6YM_Q.mjs';
9
9
  import discoverCrons from './discoverCrons-Cev7RRAf.mjs';
10
+ import { discoverFlagKeys } from './FLAGS_FILENAME-B1vE0LIo.mjs';
10
11
  import discoverHttpRoutes from './discoverHttpRoutes-CfP6cMzt.mjs';
11
12
  import discoverInserts from './discoverInserts-C7zxXkUf.mjs';
12
13
  import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-Cm81kwrb.mjs';
@@ -20,10 +21,10 @@ import discoverSchema from './discoverSchema-BnWHHJ4T.mjs';
20
21
  import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
21
22
  import { e as enclosingExportName$1 } from './discover-ast-CT6BgBr4.mjs';
22
23
  import { discoverWorkflows } from './WORKFLOWS_FILENAME-D62dcBGg.mjs';
23
- import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-BEA7oWGC.mjs';
24
- import { emitApp } from './emitApp-DiK0QSBn.mjs';
25
- import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-699G4RP4.mjs';
26
- import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-CQ3f8YQ-.mjs';
24
+ import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-CkZUgM4_.mjs';
25
+ import { emitApp } from './emitApp-DGmlt5fq.mjs';
26
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-BSoDzIn8.mjs';
27
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-VINwUAxf.mjs';
27
28
  import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
28
29
 
29
30
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
@@ -243,6 +244,7 @@ const PROBES = {
243
244
  ai: { contextProperty: "ai", moduleSpecifier: "@lunora/ai" },
244
245
  analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/bindings/analytics" },
245
246
  browser: { contextProperty: "browser", moduleSpecifier: "@lunora/browser" },
247
+ flags: { contextProperty: "flags", moduleSpecifier: "@lunora/flags" },
246
248
  hyperdrive: { contextProperty: "sql", moduleSpecifier: "@lunora/hyperdrive" },
247
249
  images: { contextProperty: "images", moduleSpecifier: "@lunora/bindings/images" },
248
250
  kv: { contextProperty: "kv", moduleSpecifier: "@lunora/bindings/kv" },
@@ -278,6 +280,7 @@ const discoverFeatureUsage = (project, lunoraDirectory) => {
278
280
  ai: false,
279
281
  analytics: false,
280
282
  browser: false,
283
+ flags: false,
281
284
  hyperdrive: false,
282
285
  images: false,
283
286
  kv: false,
@@ -315,6 +318,7 @@ const discoverFeatureUsage = (project, lunoraDirectory) => {
315
318
  };
316
319
  const buildStudioFeatures = (usage, signals) => {
317
320
  return {
321
+ flags: usage.flags || signals.dependencies.has("@lunora/flags"),
318
322
  mail: usage.mail || signals.dependencies.has("@lunora/mail"),
319
323
  payments: usage.payments || signals.dependencies.has("@lunora/payment"),
320
324
  queues: signals.queueCount > 0 || signals.dependencies.has("@lunora/queue"),
@@ -768,6 +772,8 @@ const runCodegen = (options) => {
768
772
  const hasAi = featureUsage.ai;
769
773
  const hasPayments = featureUsage.payments;
770
774
  const hasKv = featureUsage.kv;
775
+ const hasFlags = existsSync(join(lunoraDirectory, "flags.ts"));
776
+ const flagKeys = hasFlags ? discoverFlagKeys(project, lunoraDirectory) : [];
771
777
  const hasHyperdrive = featureUsage.hyperdrive;
772
778
  const hasBrowser = featureUsage.browser;
773
779
  const hasImages = featureUsage.images;
@@ -793,6 +799,7 @@ const runCodegen = (options) => {
793
799
  hasAi,
794
800
  hasAnalytics,
795
801
  hasBrowser,
802
+ hasFlags,
796
803
  hasHyperdrive,
797
804
  hasImages,
798
805
  hasKv,
@@ -809,9 +816,11 @@ const runCodegen = (options) => {
809
816
  const shardContent = emitShard({
810
817
  advisories,
811
818
  containers,
819
+ flagKeys,
812
820
  hasAi,
813
821
  hasAnalytics,
814
822
  hasBrowser,
823
+ hasFlags,
815
824
  hasHyperdrive,
816
825
  hasImages,
817
826
  hasKv,
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-BEA7oWGC.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-CkZUgM4_.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-BEA7oWGC.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-CkZUgM4_.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`)."],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/codegen",
3
- "version": "1.0.0-alpha.14",
3
+ "version": "1.0.0-alpha.15",
4
4
  "description": "Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema",
5
5
  "keywords": [
6
6
  "cloudflare",