@smoothbricks/cli 0.11.18 → 0.11.20

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 (96) hide show
  1. package/README.md +91 -11
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +28 -4
  4. package/dist/github-ci/index.d.ts.map +1 -1
  5. package/dist/github-ci/index.js +6 -20
  6. package/dist/lib/deploy-tags.d.ts +17 -3
  7. package/dist/lib/deploy-tags.d.ts.map +1 -1
  8. package/dist/lib/deploy-tags.js +23 -4
  9. package/dist/lib/json.d.ts +22 -1
  10. package/dist/lib/json.d.ts.map +1 -1
  11. package/dist/lib/json.js +15 -3
  12. package/dist/monorepo/cargo-policy.d.ts +17 -0
  13. package/dist/monorepo/cargo-policy.d.ts.map +1 -1
  14. package/dist/monorepo/cargo-policy.js +73 -1
  15. package/dist/monorepo/index.d.ts.map +1 -1
  16. package/dist/monorepo/index.js +4 -2
  17. package/dist/monorepo/managed-files.d.ts +5 -2
  18. package/dist/monorepo/managed-files.d.ts.map +1 -1
  19. package/dist/monorepo/managed-files.js +35 -21
  20. package/dist/monorepo/packs/index.d.ts.map +1 -1
  21. package/dist/monorepo/packs/index.js +6 -1
  22. package/dist/nx/index.d.ts +6 -0
  23. package/dist/nx/index.d.ts.map +1 -1
  24. package/dist/nx/index.js +14 -0
  25. package/dist/secrets/commands.d.ts +9 -5
  26. package/dist/secrets/commands.d.ts.map +1 -1
  27. package/dist/secrets/commands.js +122 -49
  28. package/dist/secrets/index.d.ts +42 -56
  29. package/dist/secrets/index.d.ts.map +1 -1
  30. package/dist/secrets/index.js +52 -79
  31. package/dist/secrets/resolver.d.ts +59 -0
  32. package/dist/secrets/resolver.d.ts.map +1 -0
  33. package/dist/secrets/resolver.js +100 -0
  34. package/dist/secrets/run.d.ts +23 -0
  35. package/dist/secrets/run.d.ts.map +1 -0
  36. package/dist/secrets/run.js +130 -0
  37. package/dist/secrets/status.d.ts +177 -0
  38. package/dist/secrets/status.d.ts.map +1 -0
  39. package/dist/secrets/status.js +724 -0
  40. package/dist/wrangler/deploy-stage.d.ts +1 -1
  41. package/dist/wrangler/deploy-stage.d.ts.map +1 -1
  42. package/dist/wrangler/deploy-stage.js +22 -20
  43. package/dist/wrangler/deployed-version.d.ts.map +1 -1
  44. package/dist/wrangler/deployed-version.js +7 -12
  45. package/dist/wrangler/flat-config.d.ts +1 -4
  46. package/dist/wrangler/flat-config.d.ts.map +1 -1
  47. package/dist/wrangler/flat-config.js +98 -44
  48. package/dist/wrangler/prepare-env.d.ts +4 -3
  49. package/dist/wrangler/prepare-env.d.ts.map +1 -1
  50. package/dist/wrangler/prepare-env.js +7 -5
  51. package/dist/wrangler/source-config.d.ts +24 -0
  52. package/dist/wrangler/source-config.d.ts.map +1 -0
  53. package/dist/wrangler/source-config.js +110 -0
  54. package/dist/wrangler/stage.d.ts +55 -23
  55. package/dist/wrangler/stage.d.ts.map +1 -1
  56. package/dist/wrangler/stage.js +81 -162
  57. package/managed/raw/tooling/direnv/devenv.smoo.nix +19 -3
  58. package/managed/raw/tooling/direnv/secret-references.ts +280 -76
  59. package/managed/raw/tooling/direnv/setup-environment.ts +58 -2
  60. package/managed/raw/tsconfig.lib.json +28 -0
  61. package/package.json +7 -2
  62. package/src/cli.ts +34 -5
  63. package/src/github-ci/index.test.ts +6 -7
  64. package/src/github-ci/index.ts +11 -19
  65. package/src/lib/deploy-tags.ts +22 -4
  66. package/src/lib/json.ts +23 -1
  67. package/src/monorepo/cargo-policy.test.ts +91 -1
  68. package/src/monorepo/cargo-policy.ts +87 -1
  69. package/src/monorepo/index.ts +8 -2
  70. package/src/monorepo/managed-files.test.ts +69 -37
  71. package/src/monorepo/managed-files.ts +38 -21
  72. package/src/monorepo/packs/index.ts +10 -1
  73. package/src/monorepo/secret-references.test.ts +412 -6
  74. package/src/monorepo/setup-environment.test.ts +181 -0
  75. package/src/nx/index.test.ts +8 -0
  76. package/src/nx/index.ts +20 -0
  77. package/src/secrets/commands.test.ts +186 -4
  78. package/src/secrets/commands.ts +142 -51
  79. package/src/secrets/index.test.ts +4 -10
  80. package/src/secrets/index.ts +54 -115
  81. package/src/secrets/resolver.ts +130 -0
  82. package/src/secrets/run.test.ts +359 -0
  83. package/src/secrets/run.ts +148 -0
  84. package/src/secrets/status.test.ts +164 -0
  85. package/src/secrets/status.ts +297 -0
  86. package/src/wrangler/deploy-stage.test.ts +95 -2
  87. package/src/wrangler/deploy-stage.ts +26 -23
  88. package/src/wrangler/deployed-version.ts +7 -11
  89. package/src/wrangler/flat-config.test.ts +9 -4
  90. package/src/wrangler/flat-config.ts +1 -20
  91. package/src/wrangler/format-parity.test.ts +433 -0
  92. package/src/wrangler/prepare-env.ts +7 -5
  93. package/src/wrangler/source-config.test.ts +102 -0
  94. package/src/wrangler/source-config.ts +110 -0
  95. package/src/wrangler/stage.test.ts +61 -32
  96. package/src/wrangler/stage.ts +124 -173
@@ -0,0 +1,110 @@
1
+ // Where a project's Wrangler configuration lives, and how its file formats become one model.
2
+ //
3
+ // Wrangler reads whichever of `wrangler.jsonc`, `wrangler.json` and `wrangler.toml` it finds
4
+ // first and says nothing about the rest: with a `.jsonc` and a `.toml` side by side, wrangler
5
+ // 4.131.0 deploys the `.jsonc` silently, whichever of the two the repo is actually editing.
6
+ // Discovery here follows the same precedence but refuses the pick, naming every file it found.
7
+ //
8
+ // Both JSON spellings go through the JSONC parser because wrangler does — comments and trailing
9
+ // commas parse in `wrangler.json` too (verified against wrangler 4.131.0) — so the format only
10
+ // ever decides which parser runs. Past this module there is one model and no config text.
11
+ import { existsSync, readFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import { getStaticTOMLValue, parseTOML } from 'toml-eslint-parser';
14
+ import typia from 'typia';
15
+ /** Wrangler's own precedence: the first of these that exists is the one it reads. */
16
+ export const WRANGLER_SOURCE_CONFIG_FILES = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml'];
17
+ const isWranglerDocument = (() => {
18
+ const _io0 = input => (undefined === input.env || "object" === typeof input.env && null !== input.env && false === Array.isArray(input.env) && _io1(input.env)) && Object.keys(input).every(key => {
19
+ if (["env"].some(prop => key === prop))
20
+ return true;
21
+ const value = input[key];
22
+ if (undefined === value)
23
+ return true;
24
+ return true;
25
+ });
26
+ const _io1 = input => Object.keys(input).every(key => {
27
+ const value = input[key];
28
+ if (undefined === value)
29
+ return true;
30
+ return undefined === value || "object" === typeof value && null !== value && false === Array.isArray(value) && _io2(value);
31
+ });
32
+ const _io2 = input => Object.keys(input).every(key => {
33
+ const value = input[key];
34
+ if (undefined === value)
35
+ return true;
36
+ return true;
37
+ });
38
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
39
+ })();
40
+ /**
41
+ * The one source config this project declares. Two configs is a refusal, not a precedence
42
+ * exercise: the repo would deploy whichever file wrangler prefers, which is not necessarily the
43
+ * one anybody is editing.
44
+ */
45
+ export function discoverWranglerSourceConfig(cwd) {
46
+ const found = WRANGLER_SOURCE_CONFIG_FILES.filter((file) => existsSync(join(cwd, file)));
47
+ const [first] = found;
48
+ if (first === undefined) {
49
+ throw new Error(`${cwd} declares no Wrangler configuration (${WRANGLER_SOURCE_CONFIG_FILES.join(', ')}); pass --config for a build-generated flat configuration.`);
50
+ }
51
+ if (found.length > 1) {
52
+ throw new Error(`${cwd} declares more than one Wrangler configuration: ${found.join(', ')}. Wrangler would read ${first} and ignore the rest without saying so; keep exactly one.`);
53
+ }
54
+ return { path: join(cwd, first), format: first === 'wrangler.toml' ? 'toml' : 'jsonc' };
55
+ }
56
+ /** The discovered source config, parsed into the model every stage derivation reads. */
57
+ export function readWranglerSourceConfig(cwd) {
58
+ const { path, format } = discoverWranglerSourceConfig(cwd);
59
+ return { path, document: parseWranglerConfig(path, readFileSync(path, 'utf8'), format) };
60
+ }
61
+ /** One document out of either format. The text is never handed on: everything downstream reads data. */
62
+ export function parseWranglerConfig(path, text, format) {
63
+ const value = parseConfigText(path, text, format);
64
+ // TOML carries values JSON does not, and the temporary per-stage config is JSON whatever the
65
+ // source format was. Name them here rather than let JSON.stringify shift a date by its timezone
66
+ // or write `null` for an infinity halfway through a deploy.
67
+ if (format === 'toml')
68
+ assertJsonRepresentable(value, '', path);
69
+ if (!isWranglerDocument(value)) {
70
+ throw new Error(`${path} is not a Wrangler configuration document: expected an object, optionally with env blocks.`);
71
+ }
72
+ return value;
73
+ }
74
+ function parseConfigText(path, text, format) {
75
+ try {
76
+ return format === 'toml' ? getStaticTOMLValue(parseTOML(text)) : Bun.JSONC.parse(text);
77
+ }
78
+ catch (error) {
79
+ const detail = error instanceof Error ? error.message : String(error);
80
+ throw new Error(`${path} is not valid ${format === 'toml' ? 'TOML' : 'JSONC'}: ${detail}`);
81
+ }
82
+ }
83
+ function assertJsonRepresentable(value, path, file) {
84
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
85
+ return;
86
+ if (typeof value === 'number') {
87
+ if (Number.isFinite(value))
88
+ return;
89
+ throw new Error(`${file} declares ${value} at ${path}, which JSON cannot carry; write a finite number.`);
90
+ }
91
+ if (Array.isArray(value)) {
92
+ for (const [index, item] of value.entries()) {
93
+ assertJsonRepresentable(item, `${path}[${index}]`, file);
94
+ }
95
+ return;
96
+ }
97
+ if (isPlainObject(value)) {
98
+ for (const [key, item] of Object.entries(value)) {
99
+ assertJsonRepresentable(item, path ? `${path}.${key}` : key, file);
100
+ }
101
+ return;
102
+ }
103
+ throw new Error(`${file} declares a bare TOML ${value instanceof Date ? 'date or time' : typeof value} at ${path}, which JSON cannot carry; quote it as a string.`);
104
+ }
105
+ function isPlainObject(value) {
106
+ if (typeof value !== 'object' || value === null)
107
+ return false;
108
+ const prototype = Object.getPrototypeOf(value);
109
+ return prototype === Object.prototype || prototype === null;
110
+ }
@@ -5,6 +5,23 @@ export declare function isPullRequestStage(stage: DeploymentStage): stage is `pr
5
5
  export declare function stageDomain(stage: string, zone: string): string;
6
6
  export declare function stageResourceName(base: string, stage: string): string;
7
7
  export declare function hasExactStageSegment(value: string, stage: `pr${number}`): boolean;
8
+ /**
9
+ * A whole Wrangler configuration as data — the one model both source formats parse into
10
+ * (`source-config.ts` owns the parsers). Nothing in this module reads configuration text, which
11
+ * is what makes a stage derive identically from `wrangler.toml` and from `wrangler.jsonc`.
12
+ */
13
+ export interface WranglerDocument {
14
+ env?: Record<string, WranglerEnvironment | undefined>;
15
+ [key: string]: unknown;
16
+ }
17
+ /**
18
+ * One `env` block, still unparsed. `StageConfigFields` is the typed statement of what this
19
+ * module reads out of it; every other key is carried into a derived stage, never interpreted.
20
+ */
21
+ export interface WranglerEnvironment {
22
+ [key: string]: unknown;
23
+ }
24
+ /** One reconciled binding of the plan: exactly what Cloudflare is asked about, and nothing else. */
8
25
  export interface KvBinding {
9
26
  binding: string;
10
27
  id: string;
@@ -13,44 +30,52 @@ export interface R2Binding {
13
30
  binding: string;
14
31
  bucketName: string;
15
32
  }
16
- /** One-stage bindings that a TOML env block and a flat JSON config both present. */
17
- export interface StageConfigFields {
18
- name: string;
19
- routes?: StageRoute[];
20
- vars?: Record<string, unknown>;
21
- kv_namespaces?: KvBinding[];
22
- r2_buckets?: {
23
- binding: string;
24
- bucket_name: string;
25
- }[];
26
- d1_databases?: StageD1Binding[];
27
- services?: StageServiceBinding[];
28
- ratelimits?: StageRateLimit[];
29
- send_email?: Array<{
30
- allowed_sender_addresses?: unknown;
31
- [key: string]: unknown;
32
- }>;
33
- }
34
33
  export interface StageRoute {
35
34
  pattern: string;
36
35
  zone_name?: string;
37
36
  custom_domain?: boolean;
37
+ [key: string]: unknown;
38
+ }
39
+ export interface StageKvBinding {
40
+ binding: string;
41
+ id: string;
42
+ [key: string]: unknown;
43
+ }
44
+ export interface StageR2Binding {
45
+ binding: string;
46
+ bucket_name: string;
47
+ [key: string]: unknown;
38
48
  }
39
49
  export interface StageD1Binding {
40
50
  binding: string;
41
51
  database_name: string;
42
52
  database_id: string;
43
53
  migrations_dir?: string;
54
+ [key: string]: unknown;
44
55
  }
45
56
  export interface StageServiceBinding {
46
57
  binding: string;
47
58
  service: string;
48
59
  environment?: string;
60
+ [key: string]: unknown;
49
61
  }
50
62
  export interface StageRateLimit {
51
63
  name: string;
52
64
  namespace_id: string;
53
- simple?: unknown;
65
+ [key: string]: unknown;
66
+ }
67
+ /** One-stage bindings that an env block and a flat JSON config both present. */
68
+ export interface StageConfigFields {
69
+ name: string;
70
+ routes?: StageRoute[];
71
+ vars?: Record<string, unknown>;
72
+ kv_namespaces?: StageKvBinding[];
73
+ r2_buckets?: StageR2Binding[];
74
+ d1_databases?: StageD1Binding[];
75
+ services?: StageServiceBinding[];
76
+ ratelimits?: StageRateLimit[];
77
+ send_email?: Record<string, unknown>[];
78
+ [key: string]: unknown;
54
79
  }
55
80
  export interface LiveKvNamespace {
56
81
  id: string;
@@ -91,7 +116,9 @@ export interface ConfiguredStageResourcePlan {
91
116
  customDomain: boolean;
92
117
  }>;
93
118
  }
94
- export declare function planConfiguredStageResources(toml: string, stage: DeploymentStage): ConfiguredStageResourcePlan;
119
+ /** The reconcile plan (`reconcileStageResources`) read off one resolved stage's bindings. */
120
+ export declare function planStageResources(config: StageConfigFields, stage: DeploymentStage): ConfiguredStageResourcePlan;
121
+ export declare function planConfiguredStageResources(document: WranglerDocument, stage: DeploymentStage): ConfiguredStageResourcePlan;
95
122
  /** The staging worker name without its required `-staging` suffix. */
96
123
  export declare function stagingWorkerBaseName(name: string): string;
97
124
  /**
@@ -99,8 +126,8 @@ export declare function stagingWorkerBaseName(name: string): string;
99
126
  * pull-request stage: it would deploy unrouted and Wrangler would expose it on workers.dev.
100
127
  */
101
128
  export declare function assertPullRequestRoutable(name: string, routes: StageRoute[] | undefined): void;
102
- export declare function planPullRequestResources(toml: string, stage: `pr${number}`, liveNamespaces: LiveKvNamespace[]): PullRequestResourcePlan;
103
- /** KV/R2/D1 isolation and route checks for a pull-request stage, shared by TOML and JSON. */
129
+ export declare function planPullRequestResources(document: WranglerDocument, stage: `pr${number}`, liveNamespaces: LiveKvNamespace[]): PullRequestResourcePlan;
130
+ /** KV/R2/D1 isolation and route checks for a pull-request stage, shared by every source format. */
104
131
  export declare function planPullRequestBindings(config: StageConfigFields, stage: `pr${number}`, liveNamespaces: LiveKvNamespace[]): PullRequestResourcePlan;
105
132
  export interface DerivePullRequestConfigOptions {
106
133
  stage: `pr${number}`;
@@ -110,6 +137,11 @@ export interface DerivePullRequestConfigOptions {
110
137
  }
111
138
  /** Staging bindings rewritten for a pull-request stage; keys this module does not model are left to the caller. */
112
139
  export declare function derivePullRequestStageConfig<T extends StageConfigFields>(config: T, options: DerivePullRequestConfigOptions): T;
113
- export declare function derivePullRequestWranglerConfig(toml: string, options: DerivePullRequestConfigOptions): string;
140
+ /**
141
+ * The whole document with `env.<prN>` added, derived from `env.staging`. Nothing read from disk
142
+ * is mutated — every level the derivation touches is rebuilt — so the committed source config is
143
+ * still exactly what the repo wrote once the deploy is done with it.
144
+ */
145
+ export declare function derivePullRequestDocument(document: WranglerDocument, options: DerivePullRequestConfigOptions): WranglerDocument;
114
146
  export declare function rateLimitNamespaceId(accountId: string, workerBaseName: string, stage: string, bindingName: string): string;
115
147
  //# sourceMappingURL=stage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/wrangler/stage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/wrangler/stage.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { getStaticTOMLValue, parseTOML } from 'toml-eslint-parser';
3
2
  import typia from 'typia';
4
- import { cloneEnvBlock } from './prepare-env.js';
5
3
  import { derivedStagingName, hasStageLabel, replaceExactToken, replaceHostnameLabel } from './stage-labels.js';
6
4
  const MAX_PULL_REQUEST_NUMBER = 999_999_999;
7
5
  const STAGE_PATTERN = /^(?:staging|production|pr[1-9][0-9]{0,8})$/;
@@ -42,17 +40,8 @@ export function hasExactStageSegment(value, stage) {
42
40
  const escaped = stage.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
41
  return new RegExp(`(?:^|[-.])${escaped}(?=$|[-.])`).test(value);
44
42
  }
45
- const isWranglerRoot = (() => {
46
- const _io0 = input => undefined === input.env || "object" === typeof input.env && null !== input.env && false === Array.isArray(input.env) && _io1(input.env);
47
- const _io1 = input => Object.keys(input).every(key => {
48
- const value = input[key];
49
- if (undefined === value)
50
- return true;
51
- return undefined === value || "object" === typeof value && null !== value && false === Array.isArray(value) && _io2(value);
52
- });
53
- const _io2 = input => true && true && true && true && true && true && true && true && true && Object.keys(input).every(key => {
54
- if (["name", "routes", "kv_namespaces", "r2_buckets", "d1_databases", "services", "ratelimits", "vars", "send_email"].some(prop => key === prop))
55
- return true;
43
+ const isWranglerEnvironment = (() => {
44
+ const _io0 = input => Object.keys(input).every(key => {
56
45
  const value = input[key];
57
46
  if (undefined === value)
58
47
  return true;
@@ -60,10 +49,8 @@ const isWranglerRoot = (() => {
60
49
  });
61
50
  return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
62
51
  })();
63
- const isWranglerEnvironment = (() => {
64
- const _io0 = input => true && true && true && true && true && true && true && true && true && Object.keys(input).every(key => {
65
- if (["name", "routes", "kv_namespaces", "r2_buckets", "d1_databases", "services", "ratelimits", "vars", "send_email"].some(prop => key === prop))
66
- return true;
52
+ const isUnknownRecord = (() => {
53
+ const _io0 = input => Object.keys(input).every(key => {
67
54
  const value = input[key];
68
55
  if (undefined === value)
69
56
  return true;
@@ -71,51 +58,73 @@ const isWranglerEnvironment = (() => {
71
58
  });
72
59
  return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
73
60
  })();
74
- const isUnknownRecord = (() => {
61
+ const isUnknownRows = (() => {
75
62
  const _io0 = input => Object.keys(input).every(key => {
76
63
  const value = input[key];
77
64
  if (undefined === value)
78
65
  return true;
79
66
  return true;
80
67
  });
81
- return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
68
+ return input => Array.isArray(input) && input.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io0(elem));
82
69
  })();
83
- const isUnknownRows = (() => {
70
+ const isRouteRows = (() => {
84
71
  const _io0 = input => Object.keys(input).every(key => {
85
72
  const value = input[key];
86
73
  if (undefined === value)
87
74
  return true;
88
75
  return true;
89
76
  });
90
- return input => Array.isArray(input) && input.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io0(elem));
77
+ return input => Array.isArray(input) && input.every(elem => null !== elem && undefined !== elem && ("string" === typeof elem || "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io0(elem)));
91
78
  })();
92
- export function planConfiguredStageResources(toml, stage) {
93
- const block = parseRoot(toml).env?.[stage];
94
- if (!isWranglerEnvironment(block)) {
95
- throw new Error(`Wrangler configuration must declare [env.${stage}].`);
96
- }
97
- const workerName = requiredString(block, 'name', `[env.${stage}]`);
79
+ /** The reconcile plan (`reconcileStageResources`) read off one resolved stage's bindings. */
80
+ export function planStageResources(config, stage) {
98
81
  return {
99
82
  stage,
100
- workerName,
101
- kvNamespaces: readKvBindings(block.kv_namespaces),
102
- r2Buckets: readR2Bindings(block.r2_buckets),
103
- routes: readRoutes(block.routes),
83
+ workerName: config.name,
84
+ kvNamespaces: (config.kv_namespaces ?? []).map(({ binding, id }) => ({ binding, id })),
85
+ r2Buckets: (config.r2_buckets ?? []).map((bucket) => ({ binding: bucket.binding, bucketName: bucket.bucket_name })),
86
+ routes: (config.routes ?? []).map((route) => ({
87
+ pattern: route.pattern,
88
+ ...(typeof route.zone_name === 'string' ? { zoneName: route.zone_name } : {}),
89
+ customDomain: route.custom_domain === true,
90
+ })),
104
91
  };
105
92
  }
106
- function parseRoot(toml) {
107
- const value = getStaticTOMLValue(parseTOML(toml));
108
- if (!isWranglerRoot(value)) {
109
- throw new Error('Wrangler configuration is not a valid TOML environment document.');
110
- }
111
- return value;
93
+ export function planConfiguredStageResources(document, stage) {
94
+ return planStageResources(stageEnvironmentConfig(document, stage), stage);
112
95
  }
113
- function stagingEnvironment(toml) {
114
- const staging = parseRoot(toml).env?.staging;
115
- if (!isWranglerEnvironment(staging)) {
116
- throw new Error('Wrangler configuration must declare [env.staging].');
96
+ /** The `env.<stage>` block as the stage model, or the refusal that it is not declared. */
97
+ function stageEnvironmentConfig(document, stage) {
98
+ const block = document.env?.[stage];
99
+ if (!isWranglerEnvironment(block)) {
100
+ throw new Error(`Wrangler configuration must declare [env.${stage}].`);
117
101
  }
118
- return staging;
102
+ return environmentStageConfig(block, `[env.${stage}]`);
103
+ }
104
+ /**
105
+ * One env block as the stage model: every field the derivation reads proven present and typed,
106
+ * every other key carried verbatim. A field absent from the source stays absent, so the same
107
+ * worker written as TOML and as JSONC produces the same model down to the key set.
108
+ */
109
+ function environmentStageConfig(environment, label) {
110
+ const config = { ...environment, name: requiredString(environment, 'name', label) };
111
+ if (environment.routes !== undefined)
112
+ config.routes = readStageRoutes(environment.routes);
113
+ if (isUnknownRecord(environment.vars))
114
+ config.vars = environment.vars;
115
+ if (environment.kv_namespaces !== undefined)
116
+ config.kv_namespaces = readKvBindings(environment.kv_namespaces);
117
+ if (environment.r2_buckets !== undefined)
118
+ config.r2_buckets = readR2Buckets(environment.r2_buckets);
119
+ if (environment.d1_databases !== undefined)
120
+ config.d1_databases = readD1Bindings(environment.d1_databases);
121
+ if (environment.services !== undefined)
122
+ config.services = readServices(environment.services);
123
+ if (environment.ratelimits !== undefined)
124
+ config.ratelimits = readRateLimits(environment.ratelimits);
125
+ if (environment.send_email !== undefined)
126
+ config.send_email = readRows(environment.send_email);
127
+ return config;
119
128
  }
120
129
  /** The staging worker name without its required `-staging` suffix. */
121
130
  export function stagingWorkerBaseName(name) {
@@ -124,12 +133,6 @@ export function stagingWorkerBaseName(name) {
124
133
  }
125
134
  return name.slice(0, -'-staging'.length);
126
135
  }
127
- function stagingWorkerName(staging) {
128
- if (typeof staging.name !== 'string' || !staging.name) {
129
- throw new Error('[env.staging] must declare a non-empty name.');
130
- }
131
- return { workerName: staging.name, workerBaseName: stagingWorkerBaseName(staging.name) };
132
- }
133
136
  /**
134
137
  * A template whose routes are all pinned (no derivable stage label) cannot serve a
135
138
  * pull-request stage: it would deploy unrouted and Wrangler would expose it on workers.dev.
@@ -141,28 +144,10 @@ export function assertPullRequestRoutable(name, routes) {
141
144
  const patterns = list.map((route) => route.pattern).join(', ');
142
145
  throw new Error(`Every route of ${name} is pinned (no staging label): ${patterns}. A pull-request stage would deploy unrouted and Wrangler would expose it on workers.dev; add a stage-derivable route such as site.staging.<zone>/*.`);
143
146
  }
144
- function stagingStageConfig(toml) {
145
- const staging = stagingEnvironment(toml);
146
- const { workerName } = stagingWorkerName(staging);
147
- return {
148
- name: workerName,
149
- routes: readStageRoutes(staging.routes),
150
- ...(isUnknownRecord(staging.vars) ? { vars: staging.vars } : {}),
151
- kv_namespaces: readKvBindings(staging.kv_namespaces),
152
- r2_buckets: readRows(staging.r2_buckets).map((row) => {
153
- const binding = requiredString(row, 'binding', 'R2 binding');
154
- return { binding, bucket_name: requiredString(row, 'bucket_name', `R2 binding ${binding}`) };
155
- }),
156
- d1_databases: readD1Bindings(staging.d1_databases),
157
- services: readServices(staging.services),
158
- ratelimits: readRateLimits(staging.ratelimits),
159
- send_email: readRows(staging.send_email),
160
- };
161
- }
162
- export function planPullRequestResources(toml, stage, liveNamespaces) {
163
- return planPullRequestBindings(stagingStageConfig(toml), stage, liveNamespaces);
147
+ export function planPullRequestResources(document, stage, liveNamespaces) {
148
+ return planPullRequestBindings(stageEnvironmentConfig(document, 'staging'), stage, liveNamespaces);
164
149
  }
165
- /** KV/R2/D1 isolation and route checks for a pull-request stage, shared by TOML and JSON. */
150
+ /** KV/R2/D1 isolation and route checks for a pull-request stage, shared by every source format. */
166
151
  export function planPullRequestBindings(config, stage, liveNamespaces) {
167
152
  parseDeploymentStage(stage);
168
153
  assertPullRequestRoutable(config.name, config.routes);
@@ -328,48 +313,17 @@ function requiredDerivedString(value) {
328
313
  }
329
314
  return value;
330
315
  }
331
- export function derivePullRequestWranglerConfig(toml, options) {
332
- const ctx = deriveContext(stagingWorkerName(stagingEnvironment(toml)).workerName, options);
333
- const cloned = cloneEnvBlock(toml, 'staging', ctx.stage);
334
- const program = parseTOML(cloned);
335
- const rootValue = getStaticTOMLValue(program);
336
- if (!isWranglerRoot(rootValue)) {
337
- throw new Error('Derived Wrangler configuration is not a valid environment document.');
338
- }
339
- const root = rootValue;
340
- const edits = [];
341
- for (const table of program.body[0].body) {
342
- if (table.type !== 'TOMLTable' || table.resolvedKey[0] !== 'env' || table.resolvedKey[1] !== ctx.stage) {
343
- continue;
344
- }
345
- const tableValue = valueAtPath(root, table.resolvedKey);
346
- if (!isUnknownRecord(tableValue)) {
347
- continue;
348
- }
349
- const section = typeof table.resolvedKey[2] === 'string' ? table.resolvedKey[2] : undefined;
350
- if (section === 'routes') {
351
- const pattern = tableValue.pattern;
352
- if (typeof pattern === 'string' && !hasStageLabel(pattern)) {
353
- edits.push({ start: table.range[0], end: table.range[1], value: '' });
354
- continue;
355
- }
356
- }
357
- const nearbyName = typeof tableValue.name === 'string' ? tableValue.name : undefined;
358
- for (const keyValue of table.body) {
359
- const key = cloned.slice(keyValue.key.range[0], keyValue.key.range[1]).trim();
360
- const current = tableValue[key];
361
- const next = deriveStageField(section, key, current, ctx, nearbyName);
362
- if (next !== current) {
363
- edits.push({ start: keyValue.value.range[0], end: keyValue.value.range[1], value: tomlLiteral(next) });
364
- }
365
- }
366
- }
367
- let derived = cloned;
368
- for (const edit of edits.sort((left, right) => right.start - left.start)) {
369
- derived = derived.slice(0, edit.start) + edit.value + derived.slice(edit.end);
316
+ /**
317
+ * The whole document with `env.<prN>` added, derived from `env.staging`. Nothing read from disk
318
+ * is mutated every level the derivation touches is rebuilt — so the committed source config is
319
+ * still exactly what the repo wrote once the deploy is done with it.
320
+ */
321
+ export function derivePullRequestDocument(document, options) {
322
+ if (document.env?.[options.stage] !== undefined) {
323
+ throw new Error(`Wrangler configuration already declares [env.${options.stage}]; a pull-request stage is derived from [env.staging] and must not be committed.`);
370
324
  }
371
- parseTOML(derived);
372
- return derived;
325
+ const derived = derivePullRequestStageConfig(stageEnvironmentConfig(document, 'staging'), options);
326
+ return { ...document, env: { ...document.env, [options.stage]: derived } };
373
327
  }
374
328
  export function rateLimitNamespaceId(accountId, workerBaseName, stage, bindingName) {
375
329
  const token = parseDeploymentStage(stage);
@@ -377,63 +331,52 @@ export function rateLimitNamespaceId(accountId, workerBaseName, stage, bindingNa
377
331
  const value = digest.readUInt32BE(0) & 0x7fff_ffff;
378
332
  return String(value === 0 ? 1 : value);
379
333
  }
334
+ /**
335
+ * Wrangler accepts a route as an object or as a bare pattern string. Both become the object form
336
+ * here, so one derivation rewrites the hostname label either way — a string route used to be
337
+ * carried into the pull-request stage verbatim, pointing it at staging's own hostname.
338
+ */
339
+ function readStageRoutes(value) {
340
+ if (!isRouteRows(value))
341
+ return [];
342
+ return value.map((row) => typeof row === 'string' ? { pattern: row } : { ...row, pattern: requiredString(row, 'pattern', 'route') });
343
+ }
380
344
  function readKvBindings(value) {
381
345
  return readRows(value).map((row) => ({
346
+ ...row,
382
347
  binding: requiredString(row, 'binding', 'KV namespace'),
383
348
  id: requiredString(row, 'id', 'KV namespace'),
384
349
  }));
385
350
  }
386
- function readR2Bindings(value) {
351
+ function readR2Buckets(value) {
387
352
  return readRows(value).map((row) => {
388
353
  const binding = requiredString(row, 'binding', 'R2 binding');
389
- return { binding, bucketName: requiredString(row, 'bucket_name', `R2 binding ${binding}`) };
354
+ return { ...row, binding, bucket_name: requiredString(row, 'bucket_name', `R2 binding ${binding}`) };
390
355
  });
391
356
  }
392
357
  function readD1Bindings(value) {
393
358
  return readRows(value).map((row) => {
394
359
  const binding = requiredString(row, 'binding', 'D1 database');
395
- const database = {
360
+ return {
361
+ ...row,
396
362
  binding,
397
363
  database_name: requiredString(row, 'database_name', `D1 binding ${binding}`),
398
364
  database_id: requiredString(row, 'database_id', `D1 binding ${binding}`),
399
365
  };
400
- return typeof row.migrations_dir === 'string' ? { ...database, migrations_dir: row.migrations_dir } : database;
401
366
  });
402
367
  }
403
368
  function readServices(value) {
404
369
  return readRows(value).map((row) => {
405
370
  const binding = requiredString(row, 'binding', 'Service binding');
406
- const service = {
407
- binding,
408
- service: requiredString(row, 'service', `Service binding ${binding}`),
409
- };
410
- return typeof row.environment === 'string' ? { ...service, environment: row.environment } : service;
371
+ return { ...row, binding, service: requiredString(row, 'service', `Service binding ${binding}`) };
411
372
  });
412
373
  }
413
374
  function readRateLimits(value) {
414
375
  return readRows(value).map((row) => {
415
376
  const name = requiredString(row, 'name', 'Rate-limit binding');
416
- const limit = {
417
- name,
418
- namespace_id: requiredString(row, 'namespace_id', `Rate-limit binding ${name}`),
419
- };
420
- return row.simple === undefined ? limit : { ...limit, simple: row.simple };
377
+ return { ...row, name, namespace_id: requiredString(row, 'namespace_id', `Rate-limit binding ${name}`) };
421
378
  });
422
379
  }
423
- function readStageRoutes(value) {
424
- return readRows(value).map((row) => ({
425
- pattern: requiredString(row, 'pattern', 'route'),
426
- ...(typeof row.zone_name === 'string' ? { zone_name: row.zone_name } : {}),
427
- ...(row.custom_domain === true ? { custom_domain: true } : {}),
428
- }));
429
- }
430
- function readRoutes(value) {
431
- return readRows(value).map((row) => ({
432
- pattern: requiredString(row, 'pattern', 'route'),
433
- ...(typeof row.zone_name === 'string' ? { zoneName: row.zone_name } : {}),
434
- customDomain: row.custom_domain === true,
435
- }));
436
- }
437
380
  function readRows(value) {
438
381
  return isUnknownRows(value) ? value : [];
439
382
  }
@@ -444,27 +387,3 @@ function requiredString(row, key, context) {
444
387
  }
445
388
  return value;
446
389
  }
447
- function valueAtPath(root, path) {
448
- let value = root;
449
- for (const segment of path) {
450
- if (typeof segment === 'number') {
451
- if (!Array.isArray(value))
452
- return undefined;
453
- value = value[segment];
454
- }
455
- else {
456
- if (!isUnknownRecord(value))
457
- return undefined;
458
- value = value[segment];
459
- }
460
- }
461
- return value;
462
- }
463
- function tomlLiteral(value) {
464
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) {
465
- const literal = JSON.stringify(value);
466
- if (literal !== undefined)
467
- return literal;
468
- }
469
- throw new Error(`Cannot materialize Wrangler TOML value of type ${typeof value}.`);
470
- }
@@ -274,16 +274,32 @@
274
274
  # 5. The shared setup-environment.ts bootstraps repository dependencies; a
275
275
  # failure aborts shell entry instead of yielding a half-working shell.
276
276
  # Repo-owned enterShell bodies merge after this prologue.
277
- # 6. The declared Nx remote cache (`smoo.remoteCache`) is NOT resolved
278
- # here. Shell entry happens on every direnv reload and every
277
+ # 6. Shell entry resolves the `shell` group of `smoo.secrets` and nothing
278
+ # else. Shell entry happens on every direnv reload and every
279
279
  # `devenv shell -- <command>`, and a provider command that runs then is a
280
280
  # credential prompt on every one of them (1Password authorises per
281
- # requesting process lineage, and this one is new each time). Nx reads
281
+ # requesting process lineage, and this one is new each time). A
282
+ # credential only one deliberate command needs belongs to that command:
283
+ # `smoo secrets run <group> <command...>`. The full statement, and the
284
+ # derivation below, live in tooling/direnv/secret-references.ts.
285
+ #
286
+ # A group is derived from what the repository already declares, so
287
+ # nothing restates it:
288
+ #
289
+ # The variable `smoo.remoteCache.tokenSecret` names is group
290
+ # `nx-cache`, and shell entry never resolves it. Nx reads
282
291
  # NX_SELF_HOSTED_REMOTE_CACHE_SERVER and _ACCESS_TOKEN from the
283
292
  # environment when it runs: CI injects them into the job, a developer
284
293
  # exports the token once in the terminal that wants the cache (for
285
294
  # example `op signin`, then the declared command), and every nested
286
295
  # shell inherits it. Absent, Nx runs with the local cache only.
296
+ #
297
+ # A variable `.npmrc` interpolates as `${VAR}` is group `registry`,
298
+ # deferred rather than resolved, so setup-environment.ts installs
299
+ # without it. An installed checkout contacts no registry, which is why
300
+ # that costs nothing in the common case; an install that genuinely
301
+ # needs one fails naming the variable, and `smoo secrets run registry
302
+ # bun install` supplies it for that one command.
287
303
  # 7. GOROOT is unset rather than set. With devenv's Go pinned to the patch
288
304
  # release ttsc vendors, a GOROOT crossing cannot misfire on version at all,
289
305
  # so this is belt-and-braces rather than the fix — it keeps the isolation