wrangler 0.0.0-e3cab74 → 0.0.0-e3ce82b
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/Cloudflare_CA.pem +18 -0
- package/README.md +27 -14
- package/bin/wrangler.js +49 -5
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +43 -28
- package/pages/functions/buildPlugin.ts +105 -0
- package/pages/functions/buildWorker.ts +93 -4
- package/pages/functions/filepath-routing.test.ts +136 -25
- package/pages/functions/filepath-routing.ts +14 -3
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +147 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +1713 -411
- package/src/__tests__/dev.test.tsx +954 -84
- package/src/__tests__/guess-worker-format.test.ts +37 -18
- package/src/__tests__/helpers/mock-account-id.ts +22 -4
- package/src/__tests__/helpers/mock-cfetch.ts +8 -4
- package/src/__tests__/helpers/mock-console.ts +35 -10
- package/src/__tests__/helpers/mock-dialogs.ts +67 -2
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +130 -0
- package/src/__tests__/index.test.ts +138 -747
- package/src/__tests__/init.test.ts +1637 -0
- package/src/__tests__/jest.setup.ts +28 -8
- package/src/__tests__/kv.test.ts +598 -258
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +754 -0
- package/src/__tests__/parse.test.ts +8 -0
- package/src/__tests__/publish.test.ts +4374 -1025
- package/src/__tests__/r2.test.ts +69 -34
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +228 -73
- package/src/__tests__/tail.test.ts +146 -20
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +60 -7
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +75 -11
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +29 -20
- package/src/cli.ts +5 -11
- package/src/config/README.md +7 -7
- package/src/config/config.ts +69 -100
- package/src/config/environment.ts +119 -4
- package/src/config/index.ts +177 -4
- package/src/config/validation-helpers.ts +237 -23
- package/src/config/validation.ts +722 -191
- package/src/config-cache.ts +45 -0
- package/src/{api/preview.ts → create-worker-preview.ts} +65 -27
- package/src/{api/form_data.ts → create-worker-upload-form.ts} +55 -18
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +401 -0
- package/src/dev/local.tsx +322 -0
- package/src/dev/remote.tsx +237 -0
- package/src/dev/use-esbuild.ts +118 -0
- package/src/dialogs.tsx +64 -6
- package/src/durable.ts +102 -0
- package/src/entry.ts +279 -14
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +123 -0
- package/src/index.tsx +1386 -867
- package/src/inspect.ts +64 -11
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv.tsx → kv.ts} +195 -62
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +94 -6
- package/src/open-in-browser.ts +9 -3
- package/src/package-manager.ts +59 -11
- package/src/pages.tsx +1123 -75
- package/src/parse.ts +26 -6
- package/src/proxy.ts +159 -57
- package/src/publish.ts +581 -228
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +130 -45
- package/src/tail/index.ts +2 -2
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +275 -110
- package/src/whoami.tsx +5 -3
- package/src/{api/worker.ts → worker.ts} +24 -29
- package/src/zones.ts +73 -0
- package/templates/gitignore +171 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/templates/static-asset-facade.js +1 -5
- package/wrangler-dist/cli.js +104803 -118375
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/dev.tsx +0 -961
- package/src/guess-worker-format.ts +0 -68
- package/src/reporting.ts +0 -115
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
- package/wrangler-dist/cli.js.map +0 -7
package/src/config/index.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { findUpSync } from "find-up";
|
|
2
|
+
import { logger } from "../logger";
|
|
2
3
|
import { parseTOML, readFileSync } from "../parse";
|
|
3
4
|
import { normalizeAndValidateConfig } from "./validation";
|
|
5
|
+
import type { CfWorkerInit } from "../worker";
|
|
4
6
|
import type { Config, RawConfig } from "./config";
|
|
5
7
|
|
|
6
|
-
export type {
|
|
8
|
+
export type {
|
|
9
|
+
Config,
|
|
10
|
+
RawConfig,
|
|
11
|
+
ConfigFields,
|
|
12
|
+
DevConfig,
|
|
13
|
+
RawDevConfig,
|
|
14
|
+
} from "./config";
|
|
7
15
|
export type {
|
|
8
16
|
Environment,
|
|
9
17
|
RawEnvironment,
|
|
@@ -13,7 +21,10 @@ export type {
|
|
|
13
21
|
/**
|
|
14
22
|
* Get the Wrangler configuration; read it from the give `configPath` if available.
|
|
15
23
|
*/
|
|
16
|
-
export function readConfig(
|
|
24
|
+
export function readConfig(
|
|
25
|
+
configPath: string | undefined,
|
|
26
|
+
args: unknown
|
|
27
|
+
): Config {
|
|
17
28
|
let rawConfig: RawConfig = {};
|
|
18
29
|
if (!configPath) {
|
|
19
30
|
configPath = findWranglerToml();
|
|
@@ -27,11 +38,12 @@ export function readConfig(configPath?: string): Config {
|
|
|
27
38
|
// Process the top-level configuration.
|
|
28
39
|
const { config, diagnostics } = normalizeAndValidateConfig(
|
|
29
40
|
rawConfig,
|
|
30
|
-
configPath
|
|
41
|
+
configPath,
|
|
42
|
+
args
|
|
31
43
|
);
|
|
32
44
|
|
|
33
45
|
if (diagnostics.hasWarnings()) {
|
|
34
|
-
|
|
46
|
+
logger.warn(diagnostics.renderWarnings());
|
|
35
47
|
}
|
|
36
48
|
if (diagnostics.hasErrors()) {
|
|
37
49
|
throw new Error(diagnostics.renderErrors());
|
|
@@ -50,3 +62,164 @@ export function findWranglerToml(
|
|
|
50
62
|
const configPath = findUpSync("wrangler.toml", { cwd: referencePath });
|
|
51
63
|
return configPath;
|
|
52
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Print all the bindings a worker using a given config would have access to
|
|
68
|
+
*/
|
|
69
|
+
export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
70
|
+
const truncate = (item: string | Record<string, unknown>) => {
|
|
71
|
+
const s = typeof item === "string" ? item : JSON.stringify(item);
|
|
72
|
+
const maxLength = 40;
|
|
73
|
+
if (s.length < maxLength) {
|
|
74
|
+
return s;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return `${s.substring(0, maxLength - 3)}...`;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const output: { type: string; entries: { key: string; value: string }[] }[] =
|
|
81
|
+
[];
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
data_blobs,
|
|
85
|
+
durable_objects,
|
|
86
|
+
kv_namespaces,
|
|
87
|
+
r2_buckets,
|
|
88
|
+
services,
|
|
89
|
+
text_blobs,
|
|
90
|
+
unsafe,
|
|
91
|
+
vars,
|
|
92
|
+
wasm_modules,
|
|
93
|
+
} = bindings;
|
|
94
|
+
|
|
95
|
+
if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) {
|
|
96
|
+
output.push({
|
|
97
|
+
type: "Data Blobs",
|
|
98
|
+
entries: Object.entries(data_blobs).map(([key, value]) => ({
|
|
99
|
+
key,
|
|
100
|
+
value: truncate(value),
|
|
101
|
+
})),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (durable_objects !== undefined && durable_objects.bindings.length > 0) {
|
|
106
|
+
output.push({
|
|
107
|
+
type: "Durable Objects",
|
|
108
|
+
entries: durable_objects.bindings.map(
|
|
109
|
+
({ name, class_name, script_name, environment }) => {
|
|
110
|
+
let value = class_name;
|
|
111
|
+
if (script_name) {
|
|
112
|
+
value += ` (defined in ${script_name})`;
|
|
113
|
+
}
|
|
114
|
+
if (environment) {
|
|
115
|
+
value += ` - ${environment}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
key: name,
|
|
120
|
+
value,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (kv_namespaces !== undefined && kv_namespaces.length > 0) {
|
|
128
|
+
output.push({
|
|
129
|
+
type: "KV Namespaces",
|
|
130
|
+
entries: kv_namespaces.map(({ binding, id }) => {
|
|
131
|
+
return {
|
|
132
|
+
key: binding,
|
|
133
|
+
value: id,
|
|
134
|
+
};
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (r2_buckets !== undefined && r2_buckets.length > 0) {
|
|
140
|
+
output.push({
|
|
141
|
+
type: "R2 Buckets",
|
|
142
|
+
entries: r2_buckets.map(({ binding, bucket_name }) => {
|
|
143
|
+
return {
|
|
144
|
+
key: binding,
|
|
145
|
+
value: bucket_name,
|
|
146
|
+
};
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (services !== undefined && services.length > 0) {
|
|
152
|
+
output.push({
|
|
153
|
+
type: "Services",
|
|
154
|
+
entries: services.map(({ binding, service, environment }) => {
|
|
155
|
+
let value = service;
|
|
156
|
+
if (environment) {
|
|
157
|
+
value += ` - ${environment}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
key: binding,
|
|
162
|
+
value,
|
|
163
|
+
};
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (text_blobs !== undefined && Object.keys(text_blobs).length > 0) {
|
|
169
|
+
output.push({
|
|
170
|
+
type: "Text Blobs",
|
|
171
|
+
entries: Object.entries(text_blobs).map(([key, value]) => ({
|
|
172
|
+
key,
|
|
173
|
+
value: truncate(value),
|
|
174
|
+
})),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (unsafe !== undefined && unsafe.length > 0) {
|
|
179
|
+
output.push({
|
|
180
|
+
type: "Unsafe",
|
|
181
|
+
entries: unsafe.map(({ name, type }) => ({
|
|
182
|
+
key: type,
|
|
183
|
+
value: name,
|
|
184
|
+
})),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (vars !== undefined && Object.keys(vars).length > 0) {
|
|
189
|
+
output.push({
|
|
190
|
+
type: "Vars",
|
|
191
|
+
entries: Object.entries(vars).map(([key, value]) => ({
|
|
192
|
+
key,
|
|
193
|
+
value: `"${truncate(`${value}`)}"`,
|
|
194
|
+
})),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (wasm_modules !== undefined && Object.keys(wasm_modules).length > 0) {
|
|
199
|
+
output.push({
|
|
200
|
+
type: "Wasm Modules",
|
|
201
|
+
entries: Object.entries(wasm_modules).map(([key, value]) => ({
|
|
202
|
+
key,
|
|
203
|
+
value: truncate(value),
|
|
204
|
+
})),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (output.length === 0) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const message = [
|
|
213
|
+
`Your worker has access to the following bindings:`,
|
|
214
|
+
...output
|
|
215
|
+
.map((bindingGroup) => {
|
|
216
|
+
return [
|
|
217
|
+
`- ${bindingGroup.type}:`,
|
|
218
|
+
bindingGroup.entries.map(({ key, value }) => ` - ${key}: ${value}`),
|
|
219
|
+
];
|
|
220
|
+
})
|
|
221
|
+
.flat(2),
|
|
222
|
+
].join("\n");
|
|
223
|
+
|
|
224
|
+
logger.log(message);
|
|
225
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RawConfig } from "./config";
|
|
2
2
|
import type { Diagnostics } from "./diagnostics";
|
|
3
3
|
import type { Environment, RawEnvironment } from "./environment";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Mark a field as deprecated.
|
|
7
7
|
*
|
|
8
|
-
* This function will add a diagnostics warning if the deprecated field is found in the `rawEnv
|
|
8
|
+
* This function will add a diagnostics warning if the deprecated field is found in the `rawEnv` (or an error if it's also a breaking deprecation)
|
|
9
9
|
* The `fieldPath` is a dot separated property path, e.g. `"build.upload.format"`.
|
|
10
10
|
*/
|
|
11
11
|
export function deprecated<T extends object>(
|
|
@@ -13,11 +13,16 @@ export function deprecated<T extends object>(
|
|
|
13
13
|
config: T,
|
|
14
14
|
fieldPath: DeepKeyOf<T>,
|
|
15
15
|
message: string,
|
|
16
|
-
remove: boolean
|
|
16
|
+
remove: boolean,
|
|
17
|
+
title = "Deprecation",
|
|
18
|
+
type: "warning" | "error" = "warning"
|
|
17
19
|
): void {
|
|
20
|
+
const BOLD = "\x1b[1m";
|
|
21
|
+
const NORMAL = "\x1b[0m";
|
|
22
|
+
const diagnosticMessage = `${BOLD}${title}${NORMAL}: "${fieldPath}":\n${message}`;
|
|
18
23
|
const result = unwindPropertyPath(config, fieldPath);
|
|
19
24
|
if (result !== undefined && result.field in result.container) {
|
|
20
|
-
diagnostics.
|
|
25
|
+
diagnostics[`${type}s`].push(diagnosticMessage);
|
|
21
26
|
if (remove) {
|
|
22
27
|
delete (result.container as Record<string, unknown>)[result.field];
|
|
23
28
|
}
|
|
@@ -51,14 +56,82 @@ export function experimental<T extends object>(
|
|
|
51
56
|
*/
|
|
52
57
|
export function inheritable<K extends keyof Environment>(
|
|
53
58
|
diagnostics: Diagnostics,
|
|
54
|
-
|
|
59
|
+
topLevelEnv: Environment | undefined,
|
|
55
60
|
rawEnv: RawEnvironment,
|
|
56
61
|
field: K,
|
|
57
62
|
validate: ValidatorFn,
|
|
63
|
+
defaultValue: Environment[K],
|
|
64
|
+
transformFn: TransformFn<Environment[K]> = (v) => v
|
|
65
|
+
): Environment[K] {
|
|
66
|
+
validate(diagnostics, field, rawEnv[field], topLevelEnv);
|
|
67
|
+
return (
|
|
68
|
+
(rawEnv[field] as Environment[K]) ??
|
|
69
|
+
transformFn(topLevelEnv?.[field]) ??
|
|
70
|
+
defaultValue
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get an inheritable environment field, but only if we are in legacy environments
|
|
76
|
+
*/
|
|
77
|
+
export function inheritableInLegacyEnvironments<K extends keyof Environment>(
|
|
78
|
+
diagnostics: Diagnostics,
|
|
79
|
+
isLegacyEnv: boolean | undefined,
|
|
80
|
+
topLevelEnv: Environment | undefined,
|
|
81
|
+
rawEnv: RawEnvironment,
|
|
82
|
+
field: K,
|
|
83
|
+
validate: ValidatorFn,
|
|
84
|
+
transformFn: TransformFn<Environment[K]> = (v) => v,
|
|
58
85
|
defaultValue: Environment[K]
|
|
59
86
|
): Environment[K] {
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
return topLevelEnv === undefined || isLegacyEnv === true
|
|
88
|
+
? inheritable(
|
|
89
|
+
diagnostics,
|
|
90
|
+
topLevelEnv,
|
|
91
|
+
rawEnv,
|
|
92
|
+
field,
|
|
93
|
+
validate,
|
|
94
|
+
defaultValue,
|
|
95
|
+
transformFn
|
|
96
|
+
)
|
|
97
|
+
: notAllowedInNamedServiceEnvironment(
|
|
98
|
+
diagnostics,
|
|
99
|
+
topLevelEnv,
|
|
100
|
+
rawEnv,
|
|
101
|
+
field
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Type of function that is used to transform an inheritable environment field.
|
|
107
|
+
*/
|
|
108
|
+
type TransformFn<T> = (fieldValue: T | undefined) => T | undefined;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Transform an environment field by appending current environment name to it.
|
|
112
|
+
*/
|
|
113
|
+
export const appendEnvName =
|
|
114
|
+
(envName: string): TransformFn<string | undefined> =>
|
|
115
|
+
(fieldValue) =>
|
|
116
|
+
fieldValue ? `${fieldValue}-${envName}` : undefined;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Log an error if this named environment is trying to override the value in the top-level
|
|
120
|
+
* environment, which is not allow for this field.
|
|
121
|
+
*/
|
|
122
|
+
function notAllowedInNamedServiceEnvironment<K extends keyof Environment>(
|
|
123
|
+
diagnostics: Diagnostics,
|
|
124
|
+
topLevelEnv: Environment,
|
|
125
|
+
rawEnv: RawEnvironment,
|
|
126
|
+
field: K
|
|
127
|
+
): Environment[K] {
|
|
128
|
+
if (field in rawEnv) {
|
|
129
|
+
diagnostics.errors.push(
|
|
130
|
+
`The "${field}" field is not allowed in named service environments.\n` +
|
|
131
|
+
`Please remove the field from this environment.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return topLevelEnv[field];
|
|
62
135
|
}
|
|
63
136
|
|
|
64
137
|
/**
|
|
@@ -69,7 +142,7 @@ export function inheritable<K extends keyof Environment>(
|
|
|
69
142
|
*/
|
|
70
143
|
export function notInheritable<K extends keyof Environment>(
|
|
71
144
|
diagnostics: Diagnostics,
|
|
72
|
-
|
|
145
|
+
topLevelEnv: Environment | undefined,
|
|
73
146
|
rawConfig: RawConfig | undefined,
|
|
74
147
|
rawEnv: RawEnvironment,
|
|
75
148
|
envName: string,
|
|
@@ -78,7 +151,7 @@ export function notInheritable<K extends keyof Environment>(
|
|
|
78
151
|
defaultValue: Environment[K]
|
|
79
152
|
): Environment[K] {
|
|
80
153
|
if (rawEnv[field] !== undefined) {
|
|
81
|
-
validate(diagnostics, field, rawEnv[field],
|
|
154
|
+
validate(diagnostics, field, rawEnv[field], topLevelEnv);
|
|
82
155
|
} else {
|
|
83
156
|
if (rawConfig?.[field] !== undefined) {
|
|
84
157
|
diagnostics.warnings.push(
|
|
@@ -136,7 +209,7 @@ export type ValidatorFn = (
|
|
|
136
209
|
diagnostics: Diagnostics,
|
|
137
210
|
field: string,
|
|
138
211
|
value: unknown,
|
|
139
|
-
|
|
212
|
+
topLevelEnv: Environment | undefined
|
|
140
213
|
) => boolean;
|
|
141
214
|
|
|
142
215
|
/**
|
|
@@ -145,7 +218,7 @@ export type ValidatorFn = (
|
|
|
145
218
|
export const isString: ValidatorFn = (diagnostics, field, value) => {
|
|
146
219
|
if (value !== undefined && typeof value !== "string") {
|
|
147
220
|
diagnostics.errors.push(
|
|
148
|
-
`Expected "${field}"
|
|
221
|
+
`Expected "${field}" to be of type string but got ${JSON.stringify(
|
|
149
222
|
value
|
|
150
223
|
)}.`
|
|
151
224
|
);
|
|
@@ -154,6 +227,25 @@ export const isString: ValidatorFn = (diagnostics, field, value) => {
|
|
|
154
227
|
return true;
|
|
155
228
|
};
|
|
156
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Validate that the `name` field is compliant with EWC constraints.
|
|
232
|
+
*/
|
|
233
|
+
export const isValidName: ValidatorFn = (diagnostics, field, value) => {
|
|
234
|
+
if (
|
|
235
|
+
(typeof value === "string" && /^$|^[a-z0-9_ ][a-z0-9-_ ]*$/.test(value)) ||
|
|
236
|
+
value === undefined
|
|
237
|
+
) {
|
|
238
|
+
return true;
|
|
239
|
+
} else {
|
|
240
|
+
diagnostics.errors.push(
|
|
241
|
+
`Expected "${field}" to be of type string, alphanumeric and lowercase with dashes only but got ${JSON.stringify(
|
|
242
|
+
value
|
|
243
|
+
)}.`
|
|
244
|
+
);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
157
249
|
/**
|
|
158
250
|
* Validate that the field is an array of strings.
|
|
159
251
|
*/
|
|
@@ -163,7 +255,7 @@ export const isStringArray: ValidatorFn = (diagnostics, field, value) => {
|
|
|
163
255
|
(!Array.isArray(value) || value.some((item) => typeof item !== "string"))
|
|
164
256
|
) {
|
|
165
257
|
diagnostics.errors.push(
|
|
166
|
-
`Expected "${field}"
|
|
258
|
+
`Expected "${field}" to be of type string array but got ${JSON.stringify(
|
|
167
259
|
value
|
|
168
260
|
)}.`
|
|
169
261
|
);
|
|
@@ -185,12 +277,21 @@ export const isObjectWith =
|
|
|
185
277
|
!properties.every((prop) => prop in value))
|
|
186
278
|
) {
|
|
187
279
|
diagnostics.errors.push(
|
|
188
|
-
`Expected "${field}"
|
|
280
|
+
`Expected "${field}" to be of type object, containing only properties ${properties}, but got ${JSON.stringify(
|
|
189
281
|
value
|
|
190
282
|
)}.`
|
|
191
283
|
);
|
|
192
284
|
return false;
|
|
193
285
|
}
|
|
286
|
+
// it's an object with the field as desired,
|
|
287
|
+
// but let's also check for unexpected fields
|
|
288
|
+
if (value !== undefined) {
|
|
289
|
+
const restFields = Object.keys(value).filter(
|
|
290
|
+
(key) => !properties.includes(key)
|
|
291
|
+
);
|
|
292
|
+
validateAdditionalProperties(diagnostics, field, restFields, []);
|
|
293
|
+
}
|
|
294
|
+
|
|
194
295
|
return true;
|
|
195
296
|
};
|
|
196
297
|
|
|
@@ -202,22 +303,70 @@ export const isOneOf =
|
|
|
202
303
|
(diagnostics, field, value) => {
|
|
203
304
|
if (value !== undefined && !choices.some((choice) => value === choice)) {
|
|
204
305
|
diagnostics.errors.push(
|
|
205
|
-
`Expected "${field}" field to be
|
|
206
|
-
|
|
207
|
-
)}.`
|
|
306
|
+
`Expected "${field}" field to be one of ${JSON.stringify(
|
|
307
|
+
choices
|
|
308
|
+
)} but got ${JSON.stringify(value)}.`
|
|
208
309
|
);
|
|
209
310
|
return false;
|
|
210
311
|
}
|
|
211
312
|
return true;
|
|
212
313
|
};
|
|
213
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Aggregate multiple validator functions
|
|
317
|
+
*/
|
|
318
|
+
export const all = (...validations: ValidatorFn[]): ValidatorFn => {
|
|
319
|
+
return (diagnostics, field, value, config) => {
|
|
320
|
+
let passedValidations = true;
|
|
321
|
+
|
|
322
|
+
for (const validate of validations) {
|
|
323
|
+
if (!validate(diagnostics, field, value, config)) {
|
|
324
|
+
passedValidations = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return passedValidations;
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Check that the field is mutually exclusive with a list of other fields.
|
|
334
|
+
*
|
|
335
|
+
* @param container the container of the fields to check against.
|
|
336
|
+
* @param fields the names of the fields to check against.
|
|
337
|
+
*/
|
|
338
|
+
export const isMutuallyExclusiveWith = <T extends RawEnvironment | RawConfig>(
|
|
339
|
+
container: T,
|
|
340
|
+
...fields: (keyof T)[]
|
|
341
|
+
): ValidatorFn => {
|
|
342
|
+
return (diagnostics, field, value) => {
|
|
343
|
+
if (value === undefined) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
for (const exclusiveWith of fields) {
|
|
348
|
+
if (container[exclusiveWith] !== undefined) {
|
|
349
|
+
diagnostics.errors.push(
|
|
350
|
+
`Expected exactly one of the following fields ${JSON.stringify([
|
|
351
|
+
field,
|
|
352
|
+
...fields,
|
|
353
|
+
])}.`
|
|
354
|
+
);
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return true;
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
|
|
214
363
|
/**
|
|
215
364
|
* Validate that the field is a boolean.
|
|
216
365
|
*/
|
|
217
366
|
export const isBoolean: ValidatorFn = (diagnostics, field, value) => {
|
|
218
367
|
if (value !== undefined && typeof value !== "boolean") {
|
|
219
368
|
diagnostics.errors.push(
|
|
220
|
-
`Expected "${field}"
|
|
369
|
+
`Expected "${field}" to be of type boolean but got ${JSON.stringify(
|
|
221
370
|
value
|
|
222
371
|
)}.`
|
|
223
372
|
);
|
|
@@ -234,21 +383,28 @@ export const validateRequiredProperty = (
|
|
|
234
383
|
container: string,
|
|
235
384
|
key: string,
|
|
236
385
|
value: unknown,
|
|
237
|
-
type: string
|
|
386
|
+
type: string,
|
|
387
|
+
choices?: unknown[]
|
|
238
388
|
): boolean => {
|
|
239
389
|
if (container) {
|
|
240
390
|
container += ".";
|
|
241
391
|
}
|
|
242
392
|
if (value === undefined) {
|
|
243
|
-
diagnostics.errors.push(`
|
|
393
|
+
diagnostics.errors.push(`"${container}${key}" is a required field.`);
|
|
244
394
|
return false;
|
|
245
395
|
} else if (typeof value !== type) {
|
|
246
396
|
diagnostics.errors.push(
|
|
247
|
-
`
|
|
397
|
+
`Expected "${container}${key}" to be of type ${type} but got ${JSON.stringify(
|
|
248
398
|
value
|
|
249
399
|
)}.`
|
|
250
400
|
);
|
|
251
401
|
return false;
|
|
402
|
+
} else if (choices) {
|
|
403
|
+
if (
|
|
404
|
+
!isOneOf(...choices)(diagnostics, `${container}${key}`, value, undefined)
|
|
405
|
+
) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
252
408
|
}
|
|
253
409
|
return true;
|
|
254
410
|
};
|
|
@@ -261,10 +417,18 @@ export const validateOptionalProperty = (
|
|
|
261
417
|
container: string,
|
|
262
418
|
key: string,
|
|
263
419
|
value: unknown,
|
|
264
|
-
type: string
|
|
420
|
+
type: string,
|
|
421
|
+
choices?: unknown[]
|
|
265
422
|
): boolean => {
|
|
266
423
|
if (value !== undefined) {
|
|
267
|
-
return validateRequiredProperty(
|
|
424
|
+
return validateRequiredProperty(
|
|
425
|
+
diagnostics,
|
|
426
|
+
container,
|
|
427
|
+
key,
|
|
428
|
+
value,
|
|
429
|
+
type,
|
|
430
|
+
choices
|
|
431
|
+
);
|
|
268
432
|
}
|
|
269
433
|
return true;
|
|
270
434
|
};
|
|
@@ -281,7 +445,7 @@ export const validateTypedArray = (
|
|
|
281
445
|
let isValid = true;
|
|
282
446
|
if (!Array.isArray(value)) {
|
|
283
447
|
diagnostics.errors.push(
|
|
284
|
-
`"${container}"
|
|
448
|
+
`Expected "${container}" to be an array of ${type}s but got ${JSON.stringify(
|
|
285
449
|
value
|
|
286
450
|
)}`
|
|
287
451
|
);
|
|
@@ -368,3 +532,53 @@ export const validateAdditionalProperties = (
|
|
|
368
532
|
}
|
|
369
533
|
return true;
|
|
370
534
|
};
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Get the names of the bindings collection in `value`.
|
|
538
|
+
*
|
|
539
|
+
* Will return an empty array if it doesn't understand the value
|
|
540
|
+
* passed in, so another form of validation should be
|
|
541
|
+
* performed externally.
|
|
542
|
+
*/
|
|
543
|
+
export const getBindingNames = (value: unknown): string[] => {
|
|
544
|
+
if (typeof value !== "object" || value === null) {
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (isBindingList(value)) {
|
|
549
|
+
return value.bindings.map(({ name }) => name);
|
|
550
|
+
} else if (isNamespaceList(value)) {
|
|
551
|
+
return value.map(({ binding }) => binding);
|
|
552
|
+
} else if (isRecord(value)) {
|
|
553
|
+
return Object.keys(value);
|
|
554
|
+
} else {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const isBindingList = (
|
|
560
|
+
value: unknown
|
|
561
|
+
): value is {
|
|
562
|
+
bindings: {
|
|
563
|
+
name: string;
|
|
564
|
+
}[];
|
|
565
|
+
} =>
|
|
566
|
+
isRecord(value) &&
|
|
567
|
+
"bindings" in value &&
|
|
568
|
+
Array.isArray(value.bindings) &&
|
|
569
|
+
value.bindings.every(
|
|
570
|
+
(binding) =>
|
|
571
|
+
isRecord(binding) && "name" in binding && typeof binding.name === "string"
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
const isNamespaceList = (value: unknown): value is { binding: string }[] =>
|
|
575
|
+
Array.isArray(value) &&
|
|
576
|
+
value.every(
|
|
577
|
+
(entry) =>
|
|
578
|
+
isRecord(entry) && "binding" in entry && typeof entry.binding === "string"
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
const isRecord = (
|
|
582
|
+
value: unknown
|
|
583
|
+
): value is Record<string | number | symbol, unknown> =>
|
|
584
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|