@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.
- package/README.md +91 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +28 -4
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +6 -20
- package/dist/lib/deploy-tags.d.ts +17 -3
- package/dist/lib/deploy-tags.d.ts.map +1 -1
- package/dist/lib/deploy-tags.js +23 -4
- package/dist/lib/json.d.ts +22 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +15 -3
- package/dist/monorepo/cargo-policy.d.ts +17 -0
- package/dist/monorepo/cargo-policy.d.ts.map +1 -1
- package/dist/monorepo/cargo-policy.js +73 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +4 -2
- package/dist/monorepo/managed-files.d.ts +5 -2
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +35 -21
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +6 -1
- package/dist/nx/index.d.ts +6 -0
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +14 -0
- package/dist/secrets/commands.d.ts +9 -5
- package/dist/secrets/commands.d.ts.map +1 -1
- package/dist/secrets/commands.js +122 -49
- package/dist/secrets/index.d.ts +42 -56
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +52 -79
- package/dist/secrets/resolver.d.ts +59 -0
- package/dist/secrets/resolver.d.ts.map +1 -0
- package/dist/secrets/resolver.js +100 -0
- package/dist/secrets/run.d.ts +23 -0
- package/dist/secrets/run.d.ts.map +1 -0
- package/dist/secrets/run.js +130 -0
- package/dist/secrets/status.d.ts +177 -0
- package/dist/secrets/status.d.ts.map +1 -0
- package/dist/secrets/status.js +724 -0
- package/dist/wrangler/deploy-stage.d.ts +1 -1
- package/dist/wrangler/deploy-stage.d.ts.map +1 -1
- package/dist/wrangler/deploy-stage.js +22 -20
- package/dist/wrangler/deployed-version.d.ts.map +1 -1
- package/dist/wrangler/deployed-version.js +7 -12
- package/dist/wrangler/flat-config.d.ts +1 -4
- package/dist/wrangler/flat-config.d.ts.map +1 -1
- package/dist/wrangler/flat-config.js +98 -44
- package/dist/wrangler/prepare-env.d.ts +4 -3
- package/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +7 -5
- package/dist/wrangler/source-config.d.ts +24 -0
- package/dist/wrangler/source-config.d.ts.map +1 -0
- package/dist/wrangler/source-config.js +110 -0
- package/dist/wrangler/stage.d.ts +55 -23
- package/dist/wrangler/stage.d.ts.map +1 -1
- package/dist/wrangler/stage.js +81 -162
- package/managed/raw/tooling/direnv/devenv.smoo.nix +19 -3
- package/managed/raw/tooling/direnv/secret-references.ts +280 -76
- package/managed/raw/tooling/direnv/setup-environment.ts +58 -2
- package/managed/raw/tsconfig.lib.json +28 -0
- package/package.json +7 -2
- package/src/cli.ts +34 -5
- package/src/github-ci/index.test.ts +6 -7
- package/src/github-ci/index.ts +11 -19
- package/src/lib/deploy-tags.ts +22 -4
- package/src/lib/json.ts +23 -1
- package/src/monorepo/cargo-policy.test.ts +91 -1
- package/src/monorepo/cargo-policy.ts +87 -1
- package/src/monorepo/index.ts +8 -2
- package/src/monorepo/managed-files.test.ts +69 -37
- package/src/monorepo/managed-files.ts +38 -21
- package/src/monorepo/packs/index.ts +10 -1
- package/src/monorepo/secret-references.test.ts +412 -6
- package/src/monorepo/setup-environment.test.ts +181 -0
- package/src/nx/index.test.ts +8 -0
- package/src/nx/index.ts +20 -0
- package/src/secrets/commands.test.ts +186 -4
- package/src/secrets/commands.ts +142 -51
- package/src/secrets/index.test.ts +4 -10
- package/src/secrets/index.ts +54 -115
- package/src/secrets/resolver.ts +130 -0
- package/src/secrets/run.test.ts +359 -0
- package/src/secrets/run.ts +148 -0
- package/src/secrets/status.test.ts +164 -0
- package/src/secrets/status.ts +297 -0
- package/src/wrangler/deploy-stage.test.ts +95 -2
- package/src/wrangler/deploy-stage.ts +26 -23
- package/src/wrangler/deployed-version.ts +7 -11
- package/src/wrangler/flat-config.test.ts +9 -4
- package/src/wrangler/flat-config.ts +1 -20
- package/src/wrangler/format-parity.test.ts +433 -0
- package/src/wrangler/prepare-env.ts +7 -5
- package/src/wrangler/source-config.test.ts +102 -0
- package/src/wrangler/source-config.ts +110 -0
- package/src/wrangler/stage.test.ts +61 -32
- 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
|
+
}
|
package/dist/wrangler/stage.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
103
|
-
/** KV/R2/D1 isolation and route checks for a pull-request stage, shared by
|
|
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
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/wrangler/stage.ts"],"names":[],"mappings":"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"}
|
package/dist/wrangler/stage.js
CHANGED
|
@@ -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
|
|
46
|
-
const _io0 = input =>
|
|
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
|
|
64
|
-
const _io0 = input =>
|
|
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
|
|
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
|
|
68
|
+
return input => Array.isArray(input) && input.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io0(elem));
|
|
82
69
|
})();
|
|
83
|
-
const
|
|
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
|
-
|
|
93
|
-
|
|
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:
|
|
102
|
-
r2Buckets:
|
|
103
|
-
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
|
|
107
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
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
|
|
145
|
-
|
|
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
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
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
|
|
351
|
+
function readR2Buckets(value) {
|
|
387
352
|
return readRows(value).map((row) => {
|
|
388
353
|
const binding = requiredString(row, 'binding', 'R2 binding');
|
|
389
|
-
return { 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
278
|
-
#
|
|
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).
|
|
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
|