@lunora/config 1.0.0-alpha.107 → 1.0.0-alpha.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/cloudflare/index.d.mts +30 -2
  2. package/dist/cloudflare/index.d.ts +30 -2
  3. package/dist/cloudflare/index.mjs +1 -1
  4. package/dist/index.d.mts +47 -1
  5. package/dist/index.d.ts +47 -1
  6. package/dist/index.mjs +1 -1
  7. package/dist/packem_shared/ALLOW_FORWARDED_ENV-DGdbCDre.mjs +1 -0
  8. package/dist/packem_shared/{CLOUDFLARE_DRIVER-BllWt7Xx.mjs → CLOUDFLARE_DRIVER-DS7cF7Cl.mjs} +1 -1
  9. package/dist/packem_shared/{DEFAULT_DEPLOY_TARGET-DMsFUSw7.mjs → DEFAULT_DEPLOY_TARGET-CBoD0Zha.mjs} +1 -1
  10. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs +6 -0
  11. package/dist/packem_shared/{LUNORA_CONFIG_FILE-4XwJQRl9.mjs → LUNORA_CONFIG_FILE-DoVUD52W.mjs} +1 -1
  12. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-BwmyesjD.mjs +1 -0
  13. package/dist/packem_shared/{buildPackageSecretsBlock-BpvaZzQ8.mjs → buildPackageSecretsBlock-DlAPMSwt.mjs} +7 -7
  14. package/dist/packem_shared/{collectWranglerSecretVariables-OacEwPmL.mjs → collectWranglerSecretVariables-jPUxMC9j.mjs} +1 -1
  15. package/dist/packem_shared/inferLunoraBindings-tf2nExl_.mjs +1 -0
  16. package/dist/packem_shared/{parseDevVariable-NAVJ8tgA.mjs → parseDevVariable-B193_28t.mjs} +1 -1
  17. package/dist/packem_shared/reconcileWranglerBindings-BCwTpuLO.mjs +1 -0
  18. package/dist/packem_shared/serveJsonHandler-DQ-Yqa4J.mjs +1 -0
  19. package/dist/studio-host/index.d.mts +33 -1
  20. package/dist/studio-host/index.d.ts +33 -1
  21. package/dist/studio-host/index.mjs +1 -1
  22. package/package.json +5 -5
  23. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-DX6xGbpr.mjs +0 -1
  24. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-BP_35C9u.mjs +0 -1
  25. package/dist/packem_shared/inferLunoraBindings-DhfIOVhl.mjs +0 -1
  26. package/dist/packem_shared/reconcileWranglerBindings-CHFT6zqp.mjs +0 -1
  27. package/dist/packem_shared/serveJsonHandler-C5GWlWJF.mjs +0 -1
@@ -44,8 +44,21 @@ interface ReconcileBindingsResult {
44
44
  *
45
45
  * Writes only when something is missing; returns `changed: false` when the
46
46
  * config already satisfies the inferred needs.
47
+ *
48
+ * `environment`, when passed, does NOT change where this writes — every step
49
+ * below still only touches the TOP-LEVEL config; wrangler's `env.<name>`
50
+ * blocks have no auto-provisioning path today. It is used only to emit an
51
+ * advisory warning, because bindings (`durable_objects`, `d1_databases`, …)
52
+ * are non-inheritable (see `wrangler-validator.ts`'s `NON_INHERITABLE_KEYS`):
53
+ * a `--env production` deploy needs its OWN copy of each one, and silently
54
+ * writing only to the top level would leave that gap unmentioned. Extending
55
+ * the JSONC writer itself to target `env.<name>.*` idempotently for every
56
+ * binding kind here is a separate, larger change (each of the ~10 pipeline
57
+ * steps below reads AND writes the top-level path) that this fix does not
58
+ * attempt — `lunora deploy --env <name>` now VALIDATES the env-scoped view
59
+ * (closing the reported gap), it just doesn't yet auto-provision it.
47
60
  */
48
- declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings) => ReconcileBindingsResult;
61
+ declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings, environment?: string) => ReconcileBindingsResult;
49
62
  interface ReconcileCompatibilityDateResult {
50
63
  /** `true` when `wrangler.jsonc` was rewritten. */
51
64
  changed: boolean;
@@ -511,6 +524,7 @@ interface WranglerConfig {
511
524
  durable_objects?: {
512
525
  bindings?: ReadonlyArray<WranglerDurableObjectBinding>;
513
526
  };
527
+ env?: Record<string, WranglerConfig>;
514
528
  exports?: Record<string, {
515
529
  cache?: {
516
530
  enabled?: boolean;
@@ -606,8 +620,15 @@ declare const withTailConsumer: (wrangler: WranglerConfig, consumer: TailConsume
606
620
  /**
607
621
  * Pure validator: given a parsed `WranglerConfig` object and an optional
608
622
  * `SchemaInfo`, produce a structured report. Performs no I/O.
623
+ *
624
+ * `environment`, when set, validates the `env.&lt;environment>` view
625
+ * ({@link mergeWranglerEnvironment}) instead of the top-level config — e.g. a
626
+ * `durable_objects` binding present only at the top level is a validation
627
+ * FAILURE for `--env production` if `env.production` doesn't repeat it,
628
+ * because `durable_objects` is non-inheritable and wrangler will not carry it
629
+ * over. Omit `environment` to validate the top level only (unchanged default).
609
630
  */
610
- declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, schema?: SchemaInfo) => WranglerValidationReport;
631
+ declare const validateWranglerConfig: (wranglerInput: WranglerConfig | undefined, schema?: SchemaInfo, environment?: string) => WranglerValidationReport;
611
632
  /**
612
633
  * Convenience alias matching the original task-spec signature
613
634
  * `validateWrangler(wranglerJson, schema)` returning
@@ -615,6 +636,13 @@ declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, sch
615
636
  */
616
637
  declare const validateWrangler: typeof validateWranglerConfig;
617
638
  interface WranglerProjectValidationOptions {
639
+ /**
640
+ * Cloudflare environment to validate against `env.&lt;name>` in
641
+ * wrangler.jsonc. See {@link mergeWranglerEnvironment} for which keys
642
+ * inherit the top-level value vs must be redeclared per environment.
643
+ * Omit to validate the top-level config only (unchanged default).
644
+ */
645
+ environment?: string;
618
646
  projectRoot: string;
619
647
  schemaDir?: string;
620
648
  }
@@ -44,8 +44,21 @@ interface ReconcileBindingsResult {
44
44
  *
45
45
  * Writes only when something is missing; returns `changed: false` when the
46
46
  * config already satisfies the inferred needs.
47
+ *
48
+ * `environment`, when passed, does NOT change where this writes — every step
49
+ * below still only touches the TOP-LEVEL config; wrangler's `env.&lt;name>`
50
+ * blocks have no auto-provisioning path today. It is used only to emit an
51
+ * advisory warning, because bindings (`durable_objects`, `d1_databases`, …)
52
+ * are non-inheritable (see `wrangler-validator.ts`'s `NON_INHERITABLE_KEYS`):
53
+ * a `--env production` deploy needs its OWN copy of each one, and silently
54
+ * writing only to the top level would leave that gap unmentioned. Extending
55
+ * the JSONC writer itself to target `env.&lt;name>.*` idempotently for every
56
+ * binding kind here is a separate, larger change (each of the ~10 pipeline
57
+ * steps below reads AND writes the top-level path) that this fix does not
58
+ * attempt — `lunora deploy --env &lt;name>` now VALIDATES the env-scoped view
59
+ * (closing the reported gap), it just doesn't yet auto-provision it.
47
60
  */
48
- declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings) => ReconcileBindingsResult;
61
+ declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings, environment?: string) => ReconcileBindingsResult;
49
62
  interface ReconcileCompatibilityDateResult {
50
63
  /** `true` when `wrangler.jsonc` was rewritten. */
51
64
  changed: boolean;
@@ -511,6 +524,7 @@ interface WranglerConfig {
511
524
  durable_objects?: {
512
525
  bindings?: ReadonlyArray<WranglerDurableObjectBinding>;
513
526
  };
527
+ env?: Record<string, WranglerConfig>;
514
528
  exports?: Record<string, {
515
529
  cache?: {
516
530
  enabled?: boolean;
@@ -606,8 +620,15 @@ declare const withTailConsumer: (wrangler: WranglerConfig, consumer: TailConsume
606
620
  /**
607
621
  * Pure validator: given a parsed `WranglerConfig` object and an optional
608
622
  * `SchemaInfo`, produce a structured report. Performs no I/O.
623
+ *
624
+ * `environment`, when set, validates the `env.&lt;environment>` view
625
+ * ({@link mergeWranglerEnvironment}) instead of the top-level config — e.g. a
626
+ * `durable_objects` binding present only at the top level is a validation
627
+ * FAILURE for `--env production` if `env.production` doesn't repeat it,
628
+ * because `durable_objects` is non-inheritable and wrangler will not carry it
629
+ * over. Omit `environment` to validate the top level only (unchanged default).
609
630
  */
610
- declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, schema?: SchemaInfo) => WranglerValidationReport;
631
+ declare const validateWranglerConfig: (wranglerInput: WranglerConfig | undefined, schema?: SchemaInfo, environment?: string) => WranglerValidationReport;
611
632
  /**
612
633
  * Convenience alias matching the original task-spec signature
613
634
  * `validateWrangler(wranglerJson, schema)` returning
@@ -615,6 +636,13 @@ declare const validateWranglerConfig: (wrangler: WranglerConfig | undefined, sch
615
636
  */
616
637
  declare const validateWrangler: typeof validateWranglerConfig;
617
638
  interface WranglerProjectValidationOptions {
639
+ /**
640
+ * Cloudflare environment to validate against `env.&lt;name>` in
641
+ * wrangler.jsonc. See {@link mergeWranglerEnvironment} for which keys
642
+ * inherit the top-level value vs must be redeclared per environment.
643
+ * Omit to validate the top-level config only (unchanged default).
644
+ */
645
+ environment?: string;
618
646
  projectRoot: string;
619
647
  schemaDir?: string;
620
648
  }
@@ -1 +1 @@
1
- import{default as o}from"../packem_shared/CLOUDFLARE_DRIVER-BllWt7Xx.mjs";import{reconcileWranglerBindings as l}from"../packem_shared/reconcileWranglerBindings-CHFT6zqp.mjs";import{reconcileWranglerCompatibilityDate as t}from"../packem_shared/reconcileWranglerCompatibilityDate-BXNiEQ7p.mjs";import{reconcileWranglerCrons as E}from"../packem_shared/reconcileWranglerCrons-BmQa_kGL.mjs";import{REMOTE_ELIGIBLE_KEYS as g,injectRemoteFlags as R,isRemoteEnvEnabled as c,materializeRemoteWranglerConfig as f,planRemoteBindings as s,resolveRemoteEnabled as W}from"../packem_shared/REMOTE_ELIGIBLE_KEYS-ws6iE0y-.mjs";import{WORKERS_CACHE_MIN_DATE as d,isCacheEnabled as x}from"../packem_shared/WORKERS_CACHE_MIN_DATE-B1h_wNDN.mjs";import{WRANGLER_FILES as I,findWranglerFile as _,readWranglerJsonc as A}from"../packem_shared/WRANGLER_FILES-Bi_18Pj6.mjs";import{collectWranglerSecretVariables as D,scanWranglerVariablesForSecrets as T}from"../packem_shared/collectWranglerSecretVariables-OacEwPmL.mjs";import{wranglerToAlchemy as F}from"../packem_shared/wranglerToAlchemy-Fh5KfTPB.mjs";import{REQUIRED_COMPATIBILITY_DATE as S,REQUIRED_FLAG as B,validateWrangler as O,validateWranglerConfig as h,validateWranglerProject as G,withTailConsumer as M}from"../packem_shared/REQUIRED_COMPATIBILITY_DATE-BP_35C9u.mjs";export{o as CLOUDFLARE_DRIVER,g as REMOTE_ELIGIBLE_KEYS,S as REQUIRED_COMPATIBILITY_DATE,B as REQUIRED_FLAG,d as WORKERS_CACHE_MIN_DATE,I as WRANGLER_FILES,D as collectWranglerSecretVariables,_ as findWranglerFile,R as injectRemoteFlags,x as isCacheEnabled,c as isRemoteEnvEnabled,f as materializeRemoteWranglerConfig,s as planRemoteBindings,A as readWranglerJsonc,l as reconcileWranglerBindings,t as reconcileWranglerCompatibilityDate,E as reconcileWranglerCrons,W as resolveRemoteEnabled,T as scanWranglerVariablesForSecrets,O as validateWrangler,h as validateWranglerConfig,G as validateWranglerProject,M as withTailConsumer,F as wranglerToAlchemy};
1
+ import{default as o}from"../packem_shared/CLOUDFLARE_DRIVER-DS7cF7Cl.mjs";import{reconcileWranglerBindings as l}from"../packem_shared/reconcileWranglerBindings-BCwTpuLO.mjs";import{reconcileWranglerCompatibilityDate as t}from"../packem_shared/reconcileWranglerCompatibilityDate-BXNiEQ7p.mjs";import{reconcileWranglerCrons as E}from"../packem_shared/reconcileWranglerCrons-BmQa_kGL.mjs";import{REMOTE_ELIGIBLE_KEYS as g,injectRemoteFlags as R,isRemoteEnvEnabled as c,materializeRemoteWranglerConfig as f,planRemoteBindings as s,resolveRemoteEnabled as W}from"../packem_shared/REMOTE_ELIGIBLE_KEYS-ws6iE0y-.mjs";import{WORKERS_CACHE_MIN_DATE as d,isCacheEnabled as x}from"../packem_shared/WORKERS_CACHE_MIN_DATE-B1h_wNDN.mjs";import{WRANGLER_FILES as I,findWranglerFile as _,readWranglerJsonc as A}from"../packem_shared/WRANGLER_FILES-Bi_18Pj6.mjs";import{collectWranglerSecretVariables as D,scanWranglerVariablesForSecrets as T}from"../packem_shared/collectWranglerSecretVariables-jPUxMC9j.mjs";import{wranglerToAlchemy as F}from"../packem_shared/wranglerToAlchemy-Fh5KfTPB.mjs";import{REQUIRED_COMPATIBILITY_DATE as S,REQUIRED_FLAG as B,validateWrangler as O,validateWranglerConfig as h,validateWranglerProject as G,withTailConsumer as M}from"../packem_shared/REQUIRED_COMPATIBILITY_DATE-BwmyesjD.mjs";export{o as CLOUDFLARE_DRIVER,g as REMOTE_ELIGIBLE_KEYS,S as REQUIRED_COMPATIBILITY_DATE,B as REQUIRED_FLAG,d as WORKERS_CACHE_MIN_DATE,I as WRANGLER_FILES,D as collectWranglerSecretVariables,_ as findWranglerFile,R as injectRemoteFlags,x as isCacheEnabled,c as isRemoteEnvEnabled,f as materializeRemoteWranglerConfig,s as planRemoteBindings,A as readWranglerJsonc,l as reconcileWranglerBindings,t as reconcileWranglerCompatibilityDate,E as reconcileWranglerCrons,W as resolveRemoteEnabled,T as scanWranglerVariablesForSecrets,O as validateWrangler,h as validateWranglerConfig,G as validateWranglerProject,M as withTailConsumer,F as wranglerToAlchemy};
package/dist/index.d.mts CHANGED
@@ -348,6 +348,37 @@ declare const parseDevVariableEntries: (content: string) => {
348
348
  key: string;
349
349
  value: string;
350
350
  }[];
351
+ /**
352
+ * Escape a runtime string for safe literal interpolation into a `RegExp`
353
+ * source. The one canonical implementation — `@lunora/config`'s own
354
+ * `infer-bindings.ts` (type-only-export detection) and `@lunora/cli`'s `env`
355
+ * command both need this and used to carry their own (functionally
356
+ * identical) copy; grammar this fundamental gets one owner like everything
357
+ * else in this module.
358
+ */
359
+ declare const escapeRegExp: (value: string) => string;
360
+ /**
361
+ * Surgically upsert a single `KEY="value"` line in raw `.dev.vars` content,
362
+ * leaving every comment, blank line, and untouched entry verbatim. Rebuilding
363
+ * the whole file from the parsed entry map (an earlier approach) silently
364
+ * dropped all `# …` comments and blank lines — including the documentation the
365
+ * registry installer and scaffolder write — and re-quoted lines the user never
366
+ * touched. If the key already has a line it is replaced in place; otherwise the
367
+ * new line is appended with a single trailing newline. Always quotes the value
368
+ * to preserve a whitespace round-trip; callers (`env set`, `env generate --set`,
369
+ * `deploy`'s minted-secret disclosure) reject newline/`"`/`\` up front so the
370
+ * verbatim quote is safe.
371
+ *
372
+ * Duplicate `KEY=` lines are collapsed down to exactly one. The shared read
373
+ * path (`parseDevVariableEntries` above) is last-wins — it keeps overwriting a
374
+ * Map entry as it walks the file, so with duplicate lines the LAST one wins at
375
+ * read time. Replacing only the first match (as a plain, non-global
376
+ * `.replace()` does) left that later, untouched duplicate still winning at read
377
+ * time — a `set` that silently didn't take effect. The first matching line is
378
+ * replaced in place (preserving its position in the file); every later
379
+ * duplicate is dropped entirely (including its own trailing newline).
380
+ */
381
+ declare const upsertDevVariableLine: (content: string, key: string, value: string) => string;
351
382
  /** The default deploy target — today's behavior for every project. */
352
383
  declare const DEFAULT_DEPLOY_TARGET = "cloudflare";
353
384
  /** The ids a caller may select, for error messages and `--target` help text. */
@@ -703,6 +734,21 @@ interface EnsureDevVariablesResult {
703
734
  generatedKeys: string[];
704
735
  status: EnsureDevVariablesStatus;
705
736
  }
737
+ /**
738
+ * Atomically (over)write a `.dev.vars`-shaped file: write the content to a
739
+ * sibling temp file with owner-only permissions (`mode: 0o600`), then
740
+ * `rename` it over the target. The rename is atomic within one filesystem,
741
+ * so a reader can never observe a half-written file, and an interrupt
742
+ * mid-write can't truncate the target — for a file holding secrets that
743
+ * would destroy every other local value alongside the new one, and the new
744
+ * value itself has no other recoverable copy if it was never disclosed
745
+ * anywhere else (Cloudflare secrets are write-only). On any failure the temp
746
+ * file is removed before the error propagates; never logs or throws the
747
+ * content. Exported so a caller writing to a `.dev.vars`-shaped path outside
748
+ * this module (`lunora deploy`'s minted-secret disclosure) reuses this
749
+ * instead of hand-rolling another copy of the pattern below.
750
+ */
751
+ declare const writeDevVariablesFileAtomically: (path: string, content: string) => void;
706
752
  /**
707
753
  * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
708
754
  *
@@ -956,4 +1002,4 @@ declare const badgeWidth: (_spec: BadgeSpec) => number;
956
1002
  declare const paintBadge: (spec: BadgeSpec) => string;
957
1003
  /** Dim continuation text (a step's chosen answer, shown under the question). */
958
1004
  declare const paintAnswer: (text: string) => string;
959
- export { ACCENT, AGENT_MODE_ENV, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentDetection, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DeployDriver, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DevServerMode, type DevServerState, type DiscoverAgentInfoResult, type DiscoverContainerInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type FillDevSecretsResult, type FrameworkClass, type FrameworkDetection, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, fillDevSecrets, formatLunoraEvent, generateSecretValue, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, secretsForPackages, streamContainerLogs, updateDevServerState, writeDevServerState, writeLinkedProject };
1005
+ export { ACCENT, AGENT_MODE_ENV, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentDetection, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DeployDriver, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DevServerMode, type DevServerState, type DiscoverAgentInfoResult, type DiscoverContainerInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type FillDevSecretsResult, type FrameworkClass, type FrameworkDetection, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, escapeRegExp, fillDevSecrets, formatLunoraEvent, generateSecretValue, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, secretsForPackages, streamContainerLogs, updateDevServerState, upsertDevVariableLine, writeDevServerState, writeDevVariablesFileAtomically, writeLinkedProject };
package/dist/index.d.ts CHANGED
@@ -348,6 +348,37 @@ declare const parseDevVariableEntries: (content: string) => {
348
348
  key: string;
349
349
  value: string;
350
350
  }[];
351
+ /**
352
+ * Escape a runtime string for safe literal interpolation into a `RegExp`
353
+ * source. The one canonical implementation — `@lunora/config`'s own
354
+ * `infer-bindings.ts` (type-only-export detection) and `@lunora/cli`'s `env`
355
+ * command both need this and used to carry their own (functionally
356
+ * identical) copy; grammar this fundamental gets one owner like everything
357
+ * else in this module.
358
+ */
359
+ declare const escapeRegExp: (value: string) => string;
360
+ /**
361
+ * Surgically upsert a single `KEY="value"` line in raw `.dev.vars` content,
362
+ * leaving every comment, blank line, and untouched entry verbatim. Rebuilding
363
+ * the whole file from the parsed entry map (an earlier approach) silently
364
+ * dropped all `# …` comments and blank lines — including the documentation the
365
+ * registry installer and scaffolder write — and re-quoted lines the user never
366
+ * touched. If the key already has a line it is replaced in place; otherwise the
367
+ * new line is appended with a single trailing newline. Always quotes the value
368
+ * to preserve a whitespace round-trip; callers (`env set`, `env generate --set`,
369
+ * `deploy`'s minted-secret disclosure) reject newline/`"`/`\` up front so the
370
+ * verbatim quote is safe.
371
+ *
372
+ * Duplicate `KEY=` lines are collapsed down to exactly one. The shared read
373
+ * path (`parseDevVariableEntries` above) is last-wins — it keeps overwriting a
374
+ * Map entry as it walks the file, so with duplicate lines the LAST one wins at
375
+ * read time. Replacing only the first match (as a plain, non-global
376
+ * `.replace()` does) left that later, untouched duplicate still winning at read
377
+ * time — a `set` that silently didn't take effect. The first matching line is
378
+ * replaced in place (preserving its position in the file); every later
379
+ * duplicate is dropped entirely (including its own trailing newline).
380
+ */
381
+ declare const upsertDevVariableLine: (content: string, key: string, value: string) => string;
351
382
  /** The default deploy target — today's behavior for every project. */
352
383
  declare const DEFAULT_DEPLOY_TARGET = "cloudflare";
353
384
  /** The ids a caller may select, for error messages and `--target` help text. */
@@ -703,6 +734,21 @@ interface EnsureDevVariablesResult {
703
734
  generatedKeys: string[];
704
735
  status: EnsureDevVariablesStatus;
705
736
  }
737
+ /**
738
+ * Atomically (over)write a `.dev.vars`-shaped file: write the content to a
739
+ * sibling temp file with owner-only permissions (`mode: 0o600`), then
740
+ * `rename` it over the target. The rename is atomic within one filesystem,
741
+ * so a reader can never observe a half-written file, and an interrupt
742
+ * mid-write can't truncate the target — for a file holding secrets that
743
+ * would destroy every other local value alongside the new one, and the new
744
+ * value itself has no other recoverable copy if it was never disclosed
745
+ * anywhere else (Cloudflare secrets are write-only). On any failure the temp
746
+ * file is removed before the error propagates; never logs or throws the
747
+ * content. Exported so a caller writing to a `.dev.vars`-shaped path outside
748
+ * this module (`lunora deploy`'s minted-secret disclosure) reuses this
749
+ * instead of hand-rolling another copy of the pattern below.
750
+ */
751
+ declare const writeDevVariablesFileAtomically: (path: string, content: string) => void;
706
752
  /**
707
753
  * Reconcile the project's `.dev.vars` with its `.dev.vars.example`:
708
754
  *
@@ -956,4 +1002,4 @@ declare const badgeWidth: (_spec: BadgeSpec) => number;
956
1002
  declare const paintBadge: (spec: BadgeSpec) => string;
957
1003
  /** Dim continuation text (a step's chosen answer, shown under the question). */
958
1004
  declare const paintAnswer: (text: string) => string;
959
- export { ACCENT, AGENT_MODE_ENV, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentDetection, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DeployDriver, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DevServerMode, type DevServerState, type DiscoverAgentInfoResult, type DiscoverContainerInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type FillDevSecretsResult, type FrameworkClass, type FrameworkDetection, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, fillDevSecrets, formatLunoraEvent, generateSecretValue, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, secretsForPackages, streamContainerLogs, updateDevServerState, writeDevServerState, writeLinkedProject };
1005
+ export { ACCENT, AGENT_MODE_ENV, AGENT_RULES_DIR, AGENT_RULES_HINT, AGENT_RULES_HINT_ENV, type AddIndexEdit, type AddOptionalColumnEdit, type AddTableEdit, type AdditiveEdit, type AgentDetection, type AgentRulesStatus, type ApplyEditResult, type ApplyFailureReason, type AugmentPlan, BADGES, BADGE_COLUMN_WIDTH, type BadgeName, type BadgeSpec, type ClaimDevServerStateResult, type ContainerLogLevel, type ContainerLogLine, type ContainerLogSource, type ContainerLogStreamHandle, type ContainerLogStreamOptions, DEFAULT_DEPLOY_TARGET, DEV_DAEMON_ENV, DEV_HANDOFF_ENV, DEV_LOG_FILE, DEV_LOG_FILE_ENV, DEV_STATE_DIR, DEV_STATE_FILE, DEV_VARS_EXAMPLE_FILE, DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, type DeployDriver, type DestructiveEdit, type DetectedFramework, type DevSecretsFillPlan, type DevServerMode, type DevServerState, type DiscoverAgentInfoResult, type DiscoverContainerInfoResult, type DiscoverWorkflowInfoResult, type DockerLike, type EnsureDevVariablesDeps, type EnsureDevVariablesResult, type EnsureDevVariablesStatus, type FillDevSecretsResult, type FrameworkClass, type FrameworkDetection, LINKED_PROJECT_DIR, LINKED_PROJECT_FILE, LUNA_ART, LUNA_BUNNY, LUNA_NAME, LUNA_SIGNOFF, LUNORA_CONFIG_FILE, LUNORA_EVENT_SOURCE, LUNORA_SKILL_NAMES, type LevelBadgeName, type LinkedProject, type LunoraFormattedLine, type LunoraLineLevel, type LunoraProjectConfig, LunoraReporter, type MultiSelectOption, PACKAGE_SECRETS_REGISTRY, type ParseSchemaResult, ROOT_SKILL_NAME, type RemotePreference, STEP_BADGE_NAMES, type ScaffoldPlan, type SchemaColumn, type SchemaEdit, type SchemaIndex, type SchemaTable, type SecretEntry, type SelectOption, type StepBadgeName, applyAdditiveEdit, badgeLead, badgeWidth, buildPackageSecretsBlock, claimAgentRulesHint, claimDevServerState, classifyEdit, clearDevServerState, createConfirm, deployTargetIds, detectAgentRules, detectAiAgent, detectFramework, discoverAgentInfo, discoverContainerInfo, discoverWorkflowInfo, ensureDevVariables, ensureDevVariablesExample as ensureDevVarsExample, escapeRegExp, fillDevSecrets, formatLunoraEvent, generateSecretValue, interpretRemote, isInteractive, isMintableSecretKey, isPlaceholderValue, isProcessAlive, isRecordedProcessCurrent, padBadge, paintAnswer, paintBadge, parseDevVariableEntries, parseSchema, planDevSecretsFill, planDevVariablesAugment, planDevVariablesScaffold, promptMultiSelect, promptSelect, promptText, promptYesNo, readDevServerState, readLinkedProject, readLiveDevServerState, readProjectDependencyNames, readProjectRemotePreference, readProjectTarget, requiredSecrets, resolveDeployDriver, resolveProjectTarget, resolveTargetOrThrow, secretsForPackages, streamContainerLogs, updateDevServerState, upsertDevVariableLine, writeDevServerState, writeDevVariablesFileAtomically, writeLinkedProject };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{AGENT_MODE_ENV as t,detectAiAgent as o}from"./packem_shared/AGENT_MODE_ENV-B54hVQ_w.mjs";import{discoverAgentInfo as E}from"./packem_shared/discoverAgentInfo-BJm0QtoI.mjs";import{AGENT_RULES_DIR as _,AGENT_RULES_HINT as s,AGENT_RULES_HINT_ENV as p,LUNORA_SKILL_NAMES as c,ROOT_SKILL_NAME as l,claimAgentRulesHint as n,detectAgentRules as d}from"./packem_shared/AGENT_RULES_DIR-hP9TiDNx.mjs";import{discoverContainerInfo as A}from"./packem_shared/discoverContainerInfo-CYG9j2LY.mjs";import{streamContainerLogs as f}from"./packem_shared/streamContainerLogs-BPYBNrWS.mjs";import{detectFramework as L,readProjectDependencyNames as N}from"./packem_shared/detectFramework-VTQfCNXy.mjs";import{DEV_DAEMON_ENV as T,DEV_HANDOFF_ENV as R,DEV_LOG_FILE as I,DEV_LOG_FILE_ENV as x,DEV_STATE_DIR as V,DEV_STATE_FILE as P,claimDevServerState as g,clearDevServerState as F,isProcessAlive as O,isRecordedProcessCurrent as u,readDevServerState as G,readLiveDevServerState as U,updateDevServerState as C,writeDevServerState as M}from"./packem_shared/DEV_DAEMON_ENV-D9Z83rlU.mjs";import{DEV_VARS_EXAMPLE_FILE as B,DEV_VARS_FILE as b,DEV_VARS_KEY_PATTERN as k,parseDevVariableEntries as w}from"./packem_shared/DEV_VARS_EXAMPLE_FILE-DX6xGbpr.mjs";import{DEFAULT_DEPLOY_TARGET as j,deployTargetIds as h,resolveDeployDriver as H}from"./packem_shared/DEFAULT_DEPLOY_TARGET-DMsFUSw7.mjs";import{inferLunoraBindings as W,packageNamesFromBindings as J}from"./packem_shared/inferLunoraBindings-DhfIOVhl.mjs";import{LINKED_PROJECT_DIR as X,LINKED_PROJECT_FILE as z,readLinkedProject as Q,writeLinkedProject as Z}from"./packem_shared/LINKED_PROJECT_DIR-CMzUvj-1.mjs";import{LUNORA_EVENT_SOURCE as ee,formatLunoraEvent as re}from"./packem_shared/LUNORA_EVENT_SOURCE-ZclWASXS.mjs";import{default as oe}from"./packem_shared/LunoraReporter-BnXUqh8t.mjs";import{PACKAGE_SECRETS_REGISTRY as Ee,secretsForPackages as ie}from"./packem_shared/PACKAGE_SECRETS_REGISTRY-BgmvEPA-.mjs";import{LUNORA_CONFIG_FILE as se,interpretRemote as pe,readProjectRemotePreference as ce,readProjectTarget as le,resolveProjectTarget as ne,resolveTargetOrThrow as de}from"./packem_shared/LUNORA_CONFIG_FILE-4XwJQRl9.mjs";import{createConfirm as Ae,isInteractive as Se,promptMultiSelect as fe,promptSelect as De,promptText as Le,promptYesNo as Ne}from"./packem_shared/createConfirm-7IL0kZyE.mjs";import{buildPackageSecretsBlock as Te,ensureDevVariables as Re,ensureDevVarsExample as Ie,fillDevSecrets as xe,generateSecretValue as Ve,isMintableSecretKey as Pe,isPlaceholderValue as ge,planDevSecretsFill as Fe,planDevVariablesAugment as Oe,planDevVariablesScaffold as ue,requiredSecrets as Ge}from"./packem_shared/buildPackageSecretsBlock-BpvaZzQ8.mjs";import{applyAdditiveEdit as Ce,classifyEdit as Me}from"./packem_shared/applyAdditiveEdit-Cff30cSa.mjs";import{parseSchema as Be}from"./packem_shared/parseSchema-BQjgz6bk.mjs";import{classifyPolicyEdit as ke,scaffoldPolicyFile as we,wireRlsIntoProcedure as Ke}from"./packem_shared/classifyPolicyEdit-BzC_MfYK.mjs";import{discoverSchemaInfo as he}from"./packem_shared/discoverSchemaInfo-C8X9mo-i.mjs";import{ACCENT as Ye,BADGES as We,BADGE_COLUMN_WIDTH as Je,LUNA_ART as qe,LUNA_BUNNY as Xe,LUNA_NAME as ze,LUNA_SIGNOFF as Qe,STEP_BADGE_NAMES as Ze,badgeLead as $e,badgeWidth as er,padBadge as rr,paintAnswer as tr,paintBadge as or}from"./packem_shared/ACCENT-CLeV5v0K.mjs";import{discoverWorkflowInfo as Er}from"./packem_shared/discoverWorkflowInfo-Bbc0u2gE.mjs";export{Ye as ACCENT,t as AGENT_MODE_ENV,_ as AGENT_RULES_DIR,s as AGENT_RULES_HINT,p as AGENT_RULES_HINT_ENV,We as BADGES,Je as BADGE_COLUMN_WIDTH,j as DEFAULT_DEPLOY_TARGET,T as DEV_DAEMON_ENV,R as DEV_HANDOFF_ENV,I as DEV_LOG_FILE,x as DEV_LOG_FILE_ENV,V as DEV_STATE_DIR,P as DEV_STATE_FILE,B as DEV_VARS_EXAMPLE_FILE,b as DEV_VARS_FILE,k as DEV_VARS_KEY_PATTERN,X as LINKED_PROJECT_DIR,z as LINKED_PROJECT_FILE,qe as LUNA_ART,Xe as LUNA_BUNNY,ze as LUNA_NAME,Qe as LUNA_SIGNOFF,se as LUNORA_CONFIG_FILE,ee as LUNORA_EVENT_SOURCE,c as LUNORA_SKILL_NAMES,oe as LunoraReporter,Ee as PACKAGE_SECRETS_REGISTRY,l as ROOT_SKILL_NAME,Ze as STEP_BADGE_NAMES,Ce as applyAdditiveEdit,$e as badgeLead,er as badgeWidth,Te as buildPackageSecretsBlock,n as claimAgentRulesHint,g as claimDevServerState,Me as classifyEdit,ke as classifyPolicyEdit,F as clearDevServerState,Ae as createConfirm,h as deployTargetIds,d as detectAgentRules,o as detectAiAgent,L as detectFramework,E as discoverAgentInfo,A as discoverContainerInfo,he as discoverSchemaInfo,Er as discoverWorkflowInfo,Re as ensureDevVariables,Ie as ensureDevVarsExample,xe as fillDevSecrets,re as formatLunoraEvent,Ve as generateSecretValue,W as inferLunoraBindings,pe as interpretRemote,Se as isInteractive,Pe as isMintableSecretKey,ge as isPlaceholderValue,O as isProcessAlive,u as isRecordedProcessCurrent,J as packageNamesFromBindings,rr as padBadge,tr as paintAnswer,or as paintBadge,w as parseDevVariableEntries,Be as parseSchema,Fe as planDevSecretsFill,Oe as planDevVariablesAugment,ue as planDevVariablesScaffold,fe as promptMultiSelect,De as promptSelect,Le as promptText,Ne as promptYesNo,G as readDevServerState,Q as readLinkedProject,U as readLiveDevServerState,N as readProjectDependencyNames,ce as readProjectRemotePreference,le as readProjectTarget,Ge as requiredSecrets,H as resolveDeployDriver,ne as resolveProjectTarget,de as resolveTargetOrThrow,we as scaffoldPolicyFile,ie as secretsForPackages,f as streamContainerLogs,C as updateDevServerState,Ke as wireRlsIntoProcedure,M as writeDevServerState,Z as writeLinkedProject};
1
+ import{AGENT_MODE_ENV as t,detectAiAgent as o}from"./packem_shared/AGENT_MODE_ENV-B54hVQ_w.mjs";import{discoverAgentInfo as E}from"./packem_shared/discoverAgentInfo-BJm0QtoI.mjs";import{AGENT_RULES_DIR as _,AGENT_RULES_HINT as s,AGENT_RULES_HINT_ENV as p,LUNORA_SKILL_NAMES as l,ROOT_SKILL_NAME as c,claimAgentRulesHint as n,detectAgentRules as m}from"./packem_shared/AGENT_RULES_DIR-hP9TiDNx.mjs";import{discoverContainerInfo as d}from"./packem_shared/discoverContainerInfo-CYG9j2LY.mjs";import{streamContainerLogs as D}from"./packem_shared/streamContainerLogs-BPYBNrWS.mjs";import{detectFramework as L,readProjectDependencyNames as N}from"./packem_shared/detectFramework-VTQfCNXy.mjs";import{DEV_DAEMON_ENV as T,DEV_HANDOFF_ENV as R,DEV_LOG_FILE as I,DEV_LOG_FILE_ENV as V,DEV_STATE_DIR as x,DEV_STATE_FILE as P,claimDevServerState as g,clearDevServerState as F,isProcessAlive as u,isRecordedProcessCurrent as O,readDevServerState as G,readLiveDevServerState as U,updateDevServerState as C,writeDevServerState as b}from"./packem_shared/DEV_DAEMON_ENV-D9Z83rlU.mjs";import{DEV_VARS_EXAMPLE_FILE as M,DEV_VARS_FILE as B,DEV_VARS_KEY_PATTERN as k,escapeRegExp as w,parseDevVariableEntries as K,upsertDevVariableLine as j}from"./packem_shared/DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs";import{DEFAULT_DEPLOY_TARGET as H,deployTargetIds as Y,resolveDeployDriver as W}from"./packem_shared/DEFAULT_DEPLOY_TARGET-CBoD0Zha.mjs";import{inferLunoraBindings as q,packageNamesFromBindings as X}from"./packem_shared/inferLunoraBindings-tf2nExl_.mjs";import{LINKED_PROJECT_DIR as Q,LINKED_PROJECT_FILE as Z,readLinkedProject as $,writeLinkedProject as ee}from"./packem_shared/LINKED_PROJECT_DIR-CMzUvj-1.mjs";import{LUNORA_EVENT_SOURCE as te,formatLunoraEvent as oe}from"./packem_shared/LUNORA_EVENT_SOURCE-ZclWASXS.mjs";import{default as Ee}from"./packem_shared/LunoraReporter-BnXUqh8t.mjs";import{PACKAGE_SECRETS_REGISTRY as _e,secretsForPackages as se}from"./packem_shared/PACKAGE_SECRETS_REGISTRY-BgmvEPA-.mjs";import{LUNORA_CONFIG_FILE as le,interpretRemote as ce,readProjectRemotePreference as ne,readProjectTarget as me,resolveProjectTarget as Ae,resolveTargetOrThrow as de}from"./packem_shared/LUNORA_CONFIG_FILE-DoVUD52W.mjs";import{createConfirm as De,isInteractive as fe,promptMultiSelect as Le,promptSelect as Ne,promptText as ve,promptYesNo as Te}from"./packem_shared/createConfirm-7IL0kZyE.mjs";import{buildPackageSecretsBlock as Ie,ensureDevVariables as Ve,ensureDevVarsExample as xe,fillDevSecrets as Pe,generateSecretValue as ge,isMintableSecretKey as Fe,isPlaceholderValue as ue,planDevSecretsFill as Oe,planDevVariablesAugment as Ge,planDevVariablesScaffold as Ue,requiredSecrets as Ce,writeDevVariablesFileAtomically as be}from"./packem_shared/buildPackageSecretsBlock-DlAPMSwt.mjs";import{applyAdditiveEdit as Me,classifyEdit as Be}from"./packem_shared/applyAdditiveEdit-Cff30cSa.mjs";import{parseSchema as we}from"./packem_shared/parseSchema-BQjgz6bk.mjs";import{classifyPolicyEdit as je,scaffoldPolicyFile as he,wireRlsIntoProcedure as He}from"./packem_shared/classifyPolicyEdit-BzC_MfYK.mjs";import{discoverSchemaInfo as We}from"./packem_shared/discoverSchemaInfo-C8X9mo-i.mjs";import{ACCENT as qe,BADGES as Xe,BADGE_COLUMN_WIDTH as ze,LUNA_ART as Qe,LUNA_BUNNY as Ze,LUNA_NAME as $e,LUNA_SIGNOFF as er,STEP_BADGE_NAMES as rr,badgeLead as tr,badgeWidth as or,padBadge as ar,paintAnswer as Er,paintBadge as ir}from"./packem_shared/ACCENT-CLeV5v0K.mjs";import{discoverWorkflowInfo as sr}from"./packem_shared/discoverWorkflowInfo-Bbc0u2gE.mjs";export{qe as ACCENT,t as AGENT_MODE_ENV,_ as AGENT_RULES_DIR,s as AGENT_RULES_HINT,p as AGENT_RULES_HINT_ENV,Xe as BADGES,ze as BADGE_COLUMN_WIDTH,H as DEFAULT_DEPLOY_TARGET,T as DEV_DAEMON_ENV,R as DEV_HANDOFF_ENV,I as DEV_LOG_FILE,V as DEV_LOG_FILE_ENV,x as DEV_STATE_DIR,P as DEV_STATE_FILE,M as DEV_VARS_EXAMPLE_FILE,B as DEV_VARS_FILE,k as DEV_VARS_KEY_PATTERN,Q as LINKED_PROJECT_DIR,Z as LINKED_PROJECT_FILE,Qe as LUNA_ART,Ze as LUNA_BUNNY,$e as LUNA_NAME,er as LUNA_SIGNOFF,le as LUNORA_CONFIG_FILE,te as LUNORA_EVENT_SOURCE,l as LUNORA_SKILL_NAMES,Ee as LunoraReporter,_e as PACKAGE_SECRETS_REGISTRY,c as ROOT_SKILL_NAME,rr as STEP_BADGE_NAMES,Me as applyAdditiveEdit,tr as badgeLead,or as badgeWidth,Ie as buildPackageSecretsBlock,n as claimAgentRulesHint,g as claimDevServerState,Be as classifyEdit,je as classifyPolicyEdit,F as clearDevServerState,De as createConfirm,Y as deployTargetIds,m as detectAgentRules,o as detectAiAgent,L as detectFramework,E as discoverAgentInfo,d as discoverContainerInfo,We as discoverSchemaInfo,sr as discoverWorkflowInfo,Ve as ensureDevVariables,xe as ensureDevVarsExample,w as escapeRegExp,Pe as fillDevSecrets,oe as formatLunoraEvent,ge as generateSecretValue,q as inferLunoraBindings,ce as interpretRemote,fe as isInteractive,Fe as isMintableSecretKey,ue as isPlaceholderValue,u as isProcessAlive,O as isRecordedProcessCurrent,X as packageNamesFromBindings,ar as padBadge,Er as paintAnswer,ir as paintBadge,K as parseDevVariableEntries,we as parseSchema,Oe as planDevSecretsFill,Ge as planDevVariablesAugment,Ue as planDevVariablesScaffold,Le as promptMultiSelect,Ne as promptSelect,ve as promptText,Te as promptYesNo,G as readDevServerState,$ as readLinkedProject,U as readLiveDevServerState,N as readProjectDependencyNames,ne as readProjectRemotePreference,me as readProjectTarget,Ce as requiredSecrets,W as resolveDeployDriver,Ae as resolveProjectTarget,de as resolveTargetOrThrow,he as scaffoldPolicyFile,se as secretsForPackages,D as streamContainerLogs,C as updateDevServerState,j as upsertDevVariableLine,He as wireRlsIntoProcedure,b as writeDevServerState,be as writeDevVariablesFileAtomically,ee as writeLinkedProject};
@@ -0,0 +1 @@
1
+ const i=e=>{const r=Array.isArray(e)?e[0]:e;return typeof r=="string"?r.trim().toLowerCase():void 0},a=e=>{if(e===void 0||e==="")return!0;const r=e.toLowerCase(),o=r.startsWith("::ffff:")?r.slice(7):r;return o==="::1"?!0:o.startsWith("127.")},d=e=>{if(e===void 0)return;if(e.startsWith("[")){const o=e.indexOf("]");return o===-1?e.slice(1):e.slice(1,o)}const r=e.indexOf(":");return r===-1?e:e.slice(0,r)},u=new Set(["0.0.0.0","127.0.0.1","::1","localhost"]),c=["x-forwarded-for","x-forwarded-host","x-forwarded-proto","forwarded"],s="LUNORA_STUDIO_ALLOW_FORWARDED",l=()=>process.env[s]==="1",f=(e,r)=>{if(!a(e.socket?.remoteAddress??void 0))return"Lunora studio is only available on loopback connections in dev.";const o=d(i(e.headers?.host));if(o!==void 0&&!u.has(o))return"Lunora studio rejects a non-localhost Host header in dev.";const t=c.find(n=>e.headers?.[n]!==void 0);if(t!==void 0&&!l())return r?.warnOnce?.(`[lunora] studio: refusing a request carrying the "${t}" header — this dev server is being reached through a proxy/tunnel (Codespaces, devcontainers, Gitpod, Cloud Workstations, ngrok, and Docker reverse proxies all add this). If this is YOUR trusted dev tunnel, set ${s}=1 to allow it.`),`Lunora studio refuses a proxied request in dev (saw the "${t}" header). If you're intentionally running behind a trusted dev tunnel/proxy (e.g. Codespaces, devcontainers, Gitpod, ngrok), set ${s}=1 to allow it.`};export{s as ALLOW_FORWARDED_ENV,i as headerValue,a as isLoopbackAddress,f as transportRejectionReason};
@@ -1 +1 @@
1
- import{inferLunoraBindings as c}from"./inferLunoraBindings-DhfIOVhl.mjs";import{reconcileWranglerBindings as p}from"./reconcileWranglerBindings-CHFT6zqp.mjs";import{reconcileWranglerCompatibilityDate as u}from"./reconcileWranglerCompatibilityDate-BXNiEQ7p.mjs";import{reconcileWranglerCrons as g}from"./reconcileWranglerCrons-BmQa_kGL.mjs";const m=(e,r)=>{const a=e.durableObjects.map(t=>({className:t.className,exported:!0,name:t.binding})),n=e.queues.map(t=>({name:t.name})),s=e.workflows.map(t=>({exported:t.exported,name:t.exportName}));return{containers:e.containers.map(t=>({exported:t.exported,name:t.exportName})),crons:[...r],globalDatabase:e.needsD1,keyValueStore:e.usesKv,objectStorage:e.usesStorage,queues:n,shardNamespaces:a,signals:[...e.signals],workflows:s}},i=(e,r)=>{try{const a=r();return{added:a.added??[],changed:a.changed,configPath:a.wranglerPath,warnings:a.warnings??[]}}catch(a){const n=a instanceof Error?a.message:String(a);return{added:[],changed:!1,warnings:[`${e} skipped: ${n}`]}}},l={deploy:e=>{const r=e.preview===!0?["versions","upload"]:["deploy"];return e.entry!==void 0&&r.push(e.entry),e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),e.dryRun===!0&&r.push("--dry-run"),e.outDir!==void 0&&r.push("--outdir",e.outDir,"--metafile"),{args:r,tool:"wrangler"}},dev:e=>{const r=["dev"];return e.configPath!==void 0&&r.push("--config",e.configPath),e.environment!==void 0&&r.push("--env",e.environment),r.push(...e.extraArgs??[]),{args:r,tool:"wrangler"}},secretList:e=>{const r=["secret","list","--format","json"];return e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}},secretPut:e=>{const r=["secret","put",e.key??""];return e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}},tail:e=>{const r=["tail"];return e.worker!==void 0&&r.push(e.worker),e.environment!==void 0&&r.push("--env",e.environment),e.format!==void 0&&r.push("--format",e.format),e.status!==void 0&&r.push("--status",e.status),e.search!==void 0&&r.push("--search",e.search),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}}},w={id:"cloudflare",infer:async e=>{const r=await c({projectRoot:e.projectRoot});return m(r,e.crons??[])},name:"Cloudflare",provision:async e=>{const r=e.crons??[],a=await(async()=>{try{const o=await c({projectRoot:e.projectRoot});return i("binding inference",()=>p(e.projectRoot,o))}catch(o){const d=o instanceof Error?o.message:String(o);return{added:[],changed:!1,warnings:[`binding inference skipped: ${d}`]}}})(),n=i("compatibility date sync",()=>u(e.projectRoot)),s=i("cron trigger sync",()=>g(e.projectRoot,r)),t=[a,{...n,added:n.changed?["compatibility_date"]:[]},{...s,added:s.changed?[`${String(r.length)} cron trigger(s)`]:[]}];return{added:t.flatMap(o=>o.added),changed:t.some(o=>o.changed),configPath:t.find(o=>o.configPath!==void 0)?.configPath,warnings:t.flatMap(o=>o.warnings)}},toolchain:l};export{w as default};
1
+ import{inferLunoraBindings as c}from"./inferLunoraBindings-tf2nExl_.mjs";import{reconcileWranglerBindings as p}from"./reconcileWranglerBindings-BCwTpuLO.mjs";import{reconcileWranglerCompatibilityDate as u}from"./reconcileWranglerCompatibilityDate-BXNiEQ7p.mjs";import{reconcileWranglerCrons as g}from"./reconcileWranglerCrons-BmQa_kGL.mjs";const m=(e,r)=>{const a=e.durableObjects.map(t=>({className:t.className,exported:!0,name:t.binding})),n=e.queues.map(t=>({name:t.name})),s=e.workflows.map(t=>({exported:t.exported,name:t.exportName}));return{containers:e.containers.map(t=>({exported:t.exported,name:t.exportName})),crons:[...r],globalDatabase:e.needsD1,keyValueStore:e.usesKv,objectStorage:e.usesStorage,queues:n,shardNamespaces:a,signals:[...e.signals],workflows:s}},i=(e,r)=>{try{const a=r();return{added:a.added??[],changed:a.changed,configPath:a.wranglerPath,warnings:a.warnings??[]}}catch(a){const n=a instanceof Error?a.message:String(a);return{added:[],changed:!1,warnings:[`${e} skipped: ${n}`]}}},l={deploy:e=>{const r=e.preview===!0?["versions","upload"]:["deploy"];return e.entry!==void 0&&r.push(e.entry),e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),e.dryRun===!0&&r.push("--dry-run"),e.outDir!==void 0&&r.push("--outdir",e.outDir,"--metafile"),{args:r,tool:"wrangler"}},dev:e=>{const r=["dev"];return e.configPath!==void 0&&r.push("--config",e.configPath),e.environment!==void 0&&r.push("--env",e.environment),r.push(...e.extraArgs??[]),{args:r,tool:"wrangler"}},secretList:e=>{const r=["secret","list","--format","json"];return e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}},secretPut:e=>{const r=["secret","put",e.key??""];return e.environment!==void 0&&r.push("--env",e.environment),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}},tail:e=>{const r=["tail"];return e.worker!==void 0&&r.push(e.worker),e.environment!==void 0&&r.push("--env",e.environment),e.format!==void 0&&r.push("--format",e.format),e.status!==void 0&&r.push("--status",e.status),e.search!==void 0&&r.push("--search",e.search),e.temporary===!0&&r.push("--temporary"),{args:r,tool:"wrangler"}}},w={id:"cloudflare",infer:async e=>{const r=await c({projectRoot:e.projectRoot});return m(r,e.crons??[])},name:"Cloudflare",provision:async e=>{const r=e.crons??[],a=await(async()=>{try{const o=await c({projectRoot:e.projectRoot});return i("binding inference",()=>p(e.projectRoot,o))}catch(o){const d=o instanceof Error?o.message:String(o);return{added:[],changed:!1,warnings:[`binding inference skipped: ${d}`]}}})(),n=i("compatibility date sync",()=>u(e.projectRoot)),s=i("cron trigger sync",()=>g(e.projectRoot,r)),t=[a,{...n,added:n.changed?["compatibility_date"]:[]},{...s,added:s.changed?[`${String(r.length)} cron trigger(s)`]:[]}];return{added:t.flatMap(o=>o.added),changed:t.some(o=>o.changed),configPath:t.find(o=>o.configPath!==void 0)?.configPath,warnings:t.flatMap(o=>o.warnings)}},toolchain:l};export{w as default};
@@ -1 +1 @@
1
- import t from"./CLOUDFLARE_DRIVER-BllWt7Xx.mjs";const a="cloudflare",r={cloudflare:t},l=()=>Object.keys(r).toSorted((e,o)=>e.localeCompare(o)),n=(e=a)=>{const o=r[e];if(o===void 0)throw new Error(`unknown deploy target "${e}" — available targets: ${l().join(", ")}`);return o};export{a as DEFAULT_DEPLOY_TARGET,l as deployTargetIds,n as resolveDeployDriver};
1
+ import t from"./CLOUDFLARE_DRIVER-DS7cF7Cl.mjs";const a="cloudflare",r={cloudflare:t},l=()=>Object.keys(r).toSorted((e,o)=>e.localeCompare(o)),n=(e=a)=>{const o=r[e];if(o===void 0)throw new Error(`unknown deploy target "${e}" — available targets: ${l().join(", ")}`);return o};export{a as DEFAULT_DEPLOY_TARGET,l as deployTargetIds,n as resolveDeployDriver};
@@ -0,0 +1,6 @@
1
+ const p=".dev.vars",V=".dev.vars.example",l=/^[A-Za-z_]\w*$/u,u=/\r?\n/u,E=t=>t.length>=2&&(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))?t.slice(1,-1):t,c=t=>{const e=t.trim();if(e===""||e.startsWith("#"))return;const r=e.indexOf("=");if(r<=0)return;const s=e.slice(0,r).trim();if(l.test(s))return{key:s,value:e.slice(r+1).trim()}},v=t=>{const e=[];for(const r of t.split(u)){const s=c(r);s&&e.push({key:s.key,value:E(s.value)})}return e},$=t=>t.replaceAll(/[.*+?^${}()|[\]\\]/gu,String.raw`\$&`),n=(t,e)=>new RegExp(String.raw`^[ \t]*${$(t)}[ \t]*=.*(\r?\n|$)`,e?"gmu":"mu"),_=(t,e,r)=>{const s=`${e}="${r}"`;if(!n(e,!1).test(t))return t===""?`${s}
2
+ `:t.endsWith(`
3
+ `)?`${t}${s}
4
+ `:`${t}
5
+ ${s}
6
+ `;let a=!1;return t.replace(n(e,!0),(o,i)=>a?"":(a=!0,`${s}${i}`))};export{V as DEV_VARS_EXAMPLE_FILE,p as DEV_VARS_FILE,l as DEV_VARS_KEY_PATTERN,u as DEV_VARS_NEWLINE,$ as escapeRegExp,v as parseDevVariableEntries,c as splitDevVariableLine,E as unquoteDevVariable,_ as upsertDevVariableLine};
@@ -1 +1 @@
1
- import{existsSync as n,readFileSync as s}from"node:fs";import{readProjectTarget as i}from"@lunora/codegen";import{parse as l}from"jsonc-parser";import{DEFAULT_DEPLOY_TARGET as c,resolveDeployDriver as f}from"./DEFAULT_DEPLOY_TARGET-DMsFUSw7.mjs";import{join as m}from"node:path";const p="lunora.json",u=r=>{if(typeof r=="boolean")return r;if(r!==null&&typeof r=="object")return!0},j=r=>{const e=m(r,p);if(!n(e))return;let t;try{t=s(e,"utf8")}catch{return}const a=[],o=l(t,a,{allowTrailingComma:!0});if(!(a.length>0||o===null||typeof o!="object"))return o},E=r=>u(j(r)?.remote),T=r=>i(r),y=(r,e)=>e??T(r)??c,F=(r,e)=>{const t=y(r,e);return f(t),t};export{p as LUNORA_CONFIG_FILE,u as interpretRemote,E as readProjectRemotePreference,T as readProjectTarget,y as resolveProjectTarget,F as resolveTargetOrThrow};
1
+ import{existsSync as n,readFileSync as s}from"node:fs";import{readProjectTarget as i}from"@lunora/codegen";import{parse as l}from"jsonc-parser";import{DEFAULT_DEPLOY_TARGET as c,resolveDeployDriver as f}from"./DEFAULT_DEPLOY_TARGET-CBoD0Zha.mjs";import{join as m}from"node:path";const p="lunora.json",u=r=>{if(typeof r=="boolean")return r;if(r!==null&&typeof r=="object")return!0},j=r=>{const e=m(r,p);if(!n(e))return;let t;try{t=s(e,"utf8")}catch{return}const a=[],o=l(t,a,{allowTrailingComma:!0});if(!(a.length>0||o===null||typeof o!="object"))return o},E=r=>u(j(r)?.remote),T=r=>i(r),y=(r,e)=>e??T(r)??c,F=(r,e)=>{const t=y(r,e);return f(t),t};export{p as LUNORA_CONFIG_FILE,u as interpretRemote,E as readProjectRemotePreference,T as readProjectTarget,y as resolveProjectTarget,F as resolveTargetOrThrow};
@@ -0,0 +1 @@
1
+ import{existsSync as m,readFileSync as k}from"node:fs";import{dirname as w,join as g}from"node:path";import{WORKER_ENTRY_FALLBACKS as M}from"./inferLunoraBindings-tf2nExl_.mjs";import{discoverSchemaInfo as q}from"./discoverSchemaInfo-C8X9mo-i.mjs";import{isCacheEnabled as O,WORKERS_CACHE_MIN_DATE as f}from"./WORKERS_CACHE_MIN_DATE-B1h_wNDN.mjs";import{findWranglerFile as R,readWranglerJsonc as x}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const h="2026-04-07",Oe="web_socket_auto_reply_to_close",y=/^\d{4}-\d{2}-\d{2}$/,v=["d1_databases","durable_objects","kv_namespaces","queues","r2_buckets","secrets_store_secrets","services","tail_consumers","vars","vectorize","workflows"],_=["assets","compatibility_date","exports","logpush","main","migrations","observability","placement"],j=(e,s)=>{if(s===void 0)return{merged:e,unverifiedKeys:[]};const t=e.env?.[s];if(t===void 0){const c=Object.keys(e.env??{}).toSorted((d,l)=>d.localeCompare(l)),o=c.length>0?` (declared: ${c.join(", ")}).`:" (no environments are declared).";return{error:`--env "${s}" names no environment declared in wrangler.jsonc's "env" block${o}`,merged:e,unverifiedKeys:[]}}const n={...e},r=Object.keys(t).filter(c=>c!=="env");for(const c of r)_.includes(c)&&(n[c]=t[c]);for(const c of v)n[c]=t[c];const a=new Set([...v,..._]),i=r.filter(c=>!a.has(c)).map(String);return{merged:n,unverifiedKeys:i}},E=(e,s,t)=>{if(s.length===0)return;const n=e.vectorize??[],r=new Set(n.filter(Boolean).map(a=>a?.index_name));for(const a of s)r.has(a)||t.push(`schema declares vector index "${a}"; wrangler "vectorize" must include a binding with index_name "${a}"`)},D=new Set(["basic","dev","lite","standard","standard-1","standard-2","standard-3","standard-4"]),L={disk_mb:2e4,memory_mib:12288,vcpu:4},W=(e,s,t)=>{const n=e.instance_type;if(n===void 0)return;if(typeof n=="string"){D.has(n)||t.push(`${s} has unknown instance_type "${n}" — expected lite, basic, standard-1..4, or a custom { vcpu, memory_mib, disk_mb } object`);return}for(const[c,o]of Object.entries(L)){const d=n[c];d!==void 0&&(typeof d!="number"||d<=0||d>o)&&t.push(`${s} custom instance_type ${c} must be a positive number ≤ ${String(o)} (got ${String(d)})`)}const{disk_mb:r,memory_mib:a,vcpu:i}=n;if(typeof i=="number"&&typeof a=="number"&&a<i*3072&&t.push(`${s} custom instance_type needs ≥ 3 GiB (3072 MiB) memory per vCPU (got ${String(a)} MiB for ${String(i)} vCPU)`),typeof a=="number"&&typeof r=="number"){const c=Math.floor(a/1024*2e3);r>c&&t.push(`${s} custom instance_type allows ≤ 2 GB disk per GiB memory (≤ ${String(c)} MB for ${String(a)} MiB memory; got ${String(r)} MB)`)}},C=(e,s,t)=>{const{boundClasses:n,errors:r,nonSqliteClasses:a,sqliteClasses:i,warnings:c}=t;if(!e||typeof e!="object"||typeof e.class_name!="string"||e.class_name.length===0){r.push(`${s} must have a non-empty "class_name" naming its container-enabled Durable Object class`);return}(typeof e.image!="string"||e.image.length===0)&&r.push(`${s} ("${e.class_name}") must have an "image" — a Dockerfile path or a registry reference`),n.has(e.class_name)||r.push(`${s} class "${e.class_name}" has no matching durable_objects binding — run \`lunora dev\` to auto-reconcile wrangler.jsonc, or add { "name": "...", "class_name": "${e.class_name}" }`),i.has(e.class_name)||r.push(a.has(e.class_name)?`${s} class "${e.class_name}" is registered via "new_classes" but containers require SQLite-backed DOs — move it to "new_sqlite_classes"`:`${s} class "${e.class_name}" is missing from migrations — add a migration entry with "new_sqlite_classes": ["${e.class_name}"]`),W(e,`${s} ("${e.class_name}")`,r),e.max_instances===void 0&&c.push(`${s} ("${e.class_name}") declares no max_instances — set a cap so a traffic spike can't fan out unbounded container spend`)},p=e=>Array.isArray(e)?e.filter(s=>s!==null&&typeof s=="object"):[],N=(e,s,t)=>{if(e.containers===void 0)return;if(!Array.isArray(e.containers)){s.push("containers must be an array of { class_name, image, ... } entries");return}const n=e.containers;if(n.length===0)return;const r=new Set(p(e.durable_objects?.bindings).map(o=>o.class_name)),a=e.migrations??[],i=new Set(a.flatMap(o=>[...o?.new_sqlite_classes??[]])),c=new Set(a.flatMap(o=>[...o?.new_classes??[]]));for(const[o,d]of n.entries())C(d,`containers[${String(o)}]`,{boundClasses:r,errors:s,nonSqliteClasses:c,sqliteClasses:i,warnings:t});e.observability?.enabled!==!0&&t.push('containers are configured but observability is not enabled — container logs will not be captured (add { "observability": { "enabled": true } })')},I=(e,s)=>{if(e.workflows===void 0)return;if(!Array.isArray(e.workflows)){s.push("workflows must be an array of { name, binding, class_name } entries");return}const t=e.workflows;for(const[n,r]of t.entries()){const a=`workflows[${String(n)}]`;if(!r||typeof r!="object"){s.push(`${a} must be a { name, binding, class_name } object`);continue}(typeof r.binding!="string"||r.binding.length===0)&&s.push(`${a} must have a non-empty "binding" naming the Workflow binding (e.g. WORKFLOW_ORDER_PIPELINE)`),(typeof r.class_name!="string"||r.class_name.length===0)&&s.push(`${a} must have a non-empty "class_name" naming the exported WorkflowEntrypoint class`),(typeof r.name!="string"||r.name.length===0)&&s.push(`${a} must have a non-empty "name" naming the deployed workflow`)}},B=(e,s)=>{if(e===void 0)return;if(!Array.isArray(e)){s.push("queues.producers must be an array of { binding, queue } entries");return}const t=e;for(const[n,r]of t.entries()){const a=`queues.producers[${String(n)}]`;if(!r||typeof r!="object"){s.push(`${a} must be a { binding, queue } object`);continue}(typeof r.binding!="string"||r.binding.length===0)&&s.push(`${a} must have a non-empty "binding" naming the Queue producer (e.g. QUEUE_EMAIL)`),(typeof r.queue!="string"||r.queue.length===0)&&s.push(`${a} must have a non-empty "queue" naming the deployed queue`)}},F=(e,s)=>{if(e===void 0)return;if(!Array.isArray(e)){s.push("queues.consumers must be an array of { queue } entries");return}const t=e;for(const[n,r]of t.entries()){const a=`queues.consumers[${String(n)}]`;if(!r||typeof r!="object"){s.push(`${a} must be a { queue } object`);continue}(typeof r.queue!="string"||r.queue.length===0)&&s.push(`${a} must have a non-empty "queue" naming the consumed queue`)}},P=(e,s)=>{if(e.queues!==void 0){if(typeof e.queues!="object"||Array.isArray(e.queues)){s.push("queues must be a { producers, consumers } object");return}B(e.queues.producers,s),F(e.queues.consumers,s)}},T=(e,s)=>{if(e.secrets_store_secrets===void 0)return;if(!Array.isArray(e.secrets_store_secrets)){s.push("secrets_store_secrets must be an array of { binding, store_id, secret_name } entries");return}const t=e.secrets_store_secrets;for(const[n,r]of t.entries()){const a=`secrets_store_secrets[${String(n)}]`;if(!r||typeof r!="object"){s.push(`${a} must be a { binding, store_id, secret_name } object`);continue}for(const i of["binding","store_id","secret_name"])(typeof r[i]!="string"||r[i].length===0)&&s.push(`${a} must have a non-empty "${i}"`)}},b=e=>typeof e=="string"&&e.length>0,A=e=>e,U=[{arrayMessage:"kv_namespaces must be an array of { binding, id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the KV namespace binding`,hintField:"id",hintMessage:(e,s)=>`${e} ("${s}") has no "id" — run \`wrangler kv namespace create\` and set the namespace id, or the binding can't resolve`,key:"kv_namespaces"},{arrayMessage:"flagship must be an array of { binding, app_id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Flagship binding`,hintField:"app_id",hintMessage:(e,s)=>`${e} ("${s}") has no "app_id" — create a Flagship app and set its id, or the binding can't resolve`,key:"flagship"},{arrayMessage:"hyperdrive must be an array of { binding, id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Hyperdrive binding`,hintField:"id",hintMessage:(e,s)=>`${e} ("${s}") has no "id" — run \`wrangler hyperdrive create\` and set the id, or the binding can't connect`,key:"hyperdrive"},{arrayMessage:"pipelines must be an array of { binding, stream } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Pipelines binding`,hintField:["stream","pipeline"],hintMessage:(e,s)=>`${e} ("${s}") has no "stream" — run \`wrangler pipelines create <name>\` and set the stream name, or the binding can't resolve`,key:"pipelines"},{arrayMessage:"analytics_engine_datasets must be an array of { binding, dataset } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Analytics Engine binding`,hintField:"dataset",hintMessage:(e,s)=>`${e} ("${s}") has no "dataset" — it defaults to the binding name; set it explicitly to avoid drift`,key:"analytics_engine_datasets"}],K=(e,s,t,n)=>{const r=e[s.key];if(r!==void 0){if(!Array.isArray(r)){t.push(s.arrayMessage);return}for(const[a,i]of A(r).entries()){const c=`${s.key}[${String(a)}]`;if(!i||typeof i!="object"||!b(i.binding)){t.push(s.bindingMessage(c));continue}(typeof s.hintField=="string"?[s.hintField]:s.hintField).some(o=>b(i[o]))||n.push(s.hintMessage(c,i.binding))}}},G=[{key:"browser",message:'browser must be an object with a non-empty "binding" (e.g. { "binding": "BROWSER" })'},{key:"images",message:'images must be an object with a non-empty "binding" (e.g. { "binding": "IMAGES" })'}],Y=(e,s,t)=>{const n=e[s.key];n!==void 0&&(typeof n!="object"||Array.isArray(n)||!b(n.binding))&&t.push(s.message)},z=[{arrayMessage:"services must be an array of { binding, service, entrypoint? } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding" naming the service binding`},{field:"service",message:e=>`${e} must have a non-empty "service" naming the target Worker`}],key:"services",objectMessage:e=>`${e} must be a { binding, service, entrypoint? } object`},{arrayMessage:"dispatch_namespaces must be an array of { binding, namespace } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding"`},{field:"namespace",message:e=>`${e} must have a non-empty "namespace" naming the dispatch namespace`}],key:"dispatch_namespaces",objectMessage:e=>`${e} must be a { binding, namespace } object`},{arrayMessage:"mtls_certificates must be an array of { binding, certificate_id } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding"`},{field:"certificate_id",message:e=>`${e} must have a non-empty "certificate_id" (upload via \`wrangler mtls-certificate upload\`)`}],key:"mtls_certificates",objectMessage:e=>`${e} must be a { binding, certificate_id } object`}],H=(e,s,t)=>{const n=e[s.key];if(n!==void 0){if(!Array.isArray(n)){t.push(s.arrayMessage);return}for(const[r,a]of A(n).entries()){const i=`${s.key}[${String(r)}]`;if(!a||typeof a!="object"){t.push(s.objectMessage(i));continue}for(const c of s.fields)b(a[c.field])||t.push(c.message(i))}}},Q=(e,s,t)=>{const n=e.send_email;if(n===void 0)return;if(!Array.isArray(n)){s.push("send_email must be an array of { name, destination_address? } entries");return}const r=n;for(const[a,i]of r.entries())(!i||typeof i!="object"||typeof i.name!="string"||i.name.length===0)&&t.push(`send_email[${String(a)}] has no non-empty "name" naming the send-email binding — set one before deploying`)},J=(e,s)=>{e.logpush!==void 0&&typeof e.logpush!="boolean"&&s.push('logpush must be a boolean (set "logpush": true to enable Cloudflare Logpush)')},V=(e,s)=>{const{placement:t}=e;if(t!==void 0){if(typeof t!="object"||Array.isArray(t)){s.push('placement must be an object (e.g. { "mode": "smart" })');return}t.mode!==void 0&&t.mode!=="smart"&&s.push('placement.mode must be "smart" (the only supported Smart Placement mode)')}},Z=(e,s)=>{const{observability:t}=e;if(t===void 0)return;if(typeof t!="object"||Array.isArray(t)){s.push('observability must be an object (e.g. { "enabled": true, "head_sampling_rate": 1 })');return}const n=(r,a)=>{r!==void 0&&(typeof r!="number"||Number.isNaN(r)||r<0||r>1)&&s.push(`${a} must be a number in [0, 1] (the fraction of requests sampled)`)};n(t.head_sampling_rate,"observability.head_sampling_rate"),t.logs!==void 0&&(typeof t.logs!="object"||Array.isArray(t.logs)?s.push("observability.logs must be an object"):n(t.logs.head_sampling_rate,"observability.logs.head_sampling_rate"))},X=(e,s)=>{const{cache:t}=e;if(t!==void 0){if(typeof t!="object"||t===null||Array.isArray(t)){s.push('cache must be an object (e.g. { "enabled": true })');return}t.enabled!==void 0&&typeof t.enabled!="boolean"&&s.push("cache.enabled must be a boolean (true or false)")}},ee=(e,s)=>{const{exports:t}=e;if(t!==void 0){if(typeof t!="object"||t===null||Array.isArray(t)){s.push("exports must be an object keyed by entrypoint name");return}for(const[n,r]of Object.entries(t)){if(typeof r!="object"||r===null){s.push(`exports["${n}"] must be an object`);continue}r.type!==void 0&&typeof r.type!="string"&&s.push(`exports["${n}"].type must be a string`),r.cache!==void 0&&(typeof r.cache!="object"||r.cache===null||Array.isArray(r.cache)?s.push(`exports["${n}"].cache must be an object`):r.cache.enabled!==void 0&&typeof r.cache.enabled!="boolean"&&s.push(`exports["${n}"].cache.enabled must be a boolean`))}}},se=(e,s)=>{const{assets:t}=e;if(t!==void 0){if(typeof t!="object"||Array.isArray(t)){s.push('assets must be an object (e.g. { "directory": "./dist/client", "binding": "ASSETS" })');return}(typeof t.directory!="string"||t.directory.length===0)&&s.push('assets must declare a non-empty "directory" pointing at the built client output (e.g. "./dist/client")'),t.binding!==void 0&&(typeof t.binding!="string"||t.binding.length===0)&&s.push('assets.binding must be a non-empty string (e.g. "ASSETS")'),t.html_handling!==void 0&&typeof t.html_handling!="string"&&s.push("assets.html_handling must be a string"),t.not_found_handling!==void 0&&typeof t.not_found_handling!="string"&&s.push("assets.not_found_handling must be a string")}},te=(e,s)=>{const t=e.tail_consumers;if(t===void 0)return;if(!Array.isArray(t)){s.push("tail_consumers must be an array of { service, environment? } entries");return}const n=t;for(const[r,a]of n.entries())(!a||typeof a!="object"||typeof a.service!="string"||a.service.length===0)&&s.push(`tail_consumers[${String(r)}] must have a non-empty "service" naming the consumer Worker`)},Re=(e,s)=>{const t=e.tail_consumers??[];return t.some(n=>!!n&&n?.service===s.service&&n?.environment===s.environment)?e:{...e,tail_consumers:[...t,s]}},ne=new Set(["1","enabled","on","true","yes"]),re=(e,s)=>{const{vars:t}=e;if(!t||typeof t!="object")return;const n=t.LUNORA_ALLOWED_ORIGINS,r=t.LUNORA_CORS_ALLOW_CREDENTIALS,a=typeof n=="string"&&n.split(",").some(c=>c.trim()==="*"),i=typeof r=="string"&&ne.has(r.trim().toLowerCase());a&&i&&s.push('vars.LUNORA_ALLOWED_ORIGINS includes a "*" wildcard while vars.LUNORA_CORS_ALLOW_CREDENTIALS is on — browsers reject this combination and it defeats the allowlist; name explicit origins or drop credentials')},ae=(e,s,t)=>{const{error:n,merged:r,unverifiedKeys:a}=j(e,s);return n!==void 0?{error:n,wrangler:r}:(a.length>0&&t.push(`env.${String(s)} overrides ${a.join(", ")}, which this validator doesn't have a verified inheritance rule for — validated against the TOP-LEVEL value only. Double-check ${a.length===1?"it":"them"} by hand for "${String(s)}".`),{wrangler:r})},S=(e,s,t)=>{const n=[],r=[];if(!e||typeof e!="object")return n.push("wrangler config is not a valid object"),{errors:n,valid:!1,warnings:r};const{error:a,wrangler:i}=ae(e,t,r);if(a!==void 0)return n.push(a),{errors:n,valid:!1,warnings:r};p(i.durable_objects?.bindings).find(o=>o.name==="SHARD"&&o.class_name==="ShardDO")||n.push('durable_objects.bindings must include { "name": "SHARD", "class_name": "ShardDO" } — run `lunora dev` to auto-reconcile wrangler.jsonc, or add the binding manually');const c=i.compatibility_date??"";c&&!y.test(c)?n.push(`compatibility_date must be in YYYY-MM-DD format (got "${c}")`):c<h&&n.push(`compatibility_date must be >= "${h}" (got "${c||"<missing>"}")`),O(i)&&y.test(c)&&c<f&&n.push(`cache.enabled requires compatibility_date >= "${f}" (got "${c||"<missing>"}")`),s?.hasGlobalTable&&(p(i.d1_databases).find(o=>o.binding==="DB")||n.push('schema declares .global() tables; d1_databases must include a binding named "DB" — run `lunora dev` to auto-reconcile wrangler.jsonc, or add the binding manually')),E(i,s?.vectorIndexNames??[],n),te(i,n),N(i,n,r),I(i,n),P(i,n),T(i,n);for(const o of U)K(i,o,n,r);for(const o of z)H(i,o,n);for(const o of G)Y(i,o,n);return Q(i,n,r),J(i,n),V(i,n),Z(i,n),se(i,n),X(i,n),ee(i,n),re(i,n),{errors:n,valid:n.length===0,warnings:r}},xe=S,ie=(e,s,t)=>{const n=[];for(const r of e){const a=r?.image;typeof a!="string"||!(a.startsWith("./")||a.startsWith("../")||a.startsWith("/")||a.includes("Dockerfile"))||m(a.startsWith("/")?a:g(s,a))||n.push(`containers image "${a}" does not exist (resolved relative to ${t}); create the Dockerfile or point image at a registry reference`)}return n},oe=(e,s,t)=>{if(typeof e=="string"&&e.length>0){const n=g(w(t),e);return m(n)?n:void 0}return M.map(n=>g(s,n)).find(n=>m(n))},ce=/\/\/[^\n]*|\/\*.*?\*\/|"[^"\n]*"|'[^'\n]*'|`[^`]*`/gsu,de=e=>e.replaceAll(ce,s=>s.replaceAll(/[^\n]/gu," ")),le=/\bexport\s*\*\s*(?:as\s+\w+\s*)?from\b/u,ue=/\bexport\b/gu,me=/^\s*(?:(?:abstract|async|declare|default)\s+)*/u,ge=/^(?:class|const|function|let|var)\s+(?<name>[$A-Z_a-z][\w$]*)/u,pe=/^\s*(?:const|let|var)\s*\{/u,be=/^[$A-Z_a-z][\w$]*/u,fe=/^type\b/u,he=/\s+/u,ye=e=>{const s=[];for(const t of e.split(",")){const n=t.trim().split(he).filter(Boolean),r=n.at(-1);r!==void 0&&n[0]!=="type"&&s.push(r)}return s},ve=e=>{const s=[];for(const t of e.split(",")){const n=t.includes(":")?t.slice(t.indexOf(":")+1):t,r=be.exec(n.trim());r&&s.push(r[0])}return s},$=e=>{const s=e.indexOf("{"),t=e.indexOf("}",s);return s===-1||t===-1?void 0:e.slice(s+1,t)},_e=e=>{const s=e.trimStart();if(fe.test(s))return[];if(s.startsWith("{")){const n=$(s);return n===void 0?[]:ye(n)}if(pe.test(e)){const n=$(e);return n===void 0?[]:ve(n)}const t=ge.exec(e.replace(me,""));return t?.groups?.name===void 0?[]:[t.groups.name]},$e=e=>{const s=new Set;for(const t of e.matchAll(ue))for(const n of _e(e.slice(t.index+6)))s.add(n);return s},we=(e,s,t)=>{const n=oe(e.main,s,t);if(n===void 0)return[];let r;try{r=k(n,"utf8")}catch{return[]}const a=de(r);if(le.test(a))return[];const i=[];for(const o of p(e.durable_objects?.bindings))typeof o.class_name=="string"&&o.class_name.length>0&&o.script_name===void 0&&i.push({className:o.class_name,label:"durable_objects.bindings"});for(const o of e.workflows??[])typeof o?.class_name=="string"&&o.class_name.length>0&&o.script_name===void 0&&i.push({className:o.class_name,label:"workflows"});const c=$e(a);return i.filter(o=>!c.has(o.className)).map(o=>`${o.label} declares class "${o.className}" but the worker entry (${n}) does not export it — wrangler refuses to bundle a Worker whose Durable Object classes are not exported. Add \`export { ${o.className} } from "…";\` to the entry.`)},Ee=e=>{const s=e.schemaDir??"lunora",t=R(e.projectRoot);if(!t){const u=`wrangler.jsonc not found in ${e.projectRoot}; create one declaring at least the SHARD durable object binding.`;return{problems:[u],report:{errors:[u],valid:!1,warnings:[]},wranglerPath:void 0}}const{parsed:n}=x(t);if(n===void 0){const u=`failed to parse ${t} as JSONC.`;return{problems:[u],report:{errors:[u],valid:!1,warnings:[]},wranglerPath:t}}const{error:r,merged:a}=j(n,e.environment);if(r!==void 0)return{problems:[r],report:{errors:[r],valid:!1,warnings:[]},wranglerPath:t};const{error:i,info:c}=q(e.projectRoot,s),o=S(n,c,e.environment);i!==void 0&&o.warnings.push(`schema parse failed in ${s}/schema.ts: ${i}`);const d=w(t);o.errors.push(...ie(a.containers??[],d,t)),o.warnings.push(...we(a,e.projectRoot,t));const l=a.assets?.directory;if(typeof l=="string"&&l.length>0){const u=l.startsWith("/")?l:g(d,l);m(u)||o.warnings.push(`assets.directory "${l}" does not exist yet — it is created by the client build; run the build before deploy`)}return o.valid=o.errors.length===0,{problems:o.errors,report:o,wranglerPath:t}};export{h as REQUIRED_COMPATIBILITY_DATE,Oe as REQUIRED_FLAG,xe as validateWrangler,S as validateWranglerConfig,Ee as validateWranglerProject,Re as withTailConsumer};
@@ -1,18 +1,18 @@
1
- import{randomBytes as R}from"node:crypto";import{existsSync as m,readFileSync as p,writeFileSync as $,renameSync as h,rmSync as f,statSync as L}from"node:fs";import{join as g}from"node:path";import{LunoraError as z}from"@lunora/errors";import{DEV_VARS_FILE as l,DEV_VARS_EXAMPLE_FILE as y,splitDevVariableLine as K,parseDevVariableEntries as S,DEV_VARS_NEWLINE as k,unquoteDevVariable as H}from"./DEV_VARS_EXAMPLE_FILE-DX6xGbpr.mjs";import{CORE_SECRETS as E,PACKAGE_SECRETS_REGISTRY as F,secretsForPackages as W}from"./PACKAGE_SECRETS_REGISTRY-BgmvEPA-.mjs";const A=e=>[...E,...W(e)],v=32,b=/(?:KEY|PASSWORD|SECRET|TOKEN)$/u,M=["replace","openssl","changeme","change-me","change_me","change-this","change_this","your-","your_","example","placeholder","todo","fill-me","fill_me","fill-in","fill_in","xxx"],P=/[.*+?^${}()|[\]\\]/gu,T=/[a-z0-9]$/u,N=e=>{const t=e.trim().toLowerCase();return t===""||t.startsWith("<")&&t.endsWith(">")?!0:M.some(n=>{const r=n.replaceAll(P,String.raw`\$&`),s=T.test(n)?`(^|[^a-z0-9])${r}([^a-z0-9]|$)`:`(^|[^a-z0-9])${r}`;return new RegExp(s,"u").test(t)})},G=e=>N(H(e.trim())),x=e=>R(e).toString("hex"),I=new Set([...E,...Object.values(F).flat()].filter(e=>b.test(e.key)&&e.placeholderValue.startsWith("<")).map(e=>e.key)),O=e=>b.test(e)&&!I.has(e),w=(e,t,n)=>O(e)&&G(t)?n(v):void 0,ae=(e=x)=>e(v),q=e=>{if(e.devVarsExists)return{status:"exists"};if(e.exampleContent===void 0)return{status:"no-example"};const t=e.randomHex??x,n=[];return{content:e.exampleContent.split(k).map(r=>{const s=K(r),i=s?w(s.key,s.value,t):void 0;return!s||i===void 0?r:(n.push(s.key),`${s.key}="${i}"`)}).join(`
2
- `),generatedKeys:n,status:"generate"}},V=e=>{const t=e.randomHex??x,n=new Set(S(e.existingContent).map(c=>c.key)),r=[],s=[],i=[];for(const c of e.exampleContent.split(k)){const o=K(c);if(!o||n.has(o.key))continue;const a=w(o.key,o.value,t);i.push(o.key),a===void 0?r.push(`${o.key}="${H(o.value)}"`):(s.push(o.key),r.push(`${o.key}="${a}"`))}return{additions:r,generatedKeys:s,missingKeys:i}},_=e=>e.length>0?` (generated ${e.join(", ")})`:"",B=(e,t)=>{const n=`${e}.tmp-${String(process.pid)}`;try{$(n,t,{encoding:"utf8",flag:"wx",mode:384}),h(n,e)}catch(r){throw f(n,{force:!0}),r}},D=5,j=e=>{try{const t=L(e);return{mtimeMs:t.mtimeMs,size:t.size}}catch{return}},U=(e,t)=>e===void 0||t===void 0?!1:e.mtimeMs===t.mtimeMs&&e.size===t.size,Y=(e,t)=>{for(let n=0;n<D;n+=1){const r=j(e),s=p(e,"utf8"),i=t(s);if(i.length===0)return[];const c=s.length>0&&!s.endsWith(`
1
+ import{randomBytes as R}from"node:crypto";import{existsSync as p,readFileSync as f,writeFileSync as g,renameSync as $,rmSync as y,statSync as L}from"node:fs";import{join as h}from"node:path";import{LunoraError as z}from"@lunora/errors";import{DEV_VARS_FILE as l,DEV_VARS_EXAMPLE_FILE as m,splitDevVariableLine as K,parseDevVariableEntries as S,DEV_VARS_NEWLINE as k,unquoteDevVariable as A}from"./DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs";import{CORE_SECRETS as E,PACKAGE_SECRETS_REGISTRY as F,secretsForPackages as W}from"./PACKAGE_SECRETS_REGISTRY-BgmvEPA-.mjs";const H=e=>[...E,...W(e)],v=32,b=/(?:KEY|PASSWORD|SECRET|TOKEN)$/u,M=["replace","openssl","changeme","change-me","change_me","change-this","change_this","your-","your_","example","placeholder","todo","fill-me","fill_me","fill-in","fill_in","xxx"],P=/[.*+?^${}()|[\]\\]/gu,T=/[a-z0-9]$/u,N=e=>{const t=e.trim().toLowerCase();return t===""||t.startsWith("<")&&t.endsWith(">")?!0:M.some(n=>{const r=n.replaceAll(P,String.raw`\$&`),s=T.test(n)?`(^|[^a-z0-9])${r}([^a-z0-9]|$)`:`(^|[^a-z0-9])${r}`;return new RegExp(s,"u").test(t)})},G=e=>N(A(e.trim())),x=e=>R(e).toString("hex"),I=new Set([...E,...Object.values(F).flat()].filter(e=>b.test(e.key)&&e.placeholderValue.startsWith("<")).map(e=>e.key)),O=e=>b.test(e)&&!I.has(e),w=(e,t,n)=>O(e)&&G(t)?n(v):void 0,ae=(e=x)=>e(v),q=e=>{if(e.devVarsExists)return{status:"exists"};if(e.exampleContent===void 0)return{status:"no-example"};const t=e.randomHex??x,n=[];return{content:e.exampleContent.split(k).map(r=>{const s=K(r),i=s?w(s.key,s.value,t):void 0;return!s||i===void 0?r:(n.push(s.key),`${s.key}="${i}"`)}).join(`
2
+ `),generatedKeys:n,status:"generate"}},C=e=>{const t=e.randomHex??x,n=new Set(S(e.existingContent).map(c=>c.key)),r=[],s=[],i=[];for(const c of e.exampleContent.split(k)){const o=K(c);if(!o||n.has(o.key))continue;const a=w(o.key,o.value,t);i.push(o.key),a===void 0?r.push(`${o.key}="${A(o.value)}"`):(s.push(o.key),r.push(`${o.key}="${a}"`))}return{additions:r,generatedKeys:s,missingKeys:i}},_=e=>e.length>0?` (generated ${e.join(", ")})`:"",ie=(e,t)=>{const n=`${e}.tmp-${String(process.pid)}`;try{g(n,t,{encoding:"utf8",mode:384}),$(n,e)}catch(r){throw y(n,{force:!0}),r}},B=(e,t)=>{const n=`${e}.tmp-${String(process.pid)}`;try{g(n,t,{encoding:"utf8",flag:"wx",mode:384}),$(n,e)}catch(r){throw y(n,{force:!0}),r}},D=5,j=e=>{try{const t=L(e);return{mtimeMs:t.mtimeMs,size:t.size}}catch{return}},U=(e,t)=>e===void 0||t===void 0?!1:e.mtimeMs===t.mtimeMs&&e.size===t.size,Y=(e,t)=>{for(let n=0;n<D;n+=1){const r=j(e),s=f(e,"utf8"),i=t(s);if(i.length===0)return[];const c=s.length>0&&!s.endsWith(`
3
3
  `)?`
4
4
  `:"",o=`${s}${c}${i.join(`
5
5
  `)}
6
- `,a=`${e}.tmp-${String(process.pid)}-${R(6).toString("hex")}`;try{$(a,o,{encoding:"utf8",mode:384});const d=j(e);if(!U(r,d)){f(a,{force:!0});continue}return h(a,e),i}catch(d){throw f(a,{force:!0}),d}}throw new z("INTERNAL",`Failed to append to ${e} after ${String(D)} attempts — a concurrent writer kept winning the race.`)},ie=async e=>{const t=g(e.cwd,l),n=g(e.cwd,y);if(!m(n))return{addedKeys:[],generatedKeys:[],status:"no-example"};const r=p(n,"utf8");if(!m(t)){const a=q({devVarsExists:!1,exampleContent:r,randomHex:e.randomHex});return a.status!=="generate"?{addedKeys:[],generatedKeys:[],status:"no-example"}:e.yes===!0||await e.confirm(`No ${l} found. Generate it from ${y} (secrets auto-filled)?`)?m(t)?{addedKeys:[],generatedKeys:[],status:"skipped-exists"}:(B(t,a.content),e.info(`Created ${l}${_(a.generatedKeys)}.`),{addedKeys:[],generatedKeys:a.generatedKeys,status:"generated"}):(e.info(`Skipped — copy ${y} to ${l} and fill it in when you're ready.`),{addedKeys:[],generatedKeys:[],status:"declined"})}const s=V({existingContent:p(t,"utf8"),exampleContent:r,randomHex:e.randomHex});if(s.missingKeys.length===0)return{addedKeys:[],generatedKeys:[],status:"exists"};const i=s.missingKeys.join(", ");if(!(e.yes===!0||await e.confirm(`${l} is missing ${String(s.missingKeys.length)} key(s) from ${y} (${i}). Add them?`)))return e.info(`Skipped — add ${i} to ${l} when you're ready.`),{addedKeys:[],generatedKeys:[],status:"declined"};const c=Y(t,a=>V({existingContent:a,exampleContent:r,randomHex:e.randomHex}).additions).map(a=>K(a)?.key).filter(a=>a!==void 0);if(c.length===0)return{addedKeys:[],generatedKeys:[],status:"exists"};const o=s.generatedKeys.filter(a=>c.includes(a));return e.info(`Updated ${l} — added ${c.join(", ")}${_(o)}.`),{addedKeys:c,generatedKeys:o,status:"augmented"}},X=e=>[`# ${e.description}`,`# Docs: ${e.docsUrl}`,`${e.key}="${e.placeholderValue}"`].join(`
7
- `),J=(e,t)=>{const n=A(e).filter(r=>!t.has(r.key));return n.length===0?"":n.map(r=>X(r)).join(`
6
+ `,a=`${e}.tmp-${String(process.pid)}-${R(6).toString("hex")}`;try{g(a,o,{encoding:"utf8",mode:384});const d=j(e);if(!U(r,d)){y(a,{force:!0});continue}return $(a,e),i}catch(d){throw y(a,{force:!0}),d}}throw new z("INTERNAL",`Failed to append to ${e} after ${String(D)} attempts — a concurrent writer kept winning the race.`)},oe=async e=>{const t=h(e.cwd,l),n=h(e.cwd,m);if(!p(n))return{addedKeys:[],generatedKeys:[],status:"no-example"};const r=f(n,"utf8");if(!p(t)){const a=q({devVarsExists:!1,exampleContent:r,randomHex:e.randomHex});return a.status!=="generate"?{addedKeys:[],generatedKeys:[],status:"no-example"}:e.yes===!0||await e.confirm(`No ${l} found. Generate it from ${m} (secrets auto-filled)?`)?p(t)?{addedKeys:[],generatedKeys:[],status:"skipped-exists"}:(B(t,a.content),e.info(`Created ${l}${_(a.generatedKeys)}.`),{addedKeys:[],generatedKeys:a.generatedKeys,status:"generated"}):(e.info(`Skipped — copy ${m} to ${l} and fill it in when you're ready.`),{addedKeys:[],generatedKeys:[],status:"declined"})}const s=C({existingContent:f(t,"utf8"),exampleContent:r,randomHex:e.randomHex});if(s.missingKeys.length===0)return{addedKeys:[],generatedKeys:[],status:"exists"};const i=s.missingKeys.join(", ");if(!(e.yes===!0||await e.confirm(`${l} is missing ${String(s.missingKeys.length)} key(s) from ${m} (${i}). Add them?`)))return e.info(`Skipped — add ${i} to ${l} when you're ready.`),{addedKeys:[],generatedKeys:[],status:"declined"};const c=Y(t,a=>C({existingContent:a,exampleContent:r,randomHex:e.randomHex}).additions).map(a=>K(a)?.key).filter(a=>a!==void 0);if(c.length===0)return{addedKeys:[],generatedKeys:[],status:"exists"};const o=s.generatedKeys.filter(a=>c.includes(a));return e.info(`Updated ${l} — added ${c.join(", ")}${_(o)}.`),{addedKeys:c,generatedKeys:o,status:"augmented"}},X=e=>[`# ${e.description}`,`# Docs: ${e.docsUrl}`,`${e.key}="${e.placeholderValue}"`].join(`
7
+ `),J=(e,t)=>{const n=H(e).filter(r=>!t.has(r.key));return n.length===0?"":n.map(r=>X(r)).join(`
8
8
 
9
- `)},oe=(e,t)=>{const n=g(e,y),r=m(n)?p(n,"utf8"):"",s=new Set(S(r).map(d=>d.key)),i=J(t,s);if(i==="")return[];const c=r.length>0&&!r.endsWith(`
9
+ `)},de=(e,t)=>{const n=h(e,m),r=p(n)?f(n,"utf8"):"",s=new Set(S(r).map(d=>d.key)),i=J(t,s);if(i==="")return[];const c=r.length>0&&!r.endsWith(`
10
10
  `)?`
11
11
  `:"",o=`${r}${c}
12
12
  ${i}
13
- `,a=`${n}.tmp-${String(process.pid)}`;try{$(a,o,{encoding:"utf8"}),h(a,n)}catch(d){throw f(a,{force:!0}),d}return A(t).filter(d=>!s.has(d.key)).map(d=>d.key)},Q=e=>{const t=e.randomHex??x,n=[],r=e.existingContent.split(k).map(d=>{const u=K(d),C=u?w(u.key,u.value,t):void 0;return!u||C===void 0?d:(n.push(u.key),`${u.key}="${C}"`)}),s=new Set(S(e.existingContent).map(d=>d.key)),i=[],c=[];for(const d of E)s.has(d.key)||(i.push(d.key),c.push(`# ${d.description}`,`${d.key}="${t(v)}"`));const o=r.join(`
13
+ `,a=`${n}.tmp-${String(process.pid)}`;try{g(a,o,{encoding:"utf8"}),$(a,n)}catch(d){throw y(a,{force:!0}),d}return H(t).filter(d=>!s.has(d.key)).map(d=>d.key)},Q=e=>{const t=e.randomHex??x,n=[],r=e.existingContent.split(k).map(d=>{const u=K(d),V=u?w(u.key,u.value,t):void 0;return!u||V===void 0?d:(n.push(u.key),`${u.key}="${V}"`)}),s=new Set(S(e.existingContent).map(d=>d.key)),i=[],c=[];for(const d of E)s.has(d.key)||(i.push(d.key),c.push(`# ${d.description}`,`${d.key}="${t(v)}"`));const o=r.join(`
14
14
  `);if(c.length===0)return{addedKeys:i,content:o,filledKeys:n};const a=o===""||o.endsWith(`
15
15
  `)?"":`
16
16
  `;return{addedKeys:i,content:`${o}${a}${c.join(`
17
17
  `)}
18
- `,filledKeys:n}},de=e=>{const t=g(e.cwd,l),n=m(t),r=n?p(t,"utf8"):"",s=Q({existingContent:r,randomHex:e.randomHex});if(s.filledKeys.length===0&&s.addedKeys.length===0)return{addedKeys:[],filledKeys:[],status:"unchanged"};const i=`${t}.tmp-${String(process.pid)}`;try{$(i,s.content,{encoding:"utf8",mode:384}),h(i,t)}catch(o){throw f(i,{force:!0}),o}const c=[...s.filledKeys,...s.addedKeys];return e.info?.(`Generated ${String(c.length)} dev secret(s) in ${l}: ${c.join(", ")}`),{addedKeys:s.addedKeys,filledKeys:s.filledKeys,status:n?"filled":"created"}};export{J as buildPackageSecretsBlock,ie as ensureDevVariables,oe as ensureDevVarsExample,de as fillDevSecrets,ae as generateSecretValue,O as isMintableSecretKey,N as isPlaceholderValue,Q as planDevSecretsFill,V as planDevVariablesAugment,q as planDevVariablesScaffold,A as requiredSecrets};
18
+ `,filledKeys:n}},ce=e=>{const t=h(e.cwd,l),n=p(t),r=n?f(t,"utf8"):"",s=Q({existingContent:r,randomHex:e.randomHex});if(s.filledKeys.length===0&&s.addedKeys.length===0)return{addedKeys:[],filledKeys:[],status:"unchanged"};const i=`${t}.tmp-${String(process.pid)}`;try{g(i,s.content,{encoding:"utf8",mode:384}),$(i,t)}catch(o){throw y(i,{force:!0}),o}const c=[...s.filledKeys,...s.addedKeys];return e.info?.(`Generated ${String(c.length)} dev secret(s) in ${l}: ${c.join(", ")}`),{addedKeys:s.addedKeys,filledKeys:s.filledKeys,status:n?"filled":"created"}};export{J as buildPackageSecretsBlock,oe as ensureDevVariables,de as ensureDevVarsExample,ce as fillDevSecrets,ae as generateSecretValue,O as isMintableSecretKey,N as isPlaceholderValue,Q as planDevSecretsFill,C as planDevVariablesAugment,q as planDevVariablesScaffold,H as requiredSecrets,ie as writeDevVariablesFileAtomically};
@@ -1 +1 @@
1
- import{relative as i}from"node:path";import{secretKindOf as c,redact as l}from"@lunora/codegen";import{isPlaceholderValue as S}from"./buildPackageSecretsBlock-BpvaZzQ8.mjs";import{findWranglerFile as E,readWranglerJsonc as p}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const d=8,f=new Set(["CREDENTIAL","CREDENTIALS","DSN","PASSPHRASE","PASSWD","PASSWORD","SECRET","TOKEN"]),m=["ACCESS_KEY","API_KEY","ENCRYPTION_KEY","PRIVATE_KEY","SIGNING_KEY"],A=new Set(["PUBLIC","PUBLISHABLE"]),_=/[_-]/u,a=e=>e.toUpperCase().split(_),u=e=>{const r=e.toUpperCase().replaceAll("-","_");return a(e).some(t=>f.has(t))?!0:m.some(t=>r===t||r.endsWith(`_${t}`))},I=e=>a(e).some(r=>A.has(r)),P=(e,r)=>{if(e===void 0)return[];const t=[];for(const[s,o]of Object.entries(e)){if(typeof o!="string"||S(o)||I(s))continue;const n=c(o)??(u(s)&&o.length>=d?"secret_named_var":void 0);n!==void 0&&t.push({file:r,key:s,kind:n,preview:l(o)})}return t},R=e=>{const r=E(e);if(r===void 0)return[];const{parsed:t}=p(r);return t===void 0?[]:P(t.vars,i(e,r))};export{R as collectWranglerSecretVariables,P as scanWranglerVariablesForSecrets};
1
+ import{relative as i}from"node:path";import{secretKindOf as c,redact as l}from"@lunora/codegen";import{isPlaceholderValue as S}from"./buildPackageSecretsBlock-DlAPMSwt.mjs";import{findWranglerFile as E,readWranglerJsonc as p}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const d=8,f=new Set(["CREDENTIAL","CREDENTIALS","DSN","PASSPHRASE","PASSWD","PASSWORD","SECRET","TOKEN"]),m=["ACCESS_KEY","API_KEY","ENCRYPTION_KEY","PRIVATE_KEY","SIGNING_KEY"],A=new Set(["PUBLIC","PUBLISHABLE"]),_=/[_-]/u,a=e=>e.toUpperCase().split(_),u=e=>{const r=e.toUpperCase().replaceAll("-","_");return a(e).some(t=>f.has(t))?!0:m.some(t=>r===t||r.endsWith(`_${t}`))},I=e=>a(e).some(r=>A.has(r)),P=(e,r)=>{if(e===void 0)return[];const t=[];for(const[s,o]of Object.entries(e)){if(typeof o!="string"||S(o)||I(s))continue;const n=c(o)??(u(s)&&o.length>=d?"secret_named_var":void 0);n!==void 0&&t.push({file:r,key:s,kind:n,preview:l(o)})}return t},R=e=>{const r=E(e);if(r===void 0)return[];const{parsed:t}=p(r);return t===void 0?[]:P(t.vars,i(e,r))};export{R as collectWranglerSecretVariables,P as scanWranglerVariablesForSecrets};
@@ -0,0 +1 @@
1
+ import{existsSync as d,statSync as k,readFileSync as b,readdirSync as C}from"node:fs";import{init as T,parse as f}from"es-module-lexer";import{discoverAgentInfo as B}from"./discoverAgentInfo-BJm0QtoI.mjs";import{WRANGLER_FILES as P,readWranglerJsonc as L}from"./WRANGLER_FILES-Bi_18Pj6.mjs";import{discoverContainerInfo as K}from"./discoverContainerInfo-CYG9j2LY.mjs";import{escapeRegExp as D}from"./DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs";import{FLAGS_FILENAME as W,discoverFlags as H,QUEUES_FILENAME as M,discoverQueues as q}from"@lunora/codegen";import{Project as $}from"ts-morph";import{join as c}from"node:path";import{discoverSchemaInfo as G}from"./discoverSchemaInfo-C8X9mo-i.mjs";import{discoverWorkflowInfo as U}from"./discoverWorkflowInfo-Bbc0u2gE.mjs";const X=(e,r)=>{const t=c(e,r,W);if(!d(t))return{};try{const s=new $({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});return{flags:H(s,c(e,r))}}catch(s){return{error:s instanceof Error?s.message:String(s)}}},Y=(e,r)=>{const t=c(e,r,M);if(!d(t))return{queues:[]};try{const s=new $({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});return{queues:q(s,c(e,r))}}catch(s){return{error:s instanceof Error?s.message:String(s),queues:[]}}},Q=new Set([".cjs",".cts",".js",".jsx",".mjs",".mts",".ts",".tsx"]),z=new Set([".git",".lunora-cache",".wrangler","_generated","dist","node_modules"]),J=["lunora","src"],V=["src/server/index.ts","src/server/index.tsx","src/index.ts","src/worker.ts"],A={SchedulerDO:"SCHEDULER",SessionDO:"SESSION",ShardDO:"SHARD"},O=Object.keys(A),Z={SchedulerDO:/\btype\s+SchedulerDO\b/,SessionDO:/\btype\s+SessionDO\b/,ShardDO:/\btype\s+ShardDO\b/},ee=/\benv\s*\.\s*DB\b/,re=/\benv\s*\.\s*AI\b/,_=/\bctx\s*\.\s*pipelines\b/,I=/^\s*import\s+type\b/,se=new Set(["@lunora/agent","@lunora/agent/sandbox"]),te=e=>{const r=e.indexOf("{");if(r===-1)return"";const t=e.indexOf("}",r+1);return t===-1?e.slice(r+1):e.slice(r+1,t)},ne=/\btype\s+browserTool\b/,oe=/\bbrowserTool\b/,ae=/import\s+\{[^}]*\bbrowserTool\b[^}]*\}\s+from\s+["']@lunora\/agent(?:\/sandbox)?["']/,ie=e=>{if(I.test(e))return!1;const r=te(e);return oe.test(r)&&!ne.test(r)},ce=e=>{try{const[r]=f(e);return r.some(t=>t.n!==void 0&&se.has(t.n)&&ie(e.slice(t.ss,t.se)))}catch{return ae.test(e)}},g={usesAi:{pattern:/\bfrom\s+["']@lunora\/ai["']/,source:"@lunora/ai"},usesAnalytics:{pattern:/\bfrom\s+["']@lunora\/bindings\/analytics["']/,source:"@lunora/bindings/analytics"},usesAuth:{pattern:/\bfrom\s+["']@lunora\/auth["']/,source:"@lunora/auth"},usesBrowser:{pattern:/\bfrom\s+["']@lunora\/browser["']/,source:"@lunora/browser"},usesHyperdrive:{pattern:/\bfrom\s+["']@lunora\/hyperdrive["']/,source:"@lunora/hyperdrive"},usesImages:{pattern:/\bfrom\s+["']@lunora\/bindings\/images["']/,source:"@lunora/bindings/images"},usesKv:{pattern:/\bfrom\s+["']@lunora\/bindings\/kv["']/,source:"@lunora/bindings/kv"},usesMail:{pattern:/\bfrom\s+["']@lunora\/mail["']/,source:"@lunora/mail"},usesPayment:{pattern:/\bfrom\s+["']@lunora\/payment["']/,source:"@lunora/payment"},usesPipelines:{pattern:_,source:"@lunora/bindings/pipelines"},usesScheduler:{pattern:/\bfrom\s+["']@lunora\/scheduler["']/,source:"@lunora/scheduler"},usesStorage:{pattern:/\bfrom\s+["']@lunora\/storage["']/,source:"@lunora/storage"},usesX402Charge:{pattern:/\bfrom\s+["']@lunora\/x402\/charge["']/,source:"@lunora/x402/charge"},usesX402Pay:{pattern:/\bfrom\s+["']@lunora\/x402\/pay["']/,source:"@lunora/x402/pay"}},p=Object.keys(g),ue="STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET (Stripe) or POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET (Polar)",j=[...p,"needsD1"],de=()=>{const e={};for(const r of j)e[r]=!1;return e},l=Object.freeze(de()),y=(e,r)=>{const t={};for(const s of j)t[s]=e[s]||r[s];return t},le=e=>{for(const r of p)if(g[r].source===e)return{...l,[r]:!0};return l},pe=e=>{const[r]=f(e);let t=l;for(const s of r){const n=s.n;!n||I.test(e.slice(s.ss,s.se))||(t=y(t,le(n)))}return t},me=e=>{const r={...l};for(const t of p)r[t]=g[t].pattern.test(e);return r},be=e=>{let r;try{r=pe(e)}catch{r=me(e)}return y(r,{...l,needsD1:ee.test(e),usesAi:re.test(e),usesPipelines:_.test(e)})},S=(e,r)=>{let t;try{t=C(e,{withFileTypes:!0})}catch{return}for(const s of t){if(s.isDirectory()){z.has(s.name)||S(c(e,s.name),r);continue}const n=s.name.lastIndexOf(".");n!==-1&&Q.has(s.name.slice(n))&&r.push(c(e,s.name))}},fe=e=>{for(const r of P){const t=c(e,r);if(!d(t))continue;const{parsed:s}=L(t),n=s?.main;if(typeof n=="string"&&d(c(e,n)))return c(e,n);break}for(const r of V){const t=c(e,r);if(d(t))return t}},ge=e=>{const r=b(e,"utf8");let t;try{const[,s]=f(r);t=new Set(s.map(n=>n.n))}catch{t=new Set(O.filter(s=>new RegExp(String.raw`\bexport\b[^\n;]*\b${s}\b`).test(r)))}return O.filter(s=>t.has(s)&&!Z[s].test(r)).map(s=>({binding:A[s],className:s}))},he=/(?:^|[\s,{])type$/u,ye=(e,r)=>{const t=r.ls>=0?r.ls:r.s;return he.test(e.slice(0,t).trimEnd())},Se=(e,r)=>{const t=D(r);return new RegExp(String.raw`\bexport\s+type\s+${t}\b`,"u").test(e)||new RegExp(String.raw`\bexport\s+type\s*\{[^}]*\b${t}\b`,"u").test(e)||new RegExp(String.raw`\bexport\s+\{[^}]*\btype\s+${t}\b`,"u").test(e)},w=(e,r,t)=>{if(r.length===0)return[];if(e===void 0)return r.map(o=>({...o,exported:!1}));const s=b(e,"utf8"),n=new RegExp(String.raw`\bexport\s*\*\s*from\s*["'][^"']*_generated\/${t}(?:\.js)?["']`).test(s);let a;try{const[,o]=f(s);a=new Set(o.filter(i=>!ye(s,i)).map(i=>i.n))}catch{a=new Set(r.map(o=>o.className).filter(o=>new RegExp(String.raw`\bexport\b[^\n;]*\b${D(o)}\b`,"u").test(s)&&!Se(s,o)))}return r.map(o=>{const i=n||a.has(o.className);return{...o,exported:i}})},we=(e,r)=>w(e,r,"containers"),xe=(e,r)=>w(e,r,"workflows"),ve=(e,r)=>w(e,r,"agents"),Ee=(e,r)=>G(e,r).info?.hasGlobalTable??!1,Re=(e,r)=>{let t=l;for(const s of r){const n=c(e,s);if(!d(n)||!k(n).isDirectory())continue;const a=[];S(n,a);for(const o of a)t=y(t,be(b(o,"utf8")))}return t},Ne=(e,r)=>{const t=c(e,r);if(!d(t)||!k(t).isDirectory())return!1;const s=[];return S(t,s),s.some(n=>ce(b(n,"utf8")))},Oe=(e,r,t)=>[...e.map(s=>s.exported?`${s.bindingName}/${s.className} (container "${s.exportName}" declared and exported)`:`hint: container "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/containers"\``),...r.map(s=>s.exported?`${s.bindingName}/${s.className} (workflow "${s.exportName}" declared and exported)`:`hint: workflow "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/workflows"\``),...t.map(s=>s.exported?`${s.bindingName}/${s.className} (agent "${s.exportName}" declared and exported)`:`hint: agent "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/agents"\``)],ke=(e,r)=>[[e.usesAi,"AI (@lunora/ai imported or env.AI used)"],[e.usesAuth&&!r.has("SessionDO"),"hint: @lunora/auth is imported; its tables are D1-backed by default. For DO-backed auth (what @better-auth/scim needs), pass `namespace` to .auth() and export the generated auth DO class"],[e.usesScheduler&&!r.has("SchedulerDO"),"hint: @lunora/scheduler is imported but no SchedulerDO is exported by the worker entry"],[e.usesStorage,"hint: @lunora/storage is imported; add an r2_buckets binding (bucket binding names are user-defined)"],[e.usesMail,"hint: @lunora/mail is imported; set RESEND_API_KEY in .dev.vars (obtain at https://resend.com/api-keys)"],[e.usesPayment,`hint: @lunora/payment is imported; set the provider secrets in .dev.vars — ${ue}`],[e.usesBrowser,"browser (@lunora/browser imported) — self-describing { binding: BROWSER }"],[e.usesImages,"images (@lunora/bindings/images imported) — self-describing { binding: IMAGES }"],[e.usesAnalytics,"analytics_engine_datasets (@lunora/bindings/analytics imported) — self-describing { binding: ANALYTICS, dataset }"],[e.usesKv,"hint: @lunora/bindings/kv is imported; add a kv_namespaces binding ({ binding, id }) and pass env.<BINDING> to createKv() — the namespace id can't be auto-provisioned"],[e.usesHyperdrive,"hint: @lunora/hyperdrive is imported; run 'wrangler hyperdrive create' and add a 'hyperdrive' binding ({ binding, id }) — the id can't be auto-provisioned"],[e.usesPipelines,"hint: ctx.pipelines is used; run 'wrangler pipelines create <name>' and add a 'pipelines' binding ({ binding, pipeline }) — the pipeline resource can't be auto-provisioned"],[e.usesX402Charge,"hint: @lunora/x402/charge is imported; set the recipient wallet address as a [vars] entry (the var name is yours to choose) and pass it to the charge config — the x402 facilitator settles USDC to that address"],[e.usesX402Pay,"hint: @lunora/x402/pay is imported (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding for the agent wallet key (name it to match signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars, so the key can't be auto-provisioned"]].filter(([t])=>t).map(([,t])=>t),De=(e,r,t,s=[],n=[],a=[])=>{const o=new Set(e.map(u=>u.className)),i=e.map(u=>`${u.binding}/${u.className} (exported by worker entry)`);return r&&i.push("DB (.global() table declared)"),i.push(...Oe(s,n,a),...ke(t,o)),i},Ke=async e=>{await T;const r=e.schemaDir??"lunora",t=e.scanDirs??J,s=Re(e.projectRoot,t),n={...s,usesBrowser:s.usesBrowser||Ne(e.projectRoot,r)},a=fe(e.projectRoot),o=a?ge(a):[],i=n.needsD1||Ee(e.projectRoot,r),u=we(a,K(e.projectRoot,r).containers),x=xe(a,U(e.projectRoot,r).workflows),v=ve(a,B(e.projectRoot,r).agents),F=[...Y(e.projectRoot,r).queues],{flags:m}=X(e.projectRoot,r),h=m?.provider==="flagship"&&m.mode==="binding"?m.bindingName:void 0,E={};for(const N of p)E[N]=n[N];const R=De(o,i,n,u,x,v);return h!==void 0&&R.push(`hint: lunora/flags.ts uses Flagship in binding mode; add a flagship binding ({ binding: "${h}", app_id }) — the app_id can't be auto-provisioned`),{agents:v,containers:u,durableObjects:o,flagshipBinding:h,needsD1:i,queues:F,signals:R,usesFlags:m!==void 0,workflows:x,...E}},We=e=>{const r=[];for(const t of p)e[t]&&r.push(g[t].source);return r};export{V as WORKER_ENTRY_FALLBACKS,Ke as inferLunoraBindings,ye as isTypeOnlyExportEntry,We as packageNamesFromBindings,fe as resolveWorkerEntry};
@@ -1 +1 @@
1
- import{readFileSync as n}from"node:fs";import{join as i}from"node:path";import{parseDevVariableEntries as a}from"./DEV_VARS_EXAMPLE_FILE-DX6xGbpr.mjs";const s=(e,r)=>{const o=a(e).find(t=>t.key===r);return o===void 0||o.value===""?void 0:o.value},d=e=>{const r=process.env.LUNORA_ADMIN_TOKEN;if(typeof r=="string"&&r!=="")return r;try{return s(n(i(e,".dev.vars"),"utf8"),"LUNORA_ADMIN_TOKEN")}catch{return}};export{s as parseDevVariable,d as resolveAdminToken};
1
+ import{readFileSync as n}from"node:fs";import{join as i}from"node:path";import{parseDevVariableEntries as a}from"./DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs";const s=(e,r)=>{const o=a(e).find(t=>t.key===r);return o===void 0||o.value===""?void 0:o.value},c=e=>{const r=process.env.LUNORA_ADMIN_TOKEN;if(typeof r=="string"&&r!=="")return r;try{return s(n(i(e,".dev.vars"),"utf8"),"LUNORA_ADMIN_TOKEN")}catch{return}};export{s as parseDevVariable,c as resolveAdminToken};
@@ -0,0 +1 @@
1
+ import{writeFileSync as S,readFileSync as k}from"node:fs";import{join as E}from"node:path";import{containerBuildTag as D}from"@lunora/container";import{DEV_VARS_FILE as A,parseDevVariableEntries as B}from"./DEV_VARS_EXAMPLE_FILE-BL0hrPx3.mjs";import{e as b}from"./jsonc-edit-BZVpxVA0.mjs";import{findWranglerFile as O,readWranglerJsonc as C}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const x="<replace-with-d1-create-id>",R=e=>{const a=[];for(const n of e.containers)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"container",module:"containers"});for(const n of e.workflows)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"workflow",module:"workflows"});for(const n of e.agents)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"agent",module:"agents"});return a},I=(e,a)=>{const n=e.flagshipBinding!==void 0&&!(a?.flagship??[]).some(s=>s.binding===e.flagshipBinding);return[[e.usesKv&&(a?.kv_namespaces?.length??0)===0,"@lunora/bindings/kv is used but no kv_namespaces binding exists; add a kv_namespaces entry ({ binding, id }) and pass env.<BINDING> to createKv() — the namespace id can't be auto-provisioned."],[e.usesHyperdrive&&(a?.hyperdrive?.length??0)===0,"@lunora/hyperdrive is used but no hyperdrive binding exists; run 'wrangler hyperdrive create' and add a 'hyperdrive' binding ({ binding, id }) — the id can't be auto-provisioned."],[e.usesPipelines&&(a?.pipelines?.length??0)===0,"ctx.pipelines is used but no pipelines binding exists; run 'wrangler pipelines create <name>' and add a 'pipelines' binding ({ binding, pipeline }) — the pipeline resource can't be auto-provisioned."],[n,`lunora/flags.ts uses Flagship in binding mode but no flagship binding "${e.flagshipBinding??""}" exists; add a flagship entry ({ binding: "${e.flagshipBinding??""}", app_id }) — the app_id can't be auto-provisioned.`]].filter(([s])=>s).map(([,s])=>s)},P=e=>[[e.usesX402Charge,"@lunora/x402/charge is used; set the recipient wallet address as a [vars] entry (the var name is your choice) and pass it to the charge config — the x402 facilitator settles USDC to that address."],[e.usesX402Pay,"@lunora/x402/pay is used (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding holding the agent wallet key (binding name == signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars."]].filter(([a])=>a).map(([,a])=>a),_=(e,a,n)=>n.filter(s=>!s.exported).map(s=>`${e} "${s.exportName}" is declared but ${s.className} is not exported by the worker entry; add \`export * from "./lunora/_generated/${a}"\` so its binding can be provisioned.`),N=[{keys:["STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"],label:"Stripe"},{keys:["POLAR_ACCESS_TOKEN","POLAR_WEBHOOK_SECRET"],label:"Polar"},{keys:["CREEM_API_KEY","CREEM_WEBHOOK_SECRET"],label:"Creem"},{keys:["AUTUMN_SECRET_KEY","AUTUMN_WEBHOOK_SECRET"],label:"Autumn"},{keys:["DODO_PAYMENTS_API_KEY","DODO_PAYMENTS_WEBHOOK_KEY"],label:"Dodo Payments"}],T=()=>N.map(({keys:e,label:a})=>`${e[0]} + ${e[1]} (${a})`).join(" or "),$=e=>{let a;try{a=k(E(e,A),"utf8")}catch{return!1}const n=new Map(B(a).map(s=>[s.key,s.value]));return N.some(({keys:s})=>s.every(r=>(n.get(r)??"")!==""))},v=(e,a,n)=>{const s=new Set(e.durableObjects.map(o=>o.className)),r=[],i=(n?.r2_buckets?.length??0)>0,u=(n?.d1_databases?.some(o=>o.binding==="DB")??!1)||e.needsD1;return e.usesStorage&&!i&&r.push("@lunora/storage is used but R2 bucket bindings have user-defined names; add an r2_buckets entry and pass env.<BINDING> to createStorage()."),e.usesAuth&&!s.has("SessionDO")&&!u&&r.push("@lunora/auth is used but the worker entry exports no SessionDO; sessions are D1-backed, or export SessionDO to enable DO-backed sessions."),e.usesScheduler&&!s.has("SchedulerDO")&&r.push("@lunora/scheduler is used but the worker entry exports no SchedulerDO; export it so the SCHEDULER binding can be provisioned."),r.push(..._("container","containers",e.containers),..._("workflow","workflows",e.workflows),..._("agent","agents",e.agents)),e.containers.length>0&&n?.observability?.enabled===!1&&r.push("containers are declared but observability is explicitly disabled in wrangler.jsonc — container logs will not be captured."),e.usesPayment&&!$(a)&&r.push(`@lunora/payment is used; set one provider's secret pair in .dev.vars — ${T()}.`),r.push(...P(e),...I(e,n)),r},M=e=>{const a=new Set(e.map(s=>s.tag));let n=1;for(;a.has(`v${String(n)}`);)n+=1;return`v${String(n)}`},q=(e,a,n)=>{const s=a.durable_objects?.bindings??[],r=new Set(s.map(c=>c.name)),i=n.filter(c=>!r.has(c.binding));let u=e;const o=[];if(i.length>0){const c=[...s,...i.map(m=>({class_name:m.className,name:m.binding}))];u=b(u,["durable_objects","bindings"],c),o.push(...i.map(m=>`${m.binding}/${m.className}`))}const p=a.migrations??[],l=new Set(p.flatMap(c=>[...c.new_sqlite_classes??[],...c.new_classes??[]])),h=n.map(c=>c.className).filter(c=>!l.has(c));if(h.length>0){const c=[...p,{new_sqlite_classes:h,tag:M(p)}];u=b(u,["migrations"],c)}return{added:o,text:u}},K=(e,a)=>{const n=a.d1_databases??[];if(n.some(i=>i.binding==="DB"))return{added:[],text:e};const s=typeof a.name=="string"&&a.name.length>0?a.name:"lunora",r=[...n,{binding:"DB",database_id:x,database_name:s}];return{added:["DB (D1)"],text:b(e,["d1_databases"],r)}},y=(e,a,n,s,r)=>{const i=a[n]?.binding;return typeof i=="string"&&i.length>0?{added:[],text:e}:{added:[r],text:b(e,[n],{binding:s})}},W=(e,a)=>(a.analytics_engine_datasets?.length??0)>0?{added:[],text:e}:{added:["ANALYTICS (Analytics Engine)"],text:b(e,["analytics_engine_datasets"],[{binding:"ANALYTICS",dataset:"ANALYTICS"}])},j=e=>{if(typeof e=="string")return e;const a={};return e.diskMb!==void 0&&(a.disk_mb=e.diskMb),e.memoryMib!==void 0&&(a.memory_mib=e.memoryMib),e.vcpu!==void 0&&(a.vcpu=e.vcpu),a},Y=e=>e.image.kind==="dockerfile"?e.image.dockerfilePath:e.image.kind==="registry"?e.image.reference:D(e.exportName),L=e=>{const a={class_name:e.className,image:Y(e)};return e.image.kind==="dockerfile"&&(a.image_build_context=e.image.buildContext),e.buildArgs!==void 0&&e.image.kind!=="registry"&&(a.image_vars=e.buildArgs),e.instanceType!==void 0&&(a.instance_type=j(e.instanceType)),e.maxInstances!==void 0&&(a.max_instances=e.maxInstances),e.name!==void 0&&(a.name=e.name),e.rollout?.stepPercentage!==void 0&&(a.rollout_step_percentage=e.rollout.stepPercentage),e.rollout?.gracePeriodSeconds!==void 0&&(a.rollout_active_grace_period=e.rollout.gracePeriodSeconds),a},G=(e,a,n)=>{const s=a.containers??[],r=new Set(s.map(o=>o.class_name)),i=n.filter(o=>!r.has(o.className));if(i.length===0)return{added:[],text:e};const u=b(e,["containers"],[...s,...i.map(o=>L(o))]);return{added:i.map(o=>`containers/${o.className}`),text:u}},H=(e,a)=>{if(a.observability!==void 0)return{added:[],text:e};const n=b(e,["observability"],{enabled:!0,head_sampling_rate:1});return{added:["observability"],text:n}},U=e=>({binding:e.bindingName,class_name:e.className,name:e.name}),F=e=>({binding:e.bindingName,class_name:e.className,name:e.name}),z=(e,a,n,s=[])=>{const r=a.workflows??[],i=new Set(r.map(l=>l.class_name)),u=n.filter(l=>!i.has(l.className)),o=s.filter(l=>!i.has(l.className));if(u.length===0&&o.length===0)return{added:[],text:e};const p=b(e,["workflows"],[...r,...u.map(l=>U(l)),...o.map(l=>F(l))]);return{added:[...u.map(l=>`workflows/${l.className}`),...o.map(l=>`workflows/${l.className}`)],text:p}},V=(e,a,n)=>{const s=a.queues??{},r=s.producers??[],i=s.consumers??[],u=new Set(r.map(d=>d.binding)),o=new Set(i.map(d=>d.queue)),p=n.filter(d=>!u.has(d.bindingName)),l=n.filter(d=>!o.has(d.name));if(p.length===0&&l.length===0)return{added:[],text:e};const h=[...r,...p.map(d=>({binding:d.bindingName,queue:d.name}))],c=[...i,...l.map(d=>{const g={queue:d.name};return d.mode==="pull"&&(g.type="http_pull"),d.tuning.maxBatchSize!==void 0&&(g.max_batch_size=d.tuning.maxBatchSize),d.tuning.maxBatchTimeout!==void 0&&(g.max_batch_timeout=d.tuning.maxBatchTimeout),d.tuning.maxRetries!==void 0&&(g.max_retries=d.tuning.maxRetries),d.tuning.deadLetterQueue!==void 0&&(g.dead_letter_queue=d.tuning.deadLetterQueue),d.tuning.retryDelay!==void 0&&(g.retry_delay=d.tuning.retryDelay),g})],m=b(e,["queues"],{consumers:c,producers:h});return{added:[...p.map(d=>`queues.producers/${d.bindingName}`),...l.map(d=>`queues.consumers/${d.name}`)],text:m}},ne=(e,a,n)=>{const s=O(e),r=R(a);if(!s)return{added:[],changed:!1,exportGaps:r,reason:"wrangler.jsonc not found",warnings:v(a,e)};const{parsed:i,text:u}=C(s);if(i===void 0)return{added:[],changed:!1,exportGaps:r,reason:`failed to parse ${s} as JSONC`,warnings:v(a,e),wranglerPath:s};const o=v(a,e,i);if(n!==void 0){const t=i.env?.[n]!==void 0;o.push(t?`auto-provisioned bindings are written to the top level of wrangler.jsonc only — "env.${n}" has its own (non-inheritable) bindings and must be reconciled by hand; \`lunora deploy --env ${n}\` now validates them, so a gap here will be reported at deploy time.`:`--env "${n}" was requested but wrangler.jsonc declares no "env.${n}" block — auto-provisioned bindings are written to the top level only and will not apply to that environment.`)}const p=a.containers.filter(t=>t.exported),l=a.agents.filter(t=>t.exported&&t.voice===!0&&t.voiceBindingName!==void 0&&t.voiceClassName!==void 0),h=[...a.durableObjects,...p.map(t=>({binding:t.bindingName,className:t.className})),...l.map(t=>({binding:t.voiceBindingName,className:t.voiceClassName}))],c=a.workflows.filter(t=>t.exported),m=a.agents.filter(t=>t.exported),d=[{enabled:!0,run:t=>q(t,i,h)},{enabled:a.needsD1,run:t=>K(t,i)},{enabled:a.usesAi,run:t=>y(t,i,"ai","AI","AI (Workers AI)")},{enabled:a.usesBrowser,run:t=>y(t,i,"browser","BROWSER","BROWSER (Browser Rendering)")},{enabled:a.usesImages,run:t=>y(t,i,"images","IMAGES","IMAGES (Cloudflare Images)")},{enabled:a.usesAnalytics,run:t=>W(t,i)},{enabled:!0,run:t=>H(t,i)},{enabled:p.length>0,run:t=>G(t,i,p)},{enabled:c.length>0||m.length>0,run:t=>z(t,i,c,m)},{enabled:a.queues.length>0,run:t=>V(t,i,a.queues)}];let g=u;const f=[];for(const t of d){if(!t.enabled)continue;const w=t.run(g);g=w.text,f.push(...w.added)}return f.includes("DB (D1)")&&o.push(`wrote a DB binding with a placeholder database_id ("${x}") — run \`wrangler d1 create <name>\` and replace it before deploying.`),g===u?{added:[],changed:!1,exportGaps:r,reason:"bindings already in sync",warnings:o,wranglerPath:s}:(S(s,g,"utf8"),{added:f,changed:!0,exportGaps:r,warnings:o,wranglerPath:s})};export{ne as reconcileWranglerBindings};
@@ -0,0 +1 @@
1
+ import{headerValue as i}from"./ALLOW_FORWARDED_ENV-DGdbCDre.mjs";const u=1e6,f=async e=>await new Promise((t,r)=>{const o=[];let a=0;e.on("data",s=>{if(a+=s.length,a>u){r(new Error("request body too large"));return}o.push(s)}),e.on("end",()=>{t(Buffer.concat(o).toString("utf8"))}),e.on("error",r)}),n=(e,t,r)=>{e.statusCode=t,e.setHeader("Content-Type","application/json; charset=utf-8"),e.end(JSON.stringify(r))},l=new Set(["none","same-origin","same-site"]),p=e=>{const t=i(e.headers["sec-fetch-site"]);if(t!==void 0)return l.has(t)?void 0:"cross-origin request rejected";const r=i(e.headers.origin);if(r===void 0||r==="null")return;let o;try{o=new URL(r).host.toLowerCase()}catch{return"invalid origin header"}return o===i(e.headers.host)?void 0:"cross-origin request rejected"},y=e=>{const t=(e.method??"GET").toUpperCase(),r=t!=="GET"&&t!=="HEAD",o=p(e);if(o!==void 0)return o;if(r&&!i(e.headers["content-type"])?.startsWith("application/json"))return"content-type must be application/json"},g=(e,t,r,o,a)=>{(async()=>{try{const s=y(e);if(s!==void 0){n(t,403,{error:s,ok:!1});return}const c=e.method==="GET"?"":await f(e);let d;try{d=c===""?void 0:JSON.parse(c)}catch{n(t,400,{error:"invalid-json",ok:!1});return}const h=r({body:d,method:e.method??"POST",projectRoot:o,schemaDirectory:a});n(t,h.status,h.body)}catch(s){n(t,500,{error:s instanceof Error?s.message:String(s),ok:!1})}})().catch(()=>{})};export{g as serveJsonHandler};
@@ -266,4 +266,36 @@ type LocalEndpointHandler = (request: LocalEndpointRequest) => LocalEndpointResp
266
266
  * instead of always defaulting to `"lunora"`.
267
267
  */
268
268
  declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string, schemaDirectory?: string) => void;
269
- export { type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp };
269
+ /** A single header value, lower-cased and trimmed; `undefined` when absent or array-valued. */
270
+ declare const headerValue: (raw: string | string[] | undefined) => string | undefined;
271
+ /**
272
+ * True for an IPv4/IPv6 loopback peer (`127.0.0.0/8`, `::1`, and the
273
+ * IPv4-mapped `::ffff:127.x`). A missing address means we cannot read the
274
+ * transport (e.g. a mocked request in tests) — treated as loopback so the
275
+ * config-derived gate stays the source of truth there; on a real Vite/Node
276
+ * server `remoteAddress` is always populated.
277
+ */
278
+ declare const isLoopbackAddress: (remoteAddress: string | undefined) => boolean;
279
+ /**
280
+ * Explicit opt-out for the forwarding-header refusal below — set to `"1"` to
281
+ * allow a proxied request through once you've confirmed the forwarding is
282
+ * your own trusted dev tunnel (Codespaces, devcontainers, Gitpod, Cloud
283
+ * Workstations, ngrok, a Docker reverse proxy, …), not an attacker's relay.
284
+ * Does not relax the socket-peer or `Host` checks — only this one.
285
+ */
286
+ declare const ALLOW_FORWARDED_ENV = "LUNORA_STUDIO_ALLOW_FORWARDED";
287
+ /**
288
+ * Per-request transport gate, independent of a host's config-derived bind
289
+ * intent (e.g. Vite's `isNonLoopbackBind`, the CLI's `isLoopback`). In Vite
290
+ * middleware mode the real bind belongs to the embedding server (so the
291
+ * config check measures the wrong thing); here we read the actual socket peer
292
+ * and the `Host` header. Returns a refusal reason, or `undefined` when the
293
+ * connection is loopback-local.
294
+ *
295
+ * `logger`, when supplied, gets a `warnOnce` line for a forwarding-header
296
+ * refusal — naming the specific header seen and the {@link ALLOW_FORWARDED_ENV}
297
+ * escape hatch — so the failure points at its cause in the terminal running
298
+ * the dev server, not just as an opaque 403 in the browser.
299
+ */
300
+ declare const transportRejectionReason: (request: IncomingMessage, logger?: WarnLogger) => string | undefined;
301
+ export { ALLOW_FORWARDED_ENV, type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, headerValue, isLoopbackAddress, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp, transportRejectionReason };
@@ -266,4 +266,36 @@ type LocalEndpointHandler = (request: LocalEndpointRequest) => LocalEndpointResp
266
266
  * instead of always defaulting to `"lunora"`.
267
267
  */
268
268
  declare const serveJsonHandler: (request: IncomingMessage, response: ServerResponse, handle: LocalEndpointHandler, projectRoot: string, schemaDirectory?: string) => void;
269
- export { type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp };
269
+ /** A single header value, lower-cased and trimmed; `undefined` when absent or array-valued. */
270
+ declare const headerValue: (raw: string | string[] | undefined) => string | undefined;
271
+ /**
272
+ * True for an IPv4/IPv6 loopback peer (`127.0.0.0/8`, `::1`, and the
273
+ * IPv4-mapped `::ffff:127.x`). A missing address means we cannot read the
274
+ * transport (e.g. a mocked request in tests) — treated as loopback so the
275
+ * config-derived gate stays the source of truth there; on a real Vite/Node
276
+ * server `remoteAddress` is always populated.
277
+ */
278
+ declare const isLoopbackAddress: (remoteAddress: string | undefined) => boolean;
279
+ /**
280
+ * Explicit opt-out for the forwarding-header refusal below — set to `"1"` to
281
+ * allow a proxied request through once you've confirmed the forwarding is
282
+ * your own trusted dev tunnel (Codespaces, devcontainers, Gitpod, Cloud
283
+ * Workstations, ngrok, a Docker reverse proxy, …), not an attacker's relay.
284
+ * Does not relax the socket-peer or `Host` checks — only this one.
285
+ */
286
+ declare const ALLOW_FORWARDED_ENV = "LUNORA_STUDIO_ALLOW_FORWARDED";
287
+ /**
288
+ * Per-request transport gate, independent of a host's config-derived bind
289
+ * intent (e.g. Vite's `isNonLoopbackBind`, the CLI's `isLoopback`). In Vite
290
+ * middleware mode the real bind belongs to the embedding server (so the
291
+ * config check measures the wrong thing); here we read the actual socket peer
292
+ * and the `Host` header. Returns a refusal reason, or `undefined` when the
293
+ * connection is loopback-local.
294
+ *
295
+ * `logger`, when supplied, gets a `warnOnce` line for a forwarding-header
296
+ * refusal — naming the specific header seen and the {@link ALLOW_FORWARDED_ENV}
297
+ * escape hatch — so the failure points at its cause in the terminal running
298
+ * the dev server, not just as an opaque 403 in the browser.
299
+ */
300
+ declare const transportRejectionReason: (request: IncomingMessage, logger?: WarnLogger) => string | undefined;
301
+ export { ALLOW_FORWARDED_ENV, type LocalEndpointHandler, type LocalEndpointRequest, type LocalEndpointResponse, POLICY_SCAFFOLD_ENDPOINT, type PolicyScaffoldBody, type PolicyScaffoldRequest, type PolicyScaffoldResponse, SCHEMA_EDIT_ENDPOINT, SEED_ENDPOINT, type SchemaEditRequest, type SchemaEditResponse, type SeedRequest, type SeedRequestBody, type SeedResponse, type StudioAssets, type StudioHtmlConfig, type WarnLogger, type WirePolicyEdit, assetContentType, handlePolicyScaffoldRequest, handleSchemaEditRequest, handleSeedRequest, headerValue, isLoopbackAddress, isStandaloneModulePath, loadStudioAssets, parseDevVariable, readStandaloneAsset, renderStudioHtml, resolveAdminToken, resolveStandaloneDirectory, serveJsonHandler, studioAssetsStamp, transportRejectionReason };
@@ -1 +1 @@
1
- import{parseDevVariable as t,resolveAdminToken as r}from"../packem_shared/parseDevVariable-NAVJ8tgA.mjs";import{assetContentType as s,isStandaloneModulePath as d,loadStudioAssets as l,readStandaloneAsset as n,resolveStandaloneDirectory as S,studioAssetsStamp as m}from"../packem_shared/assetContentType-DNZyUOuF.mjs";import{POLICY_SCAFFOLD_ENDPOINT as p,handlePolicyScaffoldRequest as i}from"../packem_shared/POLICY_SCAFFOLD_ENDPOINT-CUYWD0Mh.mjs";import{default as D}from"../packem_shared/renderStudioHtml-B2AkxcW1.mjs";import{SCHEMA_EDIT_ENDPOINT as x,handleSchemaEditRequest as A}from"../packem_shared/SCHEMA_EDIT_ENDPOINT-BNB1ogaj.mjs";import{SEED_ENDPOINT as P,handleSeedRequest as T}from"../packem_shared/SEED_ENDPOINT-BaYShU8q.mjs";import{serveJsonHandler as I}from"../packem_shared/serveJsonHandler-C5GWlWJF.mjs";export{p as POLICY_SCAFFOLD_ENDPOINT,x as SCHEMA_EDIT_ENDPOINT,P as SEED_ENDPOINT,s as assetContentType,i as handlePolicyScaffoldRequest,A as handleSchemaEditRequest,T as handleSeedRequest,d as isStandaloneModulePath,l as loadStudioAssets,t as parseDevVariable,n as readStandaloneAsset,D as renderStudioHtml,r as resolveAdminToken,S as resolveStandaloneDirectory,I as serveJsonHandler,m as studioAssetsStamp};
1
+ import{parseDevVariable as t,resolveAdminToken as r}from"../packem_shared/parseDevVariable-B193_28t.mjs";import{assetContentType as s,isStandaloneModulePath as d,loadStudioAssets as n,readStandaloneAsset as l,resolveStandaloneDirectory as p,studioAssetsStamp as m}from"../packem_shared/assetContentType-DNZyUOuF.mjs";import{POLICY_SCAFFOLD_ENDPOINT as f,handlePolicyScaffoldRequest as i}from"../packem_shared/POLICY_SCAFFOLD_ENDPOINT-CUYWD0Mh.mjs";import{default as E}from"../packem_shared/renderStudioHtml-B2AkxcW1.mjs";import{SCHEMA_EDIT_ENDPOINT as A,handleSchemaEditRequest as x}from"../packem_shared/SCHEMA_EDIT_ENDPOINT-BNB1ogaj.mjs";import{SEED_ENDPOINT as O,handleSeedRequest as R}from"../packem_shared/SEED_ENDPOINT-BaYShU8q.mjs";import{serveJsonHandler as c}from"../packem_shared/serveJsonHandler-DQ-Yqa4J.mjs";import{ALLOW_FORWARDED_ENV as P,headerValue as T,isLoopbackAddress as I,transportRejectionReason as L}from"../packem_shared/ALLOW_FORWARDED_ENV-DGdbCDre.mjs";export{P as ALLOW_FORWARDED_ENV,f as POLICY_SCAFFOLD_ENDPOINT,A as SCHEMA_EDIT_ENDPOINT,O as SEED_ENDPOINT,s as assetContentType,i as handlePolicyScaffoldRequest,x as handleSchemaEditRequest,R as handleSeedRequest,T as headerValue,I as isLoopbackAddress,d as isStandaloneModulePath,n as loadStudioAssets,t as parseDevVariable,l as readStandaloneAsset,E as renderStudioHtml,r as resolveAdminToken,p as resolveStandaloneDirectory,c as serveJsonHandler,m as studioAssetsStamp,L as transportRejectionReason};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/config",
3
- "version": "1.0.0-alpha.107",
3
+ "version": "1.0.0-alpha.109",
4
4
  "description": "Internal shared CLI + Vite config layer for Lunora: wrangler.jsonc validation, binding inference, and .dev.vars scaffolding",
5
5
  "keywords": [
6
6
  "bindings",
@@ -54,10 +54,10 @@
54
54
  "access": "public"
55
55
  },
56
56
  "dependencies": {
57
- "@lunora/codegen": "1.0.0-alpha.82",
58
- "@lunora/container": "1.0.0-alpha.18",
59
- "@lunora/errors": "1.0.0-alpha.10",
60
- "@lunora/seed": "1.0.0-alpha.56",
57
+ "@lunora/codegen": "1.0.0-alpha.84",
58
+ "@lunora/container": "1.0.0-alpha.20",
59
+ "@lunora/errors": "1.0.0-alpha.12",
60
+ "@lunora/seed": "1.0.0-alpha.57",
61
61
  "@visulima/colorize": "2.0.0",
62
62
  "@visulima/find-ai-runner": "1.0.0",
63
63
  "dockerode": "^5.0.1",
@@ -1 +0,0 @@
1
- const u=".dev.vars",E=".dev.vars.example",r=/^[A-Za-z_]\w*$/u,i=/\r?\n/u,n=e=>e.length>=2&&(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))?e.slice(1,-1):e,l=e=>{const s=e.trim();if(s===""||s.startsWith("#"))return;const a=s.indexOf("=");if(a<=0)return;const t=s.slice(0,a).trim();if(r.test(t))return{key:t,value:s.slice(a+1).trim()}},o=e=>{const s=[];for(const a of e.split(i)){const t=l(a);t&&s.push({key:t.key,value:n(t.value)})}return s};export{E as DEV_VARS_EXAMPLE_FILE,u as DEV_VARS_FILE,r as DEV_VARS_KEY_PATTERN,i as DEV_VARS_NEWLINE,o as parseDevVariableEntries,l as splitDevVariableLine,n as unquoteDevVariable};
@@ -1 +0,0 @@
1
- import{existsSync as u,readFileSync as $}from"node:fs";import{dirname as y,join as l}from"node:path";import{WORKER_ENTRY_FALLBACKS as w}from"./inferLunoraBindings-DhfIOVhl.mjs";import{discoverSchemaInfo as j}from"./discoverSchemaInfo-C8X9mo-i.mjs";import{isCacheEnabled as A,WORKERS_CACHE_MIN_DATE as g}from"./WORKERS_CACHE_MIN_DATE-B1h_wNDN.mjs";import{findWranglerFile as S,readWranglerJsonc as k}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const b="2026-04-07",je="web_socket_auto_reply_to_close",f=/^\d{4}-\d{2}-\d{2}$/,M=(e,n,s)=>{if(n.length===0)return;const t=e.vectorize??[],a=new Set(t.filter(Boolean).map(r=>r?.index_name));for(const r of n)a.has(r)||s.push(`schema declares vector index "${r}"; wrangler "vectorize" must include a binding with index_name "${r}"`)},q=new Set(["basic","dev","lite","standard","standard-1","standard-2","standard-3","standard-4"]),x={disk_mb:2e4,memory_mib:12288,vcpu:4},O=(e,n,s)=>{const t=e.instance_type;if(t===void 0)return;if(typeof t=="string"){q.has(t)||s.push(`${n} has unknown instance_type "${t}" — expected lite, basic, standard-1..4, or a custom { vcpu, memory_mib, disk_mb } object`);return}for(const[c,o]of Object.entries(x)){const d=t[c];d!==void 0&&(typeof d!="number"||d<=0||d>o)&&s.push(`${n} custom instance_type ${c} must be a positive number ≤ ${String(o)} (got ${String(d)})`)}const{disk_mb:a,memory_mib:r,vcpu:i}=t;if(typeof i=="number"&&typeof r=="number"&&r<i*3072&&s.push(`${n} custom instance_type needs ≥ 3 GiB (3072 MiB) memory per vCPU (got ${String(r)} MiB for ${String(i)} vCPU)`),typeof r=="number"&&typeof a=="number"){const c=Math.floor(r/1024*2e3);a>c&&s.push(`${n} custom instance_type allows ≤ 2 GB disk per GiB memory (≤ ${String(c)} MB for ${String(r)} MiB memory; got ${String(a)} MB)`)}},R=(e,n,s)=>{const{boundClasses:t,errors:a,nonSqliteClasses:r,sqliteClasses:i,warnings:c}=s;if(!e||typeof e!="object"||typeof e.class_name!="string"||e.class_name.length===0){a.push(`${n} must have a non-empty "class_name" naming its container-enabled Durable Object class`);return}(typeof e.image!="string"||e.image.length===0)&&a.push(`${n} ("${e.class_name}") must have an "image" — a Dockerfile path or a registry reference`),t.has(e.class_name)||a.push(`${n} class "${e.class_name}" has no matching durable_objects binding — run \`lunora dev\` to auto-reconcile wrangler.jsonc, or add { "name": "...", "class_name": "${e.class_name}" }`),i.has(e.class_name)||a.push(r.has(e.class_name)?`${n} class "${e.class_name}" is registered via "new_classes" but containers require SQLite-backed DOs — move it to "new_sqlite_classes"`:`${n} class "${e.class_name}" is missing from migrations — add a migration entry with "new_sqlite_classes": ["${e.class_name}"]`),O(e,`${n} ("${e.class_name}")`,a),e.max_instances===void 0&&c.push(`${n} ("${e.class_name}") declares no max_instances — set a cap so a traffic spike can't fan out unbounded container spend`)},m=e=>Array.isArray(e)?e.filter(n=>n!==null&&typeof n=="object"):[],E=(e,n,s)=>{if(e.containers===void 0)return;if(!Array.isArray(e.containers)){n.push("containers must be an array of { class_name, image, ... } entries");return}const t=e.containers;if(t.length===0)return;const a=new Set(m(e.durable_objects?.bindings).map(o=>o.class_name)),r=e.migrations??[],i=new Set(r.flatMap(o=>[...o?.new_sqlite_classes??[]])),c=new Set(r.flatMap(o=>[...o?.new_classes??[]]));for(const[o,d]of t.entries())R(d,`containers[${String(o)}]`,{boundClasses:a,errors:n,nonSqliteClasses:c,sqliteClasses:i,warnings:s});e.observability?.enabled!==!0&&s.push('containers are configured but observability is not enabled — container logs will not be captured (add { "observability": { "enabled": true } })')},D=(e,n)=>{if(e.workflows===void 0)return;if(!Array.isArray(e.workflows)){n.push("workflows must be an array of { name, binding, class_name } entries");return}const s=e.workflows;for(const[t,a]of s.entries()){const r=`workflows[${String(t)}]`;if(!a||typeof a!="object"){n.push(`${r} must be a { name, binding, class_name } object`);continue}(typeof a.binding!="string"||a.binding.length===0)&&n.push(`${r} must have a non-empty "binding" naming the Workflow binding (e.g. WORKFLOW_ORDER_PIPELINE)`),(typeof a.class_name!="string"||a.class_name.length===0)&&n.push(`${r} must have a non-empty "class_name" naming the exported WorkflowEntrypoint class`),(typeof a.name!="string"||a.name.length===0)&&n.push(`${r} must have a non-empty "name" naming the deployed workflow`)}},W=(e,n)=>{if(e===void 0)return;if(!Array.isArray(e)){n.push("queues.producers must be an array of { binding, queue } entries");return}const s=e;for(const[t,a]of s.entries()){const r=`queues.producers[${String(t)}]`;if(!a||typeof a!="object"){n.push(`${r} must be a { binding, queue } object`);continue}(typeof a.binding!="string"||a.binding.length===0)&&n.push(`${r} must have a non-empty "binding" naming the Queue producer (e.g. QUEUE_EMAIL)`),(typeof a.queue!="string"||a.queue.length===0)&&n.push(`${r} must have a non-empty "queue" naming the deployed queue`)}},L=(e,n)=>{if(e===void 0)return;if(!Array.isArray(e)){n.push("queues.consumers must be an array of { queue } entries");return}const s=e;for(const[t,a]of s.entries()){const r=`queues.consumers[${String(t)}]`;if(!a||typeof a!="object"){n.push(`${r} must be a { queue } object`);continue}(typeof a.queue!="string"||a.queue.length===0)&&n.push(`${r} must have a non-empty "queue" naming the consumed queue`)}},C=(e,n)=>{if(e.queues!==void 0){if(typeof e.queues!="object"||Array.isArray(e.queues)){n.push("queues must be a { producers, consumers } object");return}W(e.queues.producers,n),L(e.queues.consumers,n)}},N=(e,n)=>{if(e.secrets_store_secrets===void 0)return;if(!Array.isArray(e.secrets_store_secrets)){n.push("secrets_store_secrets must be an array of { binding, store_id, secret_name } entries");return}const s=e.secrets_store_secrets;for(const[t,a]of s.entries()){const r=`secrets_store_secrets[${String(t)}]`;if(!a||typeof a!="object"){n.push(`${r} must be a { binding, store_id, secret_name } object`);continue}for(const i of["binding","store_id","secret_name"])(typeof a[i]!="string"||a[i].length===0)&&n.push(`${r} must have a non-empty "${i}"`)}},p=e=>typeof e=="string"&&e.length>0,_=e=>e,I=[{arrayMessage:"kv_namespaces must be an array of { binding, id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the KV namespace binding`,hintField:"id",hintMessage:(e,n)=>`${e} ("${n}") has no "id" — run \`wrangler kv namespace create\` and set the namespace id, or the binding can't resolve`,key:"kv_namespaces"},{arrayMessage:"flagship must be an array of { binding, app_id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Flagship binding`,hintField:"app_id",hintMessage:(e,n)=>`${e} ("${n}") has no "app_id" — create a Flagship app and set its id, or the binding can't resolve`,key:"flagship"},{arrayMessage:"hyperdrive must be an array of { binding, id } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Hyperdrive binding`,hintField:"id",hintMessage:(e,n)=>`${e} ("${n}") has no "id" — run \`wrangler hyperdrive create\` and set the id, or the binding can't connect`,key:"hyperdrive"},{arrayMessage:"pipelines must be an array of { binding, stream } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Pipelines binding`,hintField:["stream","pipeline"],hintMessage:(e,n)=>`${e} ("${n}") has no "stream" — run \`wrangler pipelines create <name>\` and set the stream name, or the binding can't resolve`,key:"pipelines"},{arrayMessage:"analytics_engine_datasets must be an array of { binding, dataset } entries",bindingMessage:e=>`${e} must have a non-empty "binding" naming the Analytics Engine binding`,hintField:"dataset",hintMessage:(e,n)=>`${e} ("${n}") has no "dataset" — it defaults to the binding name; set it explicitly to avoid drift`,key:"analytics_engine_datasets"}],B=(e,n,s,t)=>{const a=e[n.key];if(a!==void 0){if(!Array.isArray(a)){s.push(n.arrayMessage);return}for(const[r,i]of _(a).entries()){const c=`${n.key}[${String(r)}]`;if(!i||typeof i!="object"||!p(i.binding)){s.push(n.bindingMessage(c));continue}(typeof n.hintField=="string"?[n.hintField]:n.hintField).some(o=>p(i[o]))||t.push(n.hintMessage(c,i.binding))}}},F=[{key:"browser",message:'browser must be an object with a non-empty "binding" (e.g. { "binding": "BROWSER" })'},{key:"images",message:'images must be an object with a non-empty "binding" (e.g. { "binding": "IMAGES" })'}],P=(e,n,s)=>{const t=e[n.key];t!==void 0&&(typeof t!="object"||Array.isArray(t)||!p(t.binding))&&s.push(n.message)},T=[{arrayMessage:"services must be an array of { binding, service, entrypoint? } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding" naming the service binding`},{field:"service",message:e=>`${e} must have a non-empty "service" naming the target Worker`}],key:"services",objectMessage:e=>`${e} must be a { binding, service, entrypoint? } object`},{arrayMessage:"dispatch_namespaces must be an array of { binding, namespace } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding"`},{field:"namespace",message:e=>`${e} must have a non-empty "namespace" naming the dispatch namespace`}],key:"dispatch_namespaces",objectMessage:e=>`${e} must be a { binding, namespace } object`},{arrayMessage:"mtls_certificates must be an array of { binding, certificate_id } entries",fields:[{field:"binding",message:e=>`${e} must have a non-empty "binding"`},{field:"certificate_id",message:e=>`${e} must have a non-empty "certificate_id" (upload via \`wrangler mtls-certificate upload\`)`}],key:"mtls_certificates",objectMessage:e=>`${e} must be a { binding, certificate_id } object`}],U=(e,n,s)=>{const t=e[n.key];if(t!==void 0){if(!Array.isArray(t)){s.push(n.arrayMessage);return}for(const[a,r]of _(t).entries()){const i=`${n.key}[${String(a)}]`;if(!r||typeof r!="object"){s.push(n.objectMessage(i));continue}for(const c of n.fields)p(r[c.field])||s.push(c.message(i))}}},G=(e,n,s)=>{const t=e.send_email;if(t===void 0)return;if(!Array.isArray(t)){n.push("send_email must be an array of { name, destination_address? } entries");return}const a=t;for(const[r,i]of a.entries())(!i||typeof i!="object"||typeof i.name!="string"||i.name.length===0)&&s.push(`send_email[${String(r)}] has no non-empty "name" naming the send-email binding — set one before deploying`)},Y=(e,n)=>{e.logpush!==void 0&&typeof e.logpush!="boolean"&&n.push('logpush must be a boolean (set "logpush": true to enable Cloudflare Logpush)')},H=(e,n)=>{const{placement:s}=e;if(s!==void 0){if(typeof s!="object"||Array.isArray(s)){n.push('placement must be an object (e.g. { "mode": "smart" })');return}s.mode!==void 0&&s.mode!=="smart"&&n.push('placement.mode must be "smart" (the only supported Smart Placement mode)')}},K=(e,n)=>{const{observability:s}=e;if(s===void 0)return;if(typeof s!="object"||Array.isArray(s)){n.push('observability must be an object (e.g. { "enabled": true, "head_sampling_rate": 1 })');return}const t=(a,r)=>{a!==void 0&&(typeof a!="number"||Number.isNaN(a)||a<0||a>1)&&n.push(`${r} must be a number in [0, 1] (the fraction of requests sampled)`)};t(s.head_sampling_rate,"observability.head_sampling_rate"),s.logs!==void 0&&(typeof s.logs!="object"||Array.isArray(s.logs)?n.push("observability.logs must be an object"):t(s.logs.head_sampling_rate,"observability.logs.head_sampling_rate"))},Q=(e,n)=>{const{cache:s}=e;if(s!==void 0){if(typeof s!="object"||s===null||Array.isArray(s)){n.push('cache must be an object (e.g. { "enabled": true })');return}s.enabled!==void 0&&typeof s.enabled!="boolean"&&n.push("cache.enabled must be a boolean (true or false)")}},z=(e,n)=>{const{exports:s}=e;if(s!==void 0){if(typeof s!="object"||s===null||Array.isArray(s)){n.push("exports must be an object keyed by entrypoint name");return}for(const[t,a]of Object.entries(s)){if(typeof a!="object"||a===null){n.push(`exports["${t}"] must be an object`);continue}a.type!==void 0&&typeof a.type!="string"&&n.push(`exports["${t}"].type must be a string`),a.cache!==void 0&&(typeof a.cache!="object"||a.cache===null||Array.isArray(a.cache)?n.push(`exports["${t}"].cache must be an object`):a.cache.enabled!==void 0&&typeof a.cache.enabled!="boolean"&&n.push(`exports["${t}"].cache.enabled must be a boolean`))}}},J=(e,n)=>{const{assets:s}=e;if(s!==void 0){if(typeof s!="object"||Array.isArray(s)){n.push('assets must be an object (e.g. { "directory": "./dist/client", "binding": "ASSETS" })');return}(typeof s.directory!="string"||s.directory.length===0)&&n.push('assets must declare a non-empty "directory" pointing at the built client output (e.g. "./dist/client")'),s.binding!==void 0&&(typeof s.binding!="string"||s.binding.length===0)&&n.push('assets.binding must be a non-empty string (e.g. "ASSETS")'),s.html_handling!==void 0&&typeof s.html_handling!="string"&&n.push("assets.html_handling must be a string"),s.not_found_handling!==void 0&&typeof s.not_found_handling!="string"&&n.push("assets.not_found_handling must be a string")}},Z=(e,n)=>{const s=e.tail_consumers;if(s===void 0)return;if(!Array.isArray(s)){n.push("tail_consumers must be an array of { service, environment? } entries");return}const t=s;for(const[a,r]of t.entries())(!r||typeof r!="object"||typeof r.service!="string"||r.service.length===0)&&n.push(`tail_consumers[${String(a)}] must have a non-empty "service" naming the consumer Worker`)},Ae=(e,n)=>{const s=e.tail_consumers??[];return s.some(t=>!!t&&t?.service===n.service&&t?.environment===n.environment)?e:{...e,tail_consumers:[...s,n]}},V=new Set(["1","enabled","on","true","yes"]),X=(e,n)=>{const{vars:s}=e;if(!s||typeof s!="object")return;const t=s.LUNORA_ALLOWED_ORIGINS,a=s.LUNORA_CORS_ALLOW_CREDENTIALS,r=typeof t=="string"&&t.split(",").some(c=>c.trim()==="*"),i=typeof a=="string"&&V.has(a.trim().toLowerCase());r&&i&&n.push('vars.LUNORA_ALLOWED_ORIGINS includes a "*" wildcard while vars.LUNORA_CORS_ALLOW_CREDENTIALS is on — browsers reject this combination and it defeats the allowlist; name explicit origins or drop credentials')},v=(e,n)=>{const s=[],t=[];if(!e||typeof e!="object")return s.push("wrangler config is not a valid object"),{errors:s,valid:!1,warnings:t};m(e.durable_objects?.bindings).find(r=>r.name==="SHARD"&&r.class_name==="ShardDO")||s.push('durable_objects.bindings must include { "name": "SHARD", "class_name": "ShardDO" } — run `lunora dev` to auto-reconcile wrangler.jsonc, or add the binding manually');const a=e.compatibility_date??"";a&&!f.test(a)?s.push(`compatibility_date must be in YYYY-MM-DD format (got "${a}")`):a<b&&s.push(`compatibility_date must be >= "${b}" (got "${a||"<missing>"}")`),A(e)&&f.test(a)&&a<g&&s.push(`cache.enabled requires compatibility_date >= "${g}" (got "${a||"<missing>"}")`),n?.hasGlobalTable&&(m(e.d1_databases).find(r=>r.binding==="DB")||s.push('schema declares .global() tables; d1_databases must include a binding named "DB" — run `lunora dev` to auto-reconcile wrangler.jsonc, or add the binding manually')),M(e,n?.vectorIndexNames??[],s),Z(e,s),E(e,s,t),D(e,s),C(e,s),N(e,s);for(const r of I)B(e,r,s,t);for(const r of T)U(e,r,s);for(const r of F)P(e,r,s);return G(e,s,t),Y(e,s),H(e,s),K(e,s),J(e,s),Q(e,s),z(e,s),X(e,s),{errors:s,valid:s.length===0,warnings:t}},Se=v,ee=(e,n,s)=>{const t=[];for(const a of e){const r=a?.image;typeof r!="string"||!(r.startsWith("./")||r.startsWith("../")||r.startsWith("/")||r.includes("Dockerfile"))||u(r.startsWith("/")?r:l(n,r))||t.push(`containers image "${r}" does not exist (resolved relative to ${s}); create the Dockerfile or point image at a registry reference`)}return t},se=(e,n,s)=>{if(typeof e=="string"&&e.length>0){const t=l(y(s),e);return u(t)?t:void 0}return w.map(t=>l(n,t)).find(t=>u(t))},ne=/\/\/[^\n]*|\/\*.*?\*\/|"[^"\n]*"|'[^'\n]*'|`[^`]*`/gsu,te=e=>e.replaceAll(ne,n=>n.replaceAll(/[^\n]/gu," ")),ae=/\bexport\s*\*\s*(?:as\s+\w+\s*)?from\b/u,re=/\bexport\b/gu,ie=/^\s*(?:(?:abstract|async|declare|default)\s+)*/u,oe=/^(?:class|const|function|let|var)\s+(?<name>[$A-Z_a-z][\w$]*)/u,ce=/^\s*(?:const|let|var)\s*\{/u,de=/^[$A-Z_a-z][\w$]*/u,ue=/^type\b/u,le=/\s+/u,me=e=>{const n=[];for(const s of e.split(",")){const t=s.trim().split(le).filter(Boolean),a=t.at(-1);a!==void 0&&t[0]!=="type"&&n.push(a)}return n},pe=e=>{const n=[];for(const s of e.split(",")){const t=s.includes(":")?s.slice(s.indexOf(":")+1):s,a=de.exec(t.trim());a&&n.push(a[0])}return n},h=e=>{const n=e.indexOf("{"),s=e.indexOf("}",n);return n===-1||s===-1?void 0:e.slice(n+1,s)},ge=e=>{const n=e.trimStart();if(ue.test(n))return[];if(n.startsWith("{")){const t=h(n);return t===void 0?[]:me(t)}if(ce.test(e)){const t=h(e);return t===void 0?[]:pe(t)}const s=oe.exec(e.replace(ie,""));return s?.groups?.name===void 0?[]:[s.groups.name]},be=e=>{const n=new Set;for(const s of e.matchAll(re))for(const t of ge(e.slice(s.index+6)))n.add(t);return n},fe=(e,n,s)=>{const t=se(e.main,n,s);if(t===void 0)return[];let a;try{a=$(t,"utf8")}catch{return[]}const r=te(a);if(ae.test(r))return[];const i=[];for(const o of m(e.durable_objects?.bindings))typeof o.class_name=="string"&&o.class_name.length>0&&o.script_name===void 0&&i.push({className:o.class_name,label:"durable_objects.bindings"});for(const o of e.workflows??[])typeof o?.class_name=="string"&&o.class_name.length>0&&o.script_name===void 0&&i.push({className:o.class_name,label:"workflows"});const c=be(r);return i.filter(o=>!c.has(o.className)).map(o=>`${o.label} declares class "${o.className}" but the worker entry (${t}) does not export it — wrangler refuses to bundle a Worker whose Durable Object classes are not exported. Add \`export { ${o.className} } from "…";\` to the entry.`)},ke=e=>{const n=e.schemaDir??"lunora",s=S(e.projectRoot);if(!s){const d=`wrangler.jsonc not found in ${e.projectRoot}; create one declaring at least the SHARD durable object binding.`;return{problems:[d],report:{errors:[d],valid:!1,warnings:[]},wranglerPath:void 0}}const{parsed:t}=k(s);if(t===void 0){const d=`failed to parse ${s} as JSONC.`;return{problems:[d],report:{errors:[d],valid:!1,warnings:[]},wranglerPath:s}}const{error:a,info:r}=j(e.projectRoot,n),i=v(t,r);a!==void 0&&i.warnings.push(`schema parse failed in ${n}/schema.ts: ${a}`);const c=y(s);i.errors.push(...ee(t.containers??[],c,s)),i.warnings.push(...fe(t,e.projectRoot,s));const o=t.assets?.directory;if(typeof o=="string"&&o.length>0){const d=o.startsWith("/")?o:l(c,o);u(d)||i.warnings.push(`assets.directory "${o}" does not exist yet — it is created by the client build; run the build before deploy`)}return i.valid=i.errors.length===0,{problems:i.errors,report:i,wranglerPath:s}};export{b as REQUIRED_COMPATIBILITY_DATE,je as REQUIRED_FLAG,Se as validateWrangler,v as validateWranglerConfig,ke as validateWranglerProject,Ae as withTailConsumer};
@@ -1 +0,0 @@
1
- import{existsSync as d,statSync as k,readFileSync as b,readdirSync as C}from"node:fs";import{init as T,parse as f}from"es-module-lexer";import{discoverAgentInfo as B}from"./discoverAgentInfo-BJm0QtoI.mjs";import{WRANGLER_FILES as P,readWranglerJsonc as L}from"./WRANGLER_FILES-Bi_18Pj6.mjs";import{discoverContainerInfo as K}from"./discoverContainerInfo-CYG9j2LY.mjs";import{FLAGS_FILENAME as W,discoverFlags as H,QUEUES_FILENAME as M,discoverQueues as q}from"@lunora/codegen";import{Project as D}from"ts-morph";import{join as c}from"node:path";import{discoverSchemaInfo as G}from"./discoverSchemaInfo-C8X9mo-i.mjs";import{discoverWorkflowInfo as U}from"./discoverWorkflowInfo-Bbc0u2gE.mjs";const X=(e,r)=>{const t=c(e,r,W);if(!d(t))return{};try{const s=new D({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});return{flags:H(s,c(e,r))}}catch(s){return{error:s instanceof Error?s.message:String(s)}}},Y=(e,r)=>{const t=c(e,r,M);if(!d(t))return{queues:[]};try{const s=new D({skipAddingFilesFromTsConfig:!0,useInMemoryFileSystem:!1});return{queues:q(s,c(e,r))}}catch(s){return{error:s instanceof Error?s.message:String(s),queues:[]}}},Q=new Set([".cjs",".cts",".js",".jsx",".mjs",".mts",".ts",".tsx"]),z=new Set([".git",".lunora-cache",".wrangler","_generated","dist","node_modules"]),J=["lunora","src"],V=["src/server/index.ts","src/server/index.tsx","src/index.ts","src/worker.ts"],$={SchedulerDO:"SCHEDULER",SessionDO:"SESSION",ShardDO:"SHARD"},O=Object.keys($),Z={SchedulerDO:/\btype\s+SchedulerDO\b/,SessionDO:/\btype\s+SessionDO\b/,ShardDO:/\btype\s+ShardDO\b/},ee=/\benv\s*\.\s*DB\b/,re=/\benv\s*\.\s*AI\b/,A=/\bctx\s*\.\s*pipelines\b/,_=/^\s*import\s+type\b/,se=new Set(["@lunora/agent","@lunora/agent/sandbox"]),te=e=>{const r=e.indexOf("{");if(r===-1)return"";const t=e.indexOf("}",r+1);return t===-1?e.slice(r+1):e.slice(r+1,t)},ne=/\btype\s+browserTool\b/,oe=/\bbrowserTool\b/,ae=/import\s+\{[^}]*\bbrowserTool\b[^}]*\}\s+from\s+["']@lunora\/agent(?:\/sandbox)?["']/,ie=e=>{if(_.test(e))return!1;const r=te(e);return oe.test(r)&&!ne.test(r)},ce=e=>{try{const[r]=f(e);return r.some(t=>t.n!==void 0&&se.has(t.n)&&ie(e.slice(t.ss,t.se)))}catch{return ae.test(e)}},g={usesAi:{pattern:/\bfrom\s+["']@lunora\/ai["']/,source:"@lunora/ai"},usesAnalytics:{pattern:/\bfrom\s+["']@lunora\/bindings\/analytics["']/,source:"@lunora/bindings/analytics"},usesAuth:{pattern:/\bfrom\s+["']@lunora\/auth["']/,source:"@lunora/auth"},usesBrowser:{pattern:/\bfrom\s+["']@lunora\/browser["']/,source:"@lunora/browser"},usesHyperdrive:{pattern:/\bfrom\s+["']@lunora\/hyperdrive["']/,source:"@lunora/hyperdrive"},usesImages:{pattern:/\bfrom\s+["']@lunora\/bindings\/images["']/,source:"@lunora/bindings/images"},usesKv:{pattern:/\bfrom\s+["']@lunora\/bindings\/kv["']/,source:"@lunora/bindings/kv"},usesMail:{pattern:/\bfrom\s+["']@lunora\/mail["']/,source:"@lunora/mail"},usesPayment:{pattern:/\bfrom\s+["']@lunora\/payment["']/,source:"@lunora/payment"},usesPipelines:{pattern:A,source:"@lunora/bindings/pipelines"},usesScheduler:{pattern:/\bfrom\s+["']@lunora\/scheduler["']/,source:"@lunora/scheduler"},usesStorage:{pattern:/\bfrom\s+["']@lunora\/storage["']/,source:"@lunora/storage"},usesX402Charge:{pattern:/\bfrom\s+["']@lunora\/x402\/charge["']/,source:"@lunora/x402/charge"},usesX402Pay:{pattern:/\bfrom\s+["']@lunora\/x402\/pay["']/,source:"@lunora/x402/pay"}},p=Object.keys(g),ue="STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET (Stripe) or POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET (Polar)",I=[...p,"needsD1"],de=()=>{const e={};for(const r of I)e[r]=!1;return e},l=Object.freeze(de()),y=(e,r)=>{const t={};for(const s of I)t[s]=e[s]||r[s];return t},le=e=>{for(const r of p)if(g[r].source===e)return{...l,[r]:!0};return l},pe=e=>{const[r]=f(e);let t=l;for(const s of r){const n=s.n;!n||_.test(e.slice(s.ss,s.se))||(t=y(t,le(n)))}return t},me=e=>{const r={...l};for(const t of p)r[t]=g[t].pattern.test(e);return r},be=e=>{let r;try{r=pe(e)}catch{r=me(e)}return y(r,{...l,needsD1:ee.test(e),usesAi:re.test(e),usesPipelines:A.test(e)})},S=(e,r)=>{let t;try{t=C(e,{withFileTypes:!0})}catch{return}for(const s of t){if(s.isDirectory()){z.has(s.name)||S(c(e,s.name),r);continue}const n=s.name.lastIndexOf(".");n!==-1&&Q.has(s.name.slice(n))&&r.push(c(e,s.name))}},fe=e=>{for(const r of P){const t=c(e,r);if(!d(t))continue;const{parsed:s}=L(t),n=s?.main;if(typeof n=="string"&&d(c(e,n)))return c(e,n);break}for(const r of V){const t=c(e,r);if(d(t))return t}},ge=e=>{const r=b(e,"utf8");let t;try{const[,s]=f(r);t=new Set(s.map(n=>n.n))}catch{t=new Set(O.filter(s=>new RegExp(String.raw`\bexport\b[^\n;]*\b${s}\b`).test(r)))}return O.filter(s=>t.has(s)&&!Z[s].test(r)).map(s=>({binding:$[s],className:s}))},j=e=>e.replaceAll(/[$()*+.?[\\\]^{|}]/gu,String.raw`\$&`),he=/(?:^|[\s,{])type$/u,ye=(e,r)=>{const t=r.ls>=0?r.ls:r.s;return he.test(e.slice(0,t).trimEnd())},Se=(e,r)=>{const t=j(r);return new RegExp(String.raw`\bexport\s+type\s+${t}\b`,"u").test(e)||new RegExp(String.raw`\bexport\s+type\s*\{[^}]*\b${t}\b`,"u").test(e)||new RegExp(String.raw`\bexport\s+\{[^}]*\btype\s+${t}\b`,"u").test(e)},w=(e,r,t)=>{if(r.length===0)return[];if(e===void 0)return r.map(o=>({...o,exported:!1}));const s=b(e,"utf8"),n=new RegExp(String.raw`\bexport\s*\*\s*from\s*["'][^"']*_generated\/${t}(?:\.js)?["']`).test(s);let a;try{const[,o]=f(s);a=new Set(o.filter(i=>!ye(s,i)).map(i=>i.n))}catch{a=new Set(r.map(o=>o.className).filter(o=>new RegExp(String.raw`\bexport\b[^\n;]*\b${j(o)}\b`,"u").test(s)&&!Se(s,o)))}return r.map(o=>{const i=n||a.has(o.className);return{...o,exported:i}})},we=(e,r)=>w(e,r,"containers"),xe=(e,r)=>w(e,r,"workflows"),ve=(e,r)=>w(e,r,"agents"),Ee=(e,r)=>G(e,r).info?.hasGlobalTable??!1,Ne=(e,r)=>{let t=l;for(const s of r){const n=c(e,s);if(!d(n)||!k(n).isDirectory())continue;const a=[];S(n,a);for(const o of a)t=y(t,be(b(o,"utf8")))}return t},Re=(e,r)=>{const t=c(e,r);if(!d(t)||!k(t).isDirectory())return!1;const s=[];return S(t,s),s.some(n=>ce(b(n,"utf8")))},Oe=(e,r,t)=>[...e.map(s=>s.exported?`${s.bindingName}/${s.className} (container "${s.exportName}" declared and exported)`:`hint: container "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/containers"\``),...r.map(s=>s.exported?`${s.bindingName}/${s.className} (workflow "${s.exportName}" declared and exported)`:`hint: workflow "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/workflows"\``),...t.map(s=>s.exported?`${s.bindingName}/${s.className} (agent "${s.exportName}" declared and exported)`:`hint: agent "${s.exportName}" is declared but ${s.className} is not exported by the worker entry — add \`export * from "./lunora/_generated/agents"\``)],ke=(e,r)=>[[e.usesAi,"AI (@lunora/ai imported or env.AI used)"],[e.usesAuth&&!r.has("SessionDO"),"hint: @lunora/auth is imported; its tables are D1-backed by default. For DO-backed auth (what @better-auth/scim needs), pass `namespace` to .auth() and export the generated auth DO class"],[e.usesScheduler&&!r.has("SchedulerDO"),"hint: @lunora/scheduler is imported but no SchedulerDO is exported by the worker entry"],[e.usesStorage,"hint: @lunora/storage is imported; add an r2_buckets binding (bucket binding names are user-defined)"],[e.usesMail,"hint: @lunora/mail is imported; set RESEND_API_KEY in .dev.vars (obtain at https://resend.com/api-keys)"],[e.usesPayment,`hint: @lunora/payment is imported; set the provider secrets in .dev.vars — ${ue}`],[e.usesBrowser,"browser (@lunora/browser imported) — self-describing { binding: BROWSER }"],[e.usesImages,"images (@lunora/bindings/images imported) — self-describing { binding: IMAGES }"],[e.usesAnalytics,"analytics_engine_datasets (@lunora/bindings/analytics imported) — self-describing { binding: ANALYTICS, dataset }"],[e.usesKv,"hint: @lunora/bindings/kv is imported; add a kv_namespaces binding ({ binding, id }) and pass env.<BINDING> to createKv() — the namespace id can't be auto-provisioned"],[e.usesHyperdrive,"hint: @lunora/hyperdrive is imported; run 'wrangler hyperdrive create' and add a 'hyperdrive' binding ({ binding, id }) — the id can't be auto-provisioned"],[e.usesPipelines,"hint: ctx.pipelines is used; run 'wrangler pipelines create <name>' and add a 'pipelines' binding ({ binding, pipeline }) — the pipeline resource can't be auto-provisioned"],[e.usesX402Charge,"hint: @lunora/x402/charge is imported; set the recipient wallet address as a [vars] entry (the var name is yours to choose) and pass it to the charge config — the x402 facilitator settles USDC to that address"],[e.usesX402Pay,"hint: @lunora/x402/pay is imported (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding for the agent wallet key (name it to match signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars, so the key can't be auto-provisioned"]].filter(([t])=>t).map(([,t])=>t),De=(e,r,t,s=[],n=[],a=[])=>{const o=new Set(e.map(u=>u.className)),i=e.map(u=>`${u.binding}/${u.className} (exported by worker entry)`);return r&&i.push("DB (.global() table declared)"),i.push(...Oe(s,n,a),...ke(t,o)),i},Le=async e=>{await T;const r=e.schemaDir??"lunora",t=e.scanDirs??J,s=Ne(e.projectRoot,t),n={...s,usesBrowser:s.usesBrowser||Re(e.projectRoot,r)},a=fe(e.projectRoot),o=a?ge(a):[],i=n.needsD1||Ee(e.projectRoot,r),u=we(a,K(e.projectRoot,r).containers),x=xe(a,U(e.projectRoot,r).workflows),v=ve(a,B(e.projectRoot,r).agents),F=[...Y(e.projectRoot,r).queues],{flags:m}=X(e.projectRoot,r),h=m?.provider==="flagship"&&m.mode==="binding"?m.bindingName:void 0,E={};for(const R of p)E[R]=n[R];const N=De(o,i,n,u,x,v);return h!==void 0&&N.push(`hint: lunora/flags.ts uses Flagship in binding mode; add a flagship binding ({ binding: "${h}", app_id }) — the app_id can't be auto-provisioned`),{agents:v,containers:u,durableObjects:o,flagshipBinding:h,needsD1:i,queues:F,signals:N,usesFlags:m!==void 0,workflows:x,...E}},Ke=e=>{const r=[];for(const t of p)e[t]&&r.push(g[t].source);return r};export{V as WORKER_ENTRY_FALLBACKS,Le as inferLunoraBindings,ye as isTypeOnlyExportEntry,Ke as packageNamesFromBindings,fe as resolveWorkerEntry};
@@ -1 +0,0 @@
1
- import{writeFileSync as N,readFileSync as S}from"node:fs";import{join as k}from"node:path";import{containerBuildTag as E}from"@lunora/container";import{DEV_VARS_FILE as D,parseDevVariableEntries as A}from"./DEV_VARS_EXAMPLE_FILE-DX6xGbpr.mjs";import{e as b}from"./jsonc-edit-BZVpxVA0.mjs";import{findWranglerFile as B,readWranglerJsonc as O}from"./WRANGLER_FILES-Bi_18Pj6.mjs";const v="<replace-with-d1-create-id>",C=e=>{const a=[];for(const n of e.containers)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"container",module:"containers"});for(const n of e.workflows)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"workflow",module:"workflows"});for(const n of e.agents)n.exported||a.push({className:n.className,exportName:n.exportName,kind:"agent",module:"agents"});return a},I=(e,a)=>{const n=e.flagshipBinding!==void 0&&!(a?.flagship??[]).some(s=>s.binding===e.flagshipBinding);return[[e.usesKv&&(a?.kv_namespaces?.length??0)===0,"@lunora/bindings/kv is used but no kv_namespaces binding exists; add a kv_namespaces entry ({ binding, id }) and pass env.<BINDING> to createKv() — the namespace id can't be auto-provisioned."],[e.usesHyperdrive&&(a?.hyperdrive?.length??0)===0,"@lunora/hyperdrive is used but no hyperdrive binding exists; run 'wrangler hyperdrive create' and add a 'hyperdrive' binding ({ binding, id }) — the id can't be auto-provisioned."],[e.usesPipelines&&(a?.pipelines?.length??0)===0,"ctx.pipelines is used but no pipelines binding exists; run 'wrangler pipelines create <name>' and add a 'pipelines' binding ({ binding, pipeline }) — the pipeline resource can't be auto-provisioned."],[n,`lunora/flags.ts uses Flagship in binding mode but no flagship binding "${e.flagshipBinding??""}" exists; add a flagship entry ({ binding: "${e.flagshipBinding??""}", app_id }) — the app_id can't be auto-provisioned.`]].filter(([s])=>s).map(([,s])=>s)},P=e=>[[e.usesX402Charge,"@lunora/x402/charge is used; set the recipient wallet address as a [vars] entry (the var name is your choice) and pass it to the charge config — the x402 facilitator settles USDC to that address."],[e.usesX402Pay,"@lunora/x402/pay is used (ActionCtx-only, spends real funds); add a secrets_store_secrets[] binding holding the agent wallet key (binding name == signer.secretName) and pair the pay rail with a spend policy — ctx.secrets reads a Secrets Store binding, not .dev.vars."]].filter(([a])=>a).map(([,a])=>a),f=(e,a,n)=>n.filter(s=>!s.exported).map(s=>`${e} "${s.exportName}" is declared but ${s.className} is not exported by the worker entry; add \`export * from "./lunora/_generated/${a}"\` so its binding can be provisioned.`),w=[{keys:["STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"],label:"Stripe"},{keys:["POLAR_ACCESS_TOKEN","POLAR_WEBHOOK_SECRET"],label:"Polar"},{keys:["CREEM_API_KEY","CREEM_WEBHOOK_SECRET"],label:"Creem"},{keys:["AUTUMN_SECRET_KEY","AUTUMN_WEBHOOK_SECRET"],label:"Autumn"},{keys:["DODO_PAYMENTS_API_KEY","DODO_PAYMENTS_WEBHOOK_KEY"],label:"Dodo Payments"}],R=()=>w.map(({keys:e,label:a})=>`${e[0]} + ${e[1]} (${a})`).join(" or "),T=e=>{let a;try{a=S(k(e,D),"utf8")}catch{return!1}const n=new Map(A(a).map(s=>[s.key,s.value]));return w.some(({keys:s})=>s.every(i=>(n.get(i)??"")!==""))},_=(e,a,n)=>{const s=new Set(e.durableObjects.map(d=>d.className)),i=[],o=(n?.r2_buckets?.length??0)>0,c=(n?.d1_databases?.some(d=>d.binding==="DB")??!1)||e.needsD1;return e.usesStorage&&!o&&i.push("@lunora/storage is used but R2 bucket bindings have user-defined names; add an r2_buckets entry and pass env.<BINDING> to createStorage()."),e.usesAuth&&!s.has("SessionDO")&&!c&&i.push("@lunora/auth is used but the worker entry exports no SessionDO; sessions are D1-backed, or export SessionDO to enable DO-backed sessions."),e.usesScheduler&&!s.has("SchedulerDO")&&i.push("@lunora/scheduler is used but the worker entry exports no SchedulerDO; export it so the SCHEDULER binding can be provisioned."),i.push(...f("container","containers",e.containers),...f("workflow","workflows",e.workflows),...f("agent","agents",e.agents)),e.containers.length>0&&n?.observability?.enabled===!1&&i.push("containers are declared but observability is explicitly disabled in wrangler.jsonc — container logs will not be captured."),e.usesPayment&&!T(a)&&i.push(`@lunora/payment is used; set one provider's secret pair in .dev.vars — ${R()}.`),i.push(...P(e),...I(e,n)),i},$=e=>{const a=new Set(e.map(s=>s.tag));let n=1;for(;a.has(`v${String(n)}`);)n+=1;return`v${String(n)}`},M=(e,a,n)=>{const s=a.durable_objects?.bindings??[],i=new Set(s.map(u=>u.name)),o=n.filter(u=>!i.has(u.binding));let c=e;const d=[];if(o.length>0){const u=[...s,...o.map(p=>({class_name:p.className,name:p.binding}))];c=b(c,["durable_objects","bindings"],u),d.push(...o.map(p=>`${p.binding}/${p.className}`))}const g=a.migrations??[],l=new Set(g.flatMap(u=>[...u.new_sqlite_classes??[],...u.new_classes??[]])),h=n.map(u=>u.className).filter(u=>!l.has(u));if(h.length>0){const u=[...g,{new_sqlite_classes:h,tag:$(g)}];c=b(c,["migrations"],u)}return{added:d,text:c}},K=(e,a)=>{const n=a.d1_databases??[];if(n.some(o=>o.binding==="DB"))return{added:[],text:e};const s=typeof a.name=="string"&&a.name.length>0?a.name:"lunora",i=[...n,{binding:"DB",database_id:v,database_name:s}];return{added:["DB (D1)"],text:b(e,["d1_databases"],i)}},x=(e,a,n,s,i)=>{const o=a[n]?.binding;return typeof o=="string"&&o.length>0?{added:[],text:e}:{added:[i],text:b(e,[n],{binding:s})}},q=(e,a)=>(a.analytics_engine_datasets?.length??0)>0?{added:[],text:e}:{added:["ANALYTICS (Analytics Engine)"],text:b(e,["analytics_engine_datasets"],[{binding:"ANALYTICS",dataset:"ANALYTICS"}])},W=e=>{if(typeof e=="string")return e;const a={};return e.diskMb!==void 0&&(a.disk_mb=e.diskMb),e.memoryMib!==void 0&&(a.memory_mib=e.memoryMib),e.vcpu!==void 0&&(a.vcpu=e.vcpu),a},Y=e=>e.image.kind==="dockerfile"?e.image.dockerfilePath:e.image.kind==="registry"?e.image.reference:E(e.exportName),L=e=>{const a={class_name:e.className,image:Y(e)};return e.image.kind==="dockerfile"&&(a.image_build_context=e.image.buildContext),e.buildArgs!==void 0&&e.image.kind!=="registry"&&(a.image_vars=e.buildArgs),e.instanceType!==void 0&&(a.instance_type=W(e.instanceType)),e.maxInstances!==void 0&&(a.max_instances=e.maxInstances),e.name!==void 0&&(a.name=e.name),e.rollout?.stepPercentage!==void 0&&(a.rollout_step_percentage=e.rollout.stepPercentage),e.rollout?.gracePeriodSeconds!==void 0&&(a.rollout_active_grace_period=e.rollout.gracePeriodSeconds),a},j=(e,a,n)=>{const s=a.containers??[],i=new Set(s.map(d=>d.class_name)),o=n.filter(d=>!i.has(d.className));if(o.length===0)return{added:[],text:e};const c=b(e,["containers"],[...s,...o.map(d=>L(d))]);return{added:o.map(d=>`containers/${d.className}`),text:c}},G=(e,a)=>{if(a.observability!==void 0)return{added:[],text:e};const n=b(e,["observability"],{enabled:!0,head_sampling_rate:1});return{added:["observability"],text:n}},H=e=>({binding:e.bindingName,class_name:e.className,name:e.name}),U=e=>({binding:e.bindingName,class_name:e.className,name:e.name}),F=(e,a,n,s=[])=>{const i=a.workflows??[],o=new Set(i.map(l=>l.class_name)),c=n.filter(l=>!o.has(l.className)),d=s.filter(l=>!o.has(l.className));if(c.length===0&&d.length===0)return{added:[],text:e};const g=b(e,["workflows"],[...i,...c.map(l=>H(l)),...d.map(l=>U(l))]);return{added:[...c.map(l=>`workflows/${l.className}`),...d.map(l=>`workflows/${l.className}`)],text:g}},V=(e,a,n)=>{const s=a.queues??{},i=s.producers??[],o=s.consumers??[],c=new Set(i.map(r=>r.binding)),d=new Set(o.map(r=>r.queue)),g=n.filter(r=>!c.has(r.bindingName)),l=n.filter(r=>!d.has(r.name));if(g.length===0&&l.length===0)return{added:[],text:e};const h=[...i,...g.map(r=>({binding:r.bindingName,queue:r.name}))],u=[...o,...l.map(r=>{const m={queue:r.name};return r.mode==="pull"&&(m.type="http_pull"),r.tuning.maxBatchSize!==void 0&&(m.max_batch_size=r.tuning.maxBatchSize),r.tuning.maxBatchTimeout!==void 0&&(m.max_batch_timeout=r.tuning.maxBatchTimeout),r.tuning.maxRetries!==void 0&&(m.max_retries=r.tuning.maxRetries),r.tuning.deadLetterQueue!==void 0&&(m.dead_letter_queue=r.tuning.deadLetterQueue),r.tuning.retryDelay!==void 0&&(m.retry_delay=r.tuning.retryDelay),m})],p=b(e,["queues"],{consumers:u,producers:h});return{added:[...g.map(r=>`queues.producers/${r.bindingName}`),...l.map(r=>`queues.consumers/${r.name}`)],text:p}},ae=(e,a)=>{const n=B(e),s=C(a);if(!n)return{added:[],changed:!1,exportGaps:s,reason:"wrangler.jsonc not found",warnings:_(a,e)};const{parsed:i,text:o}=O(n);if(i===void 0)return{added:[],changed:!1,exportGaps:s,reason:`failed to parse ${n} as JSONC`,warnings:_(a,e),wranglerPath:n};const c=_(a,e,i),d=a.containers.filter(t=>t.exported),g=a.agents.filter(t=>t.exported&&t.voice===!0&&t.voiceBindingName!==void 0&&t.voiceClassName!==void 0),l=[...a.durableObjects,...d.map(t=>({binding:t.bindingName,className:t.className})),...g.map(t=>({binding:t.voiceBindingName,className:t.voiceClassName}))],h=a.workflows.filter(t=>t.exported),u=a.agents.filter(t=>t.exported),p=[{enabled:!0,run:t=>M(t,i,l)},{enabled:a.needsD1,run:t=>K(t,i)},{enabled:a.usesAi,run:t=>x(t,i,"ai","AI","AI (Workers AI)")},{enabled:a.usesBrowser,run:t=>x(t,i,"browser","BROWSER","BROWSER (Browser Rendering)")},{enabled:a.usesImages,run:t=>x(t,i,"images","IMAGES","IMAGES (Cloudflare Images)")},{enabled:a.usesAnalytics,run:t=>q(t,i)},{enabled:!0,run:t=>G(t,i)},{enabled:d.length>0,run:t=>j(t,i,d)},{enabled:h.length>0||u.length>0,run:t=>F(t,i,h,u)},{enabled:a.queues.length>0,run:t=>V(t,i,a.queues)}];let r=o;const m=[];for(const t of p){if(!t.enabled)continue;const y=t.run(r);r=y.text,m.push(...y.added)}return m.includes("DB (D1)")&&c.push(`wrote a DB binding with a placeholder database_id ("${v}") — run \`wrangler d1 create <name>\` and replace it before deploying.`),r===o?{added:[],changed:!1,exportGaps:s,reason:"bindings already in sync",warnings:c,wranglerPath:n}:(N(n,r,"utf8"),{added:m,changed:!0,exportGaps:s,warnings:c,wranglerPath:n})};export{ae as reconcileWranglerBindings};
@@ -1 +0,0 @@
1
- const u=async e=>await new Promise((t,r)=>{const o=[];let a=0;e.on("data",s=>{if(a+=s.length,a>1e6){r(new Error("request body too large"));return}o.push(s)}),e.on("end",()=>{t(Buffer.concat(o).toString("utf8"))}),e.on("error",r)}),i=(e,t,r)=>{e.statusCode=t,e.setHeader("Content-Type","application/json; charset=utf-8"),e.end(JSON.stringify(r))},n=e=>{const t=Array.isArray(e)?e[0]:e;return typeof t=="string"?t.trim().toLowerCase():void 0},f=new Set(["none","same-origin","same-site"]),l=e=>{const t=n(e.headers["sec-fetch-site"]);if(t!==void 0)return f.has(t)?void 0:"cross-origin request rejected";const r=n(e.headers.origin);if(r===void 0||r==="null")return;let o;try{o=new URL(r).host.toLowerCase()}catch{return"invalid origin header"}return o===n(e.headers.host)?void 0:"cross-origin request rejected"},p=e=>{const t=(e.method??"GET").toUpperCase(),r=t!=="GET"&&t!=="HEAD",o=l(e);if(o!==void 0)return o;if(r&&!n(e.headers["content-type"])?.startsWith("application/json"))return"content-type must be application/json"},y=(e,t,r,o,a)=>{(async()=>{try{const s=p(e);if(s!==void 0){i(t,403,{error:s,ok:!1});return}const c=e.method==="GET"?"":await u(e);let d;try{d=c===""?void 0:JSON.parse(c)}catch{i(t,400,{error:"invalid-json",ok:!1});return}const h=r({body:d,method:e.method??"POST",projectRoot:o,schemaDirectory:a});i(t,h.status,h.body)}catch(s){i(t,500,{error:s instanceof Error?s.message:String(s),ok:!1})}})().catch(()=>{})};export{y as serveJsonHandler};