wrangler 0.0.0-e3cab74 → 0.0.0-e3e3243
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 +1 -1
- package/bin/wrangler.js +10 -1
- package/miniflare-dist/index.mjs +79 -0
- package/miniflare-dist/index.mjs.map +7 -0
- package/package.json +10 -7
- package/pages/functions/filepath-routing.test.ts +125 -25
- package/pages/functions/filepath-routing.ts +6 -3
- package/src/__tests__/configuration.test.ts +959 -324
- package/src/__tests__/dev.test.tsx +512 -84
- package/src/__tests__/guess-worker-format.test.ts +19 -17
- package/src/__tests__/helpers/mock-account-id.ts +22 -4
- package/src/__tests__/helpers/mock-cfetch.ts +3 -3
- package/src/__tests__/helpers/mock-console.ts +17 -9
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/run-in-tmp.ts +3 -0
- package/src/__tests__/https-options.test.ts +127 -0
- package/src/__tests__/index.test.ts +128 -11
- package/src/__tests__/jest.setup.ts +16 -4
- package/src/__tests__/kv.test.ts +364 -125
- package/src/__tests__/logout.test.ts +15 -13
- package/src/__tests__/pages.test.ts +82 -0
- package/src/__tests__/publish.test.ts +1838 -735
- package/src/__tests__/r2.test.ts +4 -4
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +167 -68
- package/src/__tests__/tail.test.ts +2 -4
- package/src/__tests__/whoami.test.tsx +21 -7
- package/src/bundle.ts +27 -5
- package/src/cfetch/internal.ts +11 -5
- package/src/config/README.md +6 -6
- package/src/config/config.ts +54 -101
- package/src/config/environment.ts +67 -1
- package/src/config/index.ts +13 -3
- package/src/config/validation-helpers.ts +207 -23
- package/src/config/validation.ts +344 -129
- package/src/{api/preview.ts → create-worker-preview.ts} +39 -20
- package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
- package/src/dev/dev.tsx +392 -0
- package/src/dev/local.tsx +232 -0
- package/src/dev/remote.tsx +203 -0
- package/src/dev/use-esbuild.ts +104 -0
- package/src/entry.ts +192 -9
- package/src/https-options.ts +122 -0
- package/src/index.tsx +577 -515
- package/src/inspect.ts +9 -1
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv.tsx → kv.ts} +97 -53
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +37 -0
- package/src/module-collection.ts +90 -2
- package/src/open-in-browser.ts +14 -2
- package/src/pages.tsx +26 -10
- package/src/proxy.ts +104 -40
- package/src/publish.ts +53 -71
- package/src/reporting.ts +25 -9
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +68 -29
- package/src/user.tsx +163 -85
- package/src/{api/worker.ts → worker.ts} +1 -24
- package/wrangler-dist/cli.js +5933 -3842
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/dev.tsx +0 -961
- package/src/guess-worker-format.ts +0 -68
package/src/config/config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Environment, RawEnvironment } from "./environment";
|
|
|
3
3
|
/**
|
|
4
4
|
* This is the static type definition for the configuration object.
|
|
5
5
|
*
|
|
6
|
-
* It reflects the configuration that you can write in wrangler.toml,
|
|
6
|
+
* It reflects a normalized and validated version of the configuration that you can write in wrangler.toml,
|
|
7
7
|
* and optionally augment with arguments passed directly to wrangler.
|
|
8
8
|
*
|
|
9
9
|
* For more information about the configuration object, see the
|
|
@@ -20,20 +20,16 @@ import type { Environment, RawEnvironment } from "./environment";
|
|
|
20
20
|
* - `@breaking`: the deprecation/optionality is a breaking change from wrangler 1.
|
|
21
21
|
* - `@todo`: there's more work to be done (with details attached).
|
|
22
22
|
*/
|
|
23
|
-
export type Config = ConfigFields<
|
|
23
|
+
export type Config = ConfigFields<DevConfig> & Environment;
|
|
24
24
|
|
|
25
|
-
export type RawConfig = Partial<ConfigFields<
|
|
25
|
+
export type RawConfig = Partial<ConfigFields<RawDevConfig>> &
|
|
26
26
|
RawEnvironment &
|
|
27
|
-
DeprecatedConfigFields
|
|
27
|
+
DeprecatedConfigFields &
|
|
28
|
+
EnvironmentMap;
|
|
28
29
|
|
|
29
|
-
export interface ConfigFields<
|
|
30
|
+
export interface ConfigFields<Dev extends RawDevConfig> {
|
|
30
31
|
configPath: string | undefined;
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
-
* The entrypoint/path to the JavaScript file that will be executed.
|
|
34
|
-
*/
|
|
35
|
-
main: string | undefined;
|
|
36
|
-
|
|
37
33
|
/**
|
|
38
34
|
* A boolean to enable "legacy" style wrangler environments (from wrangler 1).
|
|
39
35
|
* These have been superseded by Services, but there may be projects that won't
|
|
@@ -42,59 +38,10 @@ export interface ConfigFields<Env extends RawEnvironment> {
|
|
|
42
38
|
*/
|
|
43
39
|
legacy_env: boolean;
|
|
44
40
|
|
|
45
|
-
/**
|
|
46
|
-
* The `env` section defines overrides for the configuration for different environments.
|
|
47
|
-
*
|
|
48
|
-
* All environment fields can be specified at the top level of the config indicating the default environment settings.
|
|
49
|
-
*
|
|
50
|
-
* - Some fields are inherited and overridable in each environment.
|
|
51
|
-
* - But some are not inherited and must be explicitly specified in every environment, if they are specified at the top level.
|
|
52
|
-
*
|
|
53
|
-
* For more information, see the documentation at https://developers.cloudflare.com/workers/cli-wrangler/configuration#environments
|
|
54
|
-
*
|
|
55
|
-
* @default `{}`
|
|
56
|
-
*/
|
|
57
|
-
env: {
|
|
58
|
-
[envName: string]: Env;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
41
|
/**
|
|
62
42
|
* Options to configure the development server that your worker will use.
|
|
63
|
-
*
|
|
64
|
-
* @default `{}`
|
|
65
43
|
*/
|
|
66
|
-
dev:
|
|
67
|
-
/**
|
|
68
|
-
* IP address for the local dev server to listen on,
|
|
69
|
-
*
|
|
70
|
-
* @default `127.0.0.1`
|
|
71
|
-
* @todo this needs to be implemented
|
|
72
|
-
*/
|
|
73
|
-
ip?: string;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Port for the local dev server to listen on
|
|
77
|
-
*
|
|
78
|
-
* @default `8787`
|
|
79
|
-
*/
|
|
80
|
-
port?: number;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Protocol that local wrangler dev server listens to requests on.
|
|
84
|
-
*
|
|
85
|
-
* @default `http`
|
|
86
|
-
* @todo this needs to be implemented
|
|
87
|
-
*/
|
|
88
|
-
local_protocol?: string;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Protocol that wrangler dev forwards requests on
|
|
92
|
-
*
|
|
93
|
-
* @default `https`
|
|
94
|
-
* @todo this needs to be implemented
|
|
95
|
-
*/
|
|
96
|
-
upstream_protocol?: string;
|
|
97
|
-
};
|
|
44
|
+
dev: Dev;
|
|
98
45
|
|
|
99
46
|
/**
|
|
100
47
|
* A list of migrations that should be uploaded with your Worker.
|
|
@@ -188,27 +135,50 @@ export interface ConfigFields<Env extends RawEnvironment> {
|
|
|
188
135
|
[key: string]: string;
|
|
189
136
|
}
|
|
190
137
|
| undefined;
|
|
138
|
+
}
|
|
191
139
|
|
|
140
|
+
export interface DevConfig {
|
|
192
141
|
/**
|
|
193
|
-
*
|
|
142
|
+
* IP address for the local dev server to listen on,
|
|
194
143
|
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
144
|
+
* @default `localhost`
|
|
145
|
+
* @todo this needs to be implemented
|
|
146
|
+
*/
|
|
147
|
+
ip: string;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Port for the local dev server to listen on
|
|
197
151
|
*
|
|
198
|
-
* @default
|
|
152
|
+
* @default `8787`
|
|
199
153
|
*/
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
154
|
+
port: number;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Protocol that local wrangler dev server listens to requests on.
|
|
158
|
+
*
|
|
159
|
+
* @default `http`
|
|
160
|
+
* @todo this needs to be implemented
|
|
161
|
+
*/
|
|
162
|
+
local_protocol: "http" | "https";
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Protocol that wrangler dev forwards requests on
|
|
166
|
+
*
|
|
167
|
+
* Setting this to `http` is not currently implemented.
|
|
168
|
+
* See https://github.com/cloudflare/wrangler2/issues/583
|
|
169
|
+
*
|
|
170
|
+
* @default `https`
|
|
171
|
+
*/
|
|
172
|
+
upstream_protocol: "https" | "http";
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Host to forward requests to, defaults to the zone of project
|
|
176
|
+
*/
|
|
177
|
+
host: string | undefined;
|
|
210
178
|
}
|
|
211
179
|
|
|
180
|
+
export type RawDevConfig = Partial<DevConfig>;
|
|
181
|
+
|
|
212
182
|
export interface DeprecatedConfigFields {
|
|
213
183
|
/**
|
|
214
184
|
* The project "type". A holdover from wrangler 1.x.
|
|
@@ -229,37 +199,20 @@ export interface DeprecatedConfigFields {
|
|
|
229
199
|
webpack_config?: string;
|
|
230
200
|
}
|
|
231
201
|
|
|
232
|
-
|
|
233
|
-
* Deprecated upload configuration.
|
|
234
|
-
*/
|
|
235
|
-
export interface DeprecatedUpload {
|
|
202
|
+
interface EnvironmentMap {
|
|
236
203
|
/**
|
|
237
|
-
* The
|
|
204
|
+
* The `env` section defines overrides for the configuration for different environments.
|
|
238
205
|
*
|
|
239
|
-
*
|
|
240
|
-
*/
|
|
241
|
-
format?: "modules" | "service-worker";
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* The directory you wish to upload your worker from,
|
|
245
|
-
* relative to the wrangler.toml file.
|
|
206
|
+
* All environment fields can be specified at the top level of the config indicating the default environment settings.
|
|
246
207
|
*
|
|
247
|
-
*
|
|
208
|
+
* - Some fields are inherited and overridable in each environment.
|
|
209
|
+
* - But some are not inherited and must be explicitly specified in every environment, if they are specified at the top level.
|
|
248
210
|
*
|
|
249
|
-
*
|
|
250
|
-
* @breaking In wrangler 1, this defaults to ./dist, whereas in wrangler 2 it defaults to ./
|
|
251
|
-
*/
|
|
252
|
-
dir?: string;
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* The path to the Worker script, relative to `upload.dir`.
|
|
211
|
+
* For more information, see the documentation at https://developers.cloudflare.com/workers/cli-wrangler/configuration#environments
|
|
256
212
|
*
|
|
257
|
-
* @
|
|
258
|
-
*/
|
|
259
|
-
main?: string;
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* @deprecated This is now defined at the top level `rules` field.
|
|
213
|
+
* @default `{}`
|
|
263
214
|
*/
|
|
264
|
-
|
|
215
|
+
env?: {
|
|
216
|
+
[envName: string]: RawEnvironment;
|
|
217
|
+
};
|
|
265
218
|
}
|
|
@@ -51,6 +51,11 @@ interface EnvironmentInheritable {
|
|
|
51
51
|
*/
|
|
52
52
|
compatibility_flags: string[];
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* The entrypoint/path to the JavaScript file that will be executed.
|
|
56
|
+
*/
|
|
57
|
+
main: string | undefined;
|
|
58
|
+
|
|
54
59
|
/**
|
|
55
60
|
* Whether we use <name>.<subdomain>.workers.dev to
|
|
56
61
|
* test and deploy your worker.
|
|
@@ -59,7 +64,7 @@ interface EnvironmentInheritable {
|
|
|
59
64
|
* @breaking
|
|
60
65
|
* @inheritable
|
|
61
66
|
*/
|
|
62
|
-
workers_dev: boolean;
|
|
67
|
+
workers_dev: boolean | undefined;
|
|
63
68
|
|
|
64
69
|
/**
|
|
65
70
|
* A list of routes that your worker should be published to.
|
|
@@ -82,6 +87,11 @@ interface EnvironmentInheritable {
|
|
|
82
87
|
*/
|
|
83
88
|
route: string | undefined;
|
|
84
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Path to a custom tsconfig
|
|
92
|
+
*/
|
|
93
|
+
tsconfig: string | undefined;
|
|
94
|
+
|
|
85
95
|
/**
|
|
86
96
|
* The function to use to replace jsx syntax.
|
|
87
97
|
*
|
|
@@ -133,6 +143,28 @@ interface EnvironmentInheritable {
|
|
|
133
143
|
*/
|
|
134
144
|
rules: Rule[];
|
|
135
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Configures a custom build step to be run by Wrangler when building your Worker.
|
|
148
|
+
*
|
|
149
|
+
* Refer to the [custom builds documentation](https://developers.cloudflare.com/workers/cli-wrangler/configuration#build)
|
|
150
|
+
* for more details.
|
|
151
|
+
*
|
|
152
|
+
* @default {}
|
|
153
|
+
*/
|
|
154
|
+
build: {
|
|
155
|
+
/** The command used to build your Worker. On Linux and macOS, the command is executed in the `sh` shell and the `cmd` shell for Windows. The `&&` and `||` shell operators may be used. */
|
|
156
|
+
command?: string;
|
|
157
|
+
/** The directory in which the command is executed. */
|
|
158
|
+
cwd?: string;
|
|
159
|
+
/** The directory to watch for changes while using wrangler dev, defaults to the current working directory */
|
|
160
|
+
watch_dir?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Deprecated field previously used to configure the build and upload of the script.
|
|
163
|
+
* @deprecated
|
|
164
|
+
*/
|
|
165
|
+
upload?: DeprecatedUpload;
|
|
166
|
+
};
|
|
167
|
+
|
|
136
168
|
/**
|
|
137
169
|
* TODO: remove this as it has been deprecated.
|
|
138
170
|
*
|
|
@@ -277,6 +309,40 @@ interface EnvironmentDeprecated {
|
|
|
277
309
|
}[];
|
|
278
310
|
}
|
|
279
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Deprecated upload configuration.
|
|
314
|
+
*/
|
|
315
|
+
export interface DeprecatedUpload {
|
|
316
|
+
/**
|
|
317
|
+
* The format of the Worker script.
|
|
318
|
+
*
|
|
319
|
+
* @deprecated We infer the format automatically now.
|
|
320
|
+
*/
|
|
321
|
+
format?: "modules" | "service-worker";
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The directory you wish to upload your worker from,
|
|
325
|
+
* relative to the wrangler.toml file.
|
|
326
|
+
*
|
|
327
|
+
* Defaults to the directory containing the wrangler.toml file.
|
|
328
|
+
*
|
|
329
|
+
* @deprecated
|
|
330
|
+
*/
|
|
331
|
+
dir?: string;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The path to the Worker script, relative to `upload.dir`.
|
|
335
|
+
*
|
|
336
|
+
* @deprecated This will be replaced by a command line argument.
|
|
337
|
+
*/
|
|
338
|
+
main?: string;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @deprecated This is now defined at the top level `rules` field.
|
|
342
|
+
*/
|
|
343
|
+
rules?: Environment["rules"];
|
|
344
|
+
}
|
|
345
|
+
|
|
280
346
|
/**
|
|
281
347
|
* The raw environment configuration that we read from the config file.
|
|
282
348
|
*
|
package/src/config/index.ts
CHANGED
|
@@ -3,7 +3,13 @@ import { parseTOML, readFileSync } from "../parse";
|
|
|
3
3
|
import { normalizeAndValidateConfig } from "./validation";
|
|
4
4
|
import type { Config, RawConfig } from "./config";
|
|
5
5
|
|
|
6
|
-
export type {
|
|
6
|
+
export type {
|
|
7
|
+
Config,
|
|
8
|
+
RawConfig,
|
|
9
|
+
ConfigFields,
|
|
10
|
+
DevConfig,
|
|
11
|
+
RawDevConfig,
|
|
12
|
+
} from "./config";
|
|
7
13
|
export type {
|
|
8
14
|
Environment,
|
|
9
15
|
RawEnvironment,
|
|
@@ -13,7 +19,10 @@ export type {
|
|
|
13
19
|
/**
|
|
14
20
|
* Get the Wrangler configuration; read it from the give `configPath` if available.
|
|
15
21
|
*/
|
|
16
|
-
export function readConfig(
|
|
22
|
+
export function readConfig(
|
|
23
|
+
configPath: string | undefined,
|
|
24
|
+
args: unknown
|
|
25
|
+
): Config {
|
|
17
26
|
let rawConfig: RawConfig = {};
|
|
18
27
|
if (!configPath) {
|
|
19
28
|
configPath = findWranglerToml();
|
|
@@ -27,7 +36,8 @@ export function readConfig(configPath?: string): Config {
|
|
|
27
36
|
// Process the top-level configuration.
|
|
28
37
|
const { config, diagnostics } = normalizeAndValidateConfig(
|
|
29
38
|
rawConfig,
|
|
30
|
-
configPath
|
|
39
|
+
configPath,
|
|
40
|
+
args
|
|
31
41
|
);
|
|
32
42
|
|
|
33
43
|
if (diagnostics.hasWarnings()) {
|
|
@@ -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,14 @@ 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
|
+
breaking = false
|
|
17
18
|
): void {
|
|
18
19
|
const result = unwindPropertyPath(config, fieldPath);
|
|
19
20
|
if (result !== undefined && result.field in result.container) {
|
|
20
|
-
diagnostics.warnings.push(
|
|
21
|
+
(breaking ? diagnostics.errors : diagnostics.warnings).push(
|
|
22
|
+
`DEPRECATION: "${fieldPath}":\n${message}`
|
|
23
|
+
);
|
|
21
24
|
if (remove) {
|
|
22
25
|
delete (result.container as Record<string, unknown>)[result.field];
|
|
23
26
|
}
|
|
@@ -51,14 +54,82 @@ export function experimental<T extends object>(
|
|
|
51
54
|
*/
|
|
52
55
|
export function inheritable<K extends keyof Environment>(
|
|
53
56
|
diagnostics: Diagnostics,
|
|
54
|
-
|
|
57
|
+
topLevelEnv: Environment | undefined,
|
|
58
|
+
rawEnv: RawEnvironment,
|
|
59
|
+
field: K,
|
|
60
|
+
validate: ValidatorFn,
|
|
61
|
+
defaultValue: Environment[K],
|
|
62
|
+
transformFn: TransformFn<Environment[K]> = (v) => v
|
|
63
|
+
): Environment[K] {
|
|
64
|
+
validate(diagnostics, field, rawEnv[field], topLevelEnv);
|
|
65
|
+
return (
|
|
66
|
+
(rawEnv[field] as Environment[K]) ??
|
|
67
|
+
transformFn(topLevelEnv?.[field]) ??
|
|
68
|
+
defaultValue
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get an inheritable environment field, but only if we are in legacy environments
|
|
74
|
+
*/
|
|
75
|
+
export function inheritableInLegacyEnvironments<K extends keyof Environment>(
|
|
76
|
+
diagnostics: Diagnostics,
|
|
77
|
+
isLegacyEnv: boolean | undefined,
|
|
78
|
+
topLevelEnv: Environment | undefined,
|
|
55
79
|
rawEnv: RawEnvironment,
|
|
56
80
|
field: K,
|
|
57
81
|
validate: ValidatorFn,
|
|
82
|
+
transformFn: TransformFn<Environment[K]>,
|
|
58
83
|
defaultValue: Environment[K]
|
|
59
84
|
): Environment[K] {
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
return topLevelEnv === undefined || isLegacyEnv === true
|
|
86
|
+
? inheritable(
|
|
87
|
+
diagnostics,
|
|
88
|
+
topLevelEnv,
|
|
89
|
+
rawEnv,
|
|
90
|
+
field,
|
|
91
|
+
validate,
|
|
92
|
+
defaultValue,
|
|
93
|
+
transformFn
|
|
94
|
+
)
|
|
95
|
+
: notAllowedInNamedServiceEnvironment(
|
|
96
|
+
diagnostics,
|
|
97
|
+
topLevelEnv,
|
|
98
|
+
rawEnv,
|
|
99
|
+
field
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Type of function that is used to transform an inheritable environment field.
|
|
105
|
+
*/
|
|
106
|
+
type TransformFn<T> = (fieldValue: T | undefined) => T | undefined;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Transform an environment field by appending current environment name to it.
|
|
110
|
+
*/
|
|
111
|
+
export const appendEnvName =
|
|
112
|
+
(envName: string): TransformFn<string | undefined> =>
|
|
113
|
+
(fieldValue) =>
|
|
114
|
+
fieldValue ? `${fieldValue}-${envName}` : undefined;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Log an error if this named environment is trying to override the value in the top-level
|
|
118
|
+
* environment, which is not allow for this field.
|
|
119
|
+
*/
|
|
120
|
+
function notAllowedInNamedServiceEnvironment<K extends keyof Environment>(
|
|
121
|
+
diagnostics: Diagnostics,
|
|
122
|
+
topLevelEnv: Environment,
|
|
123
|
+
rawEnv: RawEnvironment,
|
|
124
|
+
field: K
|
|
125
|
+
): Environment[K] {
|
|
126
|
+
if (field in rawEnv) {
|
|
127
|
+
diagnostics.errors.push(
|
|
128
|
+
`The "${field}" field is not allowed in named service environments.\n` +
|
|
129
|
+
`Please remove the field from this environment.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return topLevelEnv[field];
|
|
62
133
|
}
|
|
63
134
|
|
|
64
135
|
/**
|
|
@@ -69,7 +140,7 @@ export function inheritable<K extends keyof Environment>(
|
|
|
69
140
|
*/
|
|
70
141
|
export function notInheritable<K extends keyof Environment>(
|
|
71
142
|
diagnostics: Diagnostics,
|
|
72
|
-
|
|
143
|
+
topLevelEnv: Environment | undefined,
|
|
73
144
|
rawConfig: RawConfig | undefined,
|
|
74
145
|
rawEnv: RawEnvironment,
|
|
75
146
|
envName: string,
|
|
@@ -78,7 +149,7 @@ export function notInheritable<K extends keyof Environment>(
|
|
|
78
149
|
defaultValue: Environment[K]
|
|
79
150
|
): Environment[K] {
|
|
80
151
|
if (rawEnv[field] !== undefined) {
|
|
81
|
-
validate(diagnostics, field, rawEnv[field],
|
|
152
|
+
validate(diagnostics, field, rawEnv[field], topLevelEnv);
|
|
82
153
|
} else {
|
|
83
154
|
if (rawConfig?.[field] !== undefined) {
|
|
84
155
|
diagnostics.warnings.push(
|
|
@@ -136,7 +207,7 @@ export type ValidatorFn = (
|
|
|
136
207
|
diagnostics: Diagnostics,
|
|
137
208
|
field: string,
|
|
138
209
|
value: unknown,
|
|
139
|
-
|
|
210
|
+
topLevelEnv: Environment | undefined
|
|
140
211
|
) => boolean;
|
|
141
212
|
|
|
142
213
|
/**
|
|
@@ -145,7 +216,7 @@ export type ValidatorFn = (
|
|
|
145
216
|
export const isString: ValidatorFn = (diagnostics, field, value) => {
|
|
146
217
|
if (value !== undefined && typeof value !== "string") {
|
|
147
218
|
diagnostics.errors.push(
|
|
148
|
-
`Expected "${field}"
|
|
219
|
+
`Expected "${field}" to be of type string but got ${JSON.stringify(
|
|
149
220
|
value
|
|
150
221
|
)}.`
|
|
151
222
|
);
|
|
@@ -163,7 +234,7 @@ export const isStringArray: ValidatorFn = (diagnostics, field, value) => {
|
|
|
163
234
|
(!Array.isArray(value) || value.some((item) => typeof item !== "string"))
|
|
164
235
|
) {
|
|
165
236
|
diagnostics.errors.push(
|
|
166
|
-
`Expected "${field}"
|
|
237
|
+
`Expected "${field}" to be of type string array but got ${JSON.stringify(
|
|
167
238
|
value
|
|
168
239
|
)}.`
|
|
169
240
|
);
|
|
@@ -185,7 +256,7 @@ export const isObjectWith =
|
|
|
185
256
|
!properties.every((prop) => prop in value))
|
|
186
257
|
) {
|
|
187
258
|
diagnostics.errors.push(
|
|
188
|
-
`Expected "${field}"
|
|
259
|
+
`Expected "${field}" to be of type object, containing properties ${properties}, but got ${JSON.stringify(
|
|
189
260
|
value
|
|
190
261
|
)}.`
|
|
191
262
|
);
|
|
@@ -202,22 +273,70 @@ export const isOneOf =
|
|
|
202
273
|
(diagnostics, field, value) => {
|
|
203
274
|
if (value !== undefined && !choices.some((choice) => value === choice)) {
|
|
204
275
|
diagnostics.errors.push(
|
|
205
|
-
`Expected "${field}" field to be
|
|
206
|
-
|
|
207
|
-
)}.`
|
|
276
|
+
`Expected "${field}" field to be one of ${JSON.stringify(
|
|
277
|
+
choices
|
|
278
|
+
)} but got ${JSON.stringify(value)}.`
|
|
208
279
|
);
|
|
209
280
|
return false;
|
|
210
281
|
}
|
|
211
282
|
return true;
|
|
212
283
|
};
|
|
213
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Aggregate multiple validator functions
|
|
287
|
+
*/
|
|
288
|
+
export const all = (...validations: ValidatorFn[]): ValidatorFn => {
|
|
289
|
+
return (diagnostics, field, value, config) => {
|
|
290
|
+
let passedValidations = true;
|
|
291
|
+
|
|
292
|
+
for (const validate of validations) {
|
|
293
|
+
if (!validate(diagnostics, field, value, config)) {
|
|
294
|
+
passedValidations = false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return passedValidations;
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Check that the field is mutually exclusive with a list of other fields.
|
|
304
|
+
*
|
|
305
|
+
* @param container the container of the fields to check against.
|
|
306
|
+
* @param fields the names of the fields to check against.
|
|
307
|
+
*/
|
|
308
|
+
export const isMutuallyExclusiveWith = <T extends RawEnvironment | RawConfig>(
|
|
309
|
+
container: T,
|
|
310
|
+
...fields: (keyof T)[]
|
|
311
|
+
): ValidatorFn => {
|
|
312
|
+
return (diagnostics, field, value) => {
|
|
313
|
+
if (value === undefined) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
for (const exclusiveWith of fields) {
|
|
318
|
+
if (container[exclusiveWith] !== undefined) {
|
|
319
|
+
diagnostics.errors.push(
|
|
320
|
+
`Expected exactly one of the following fields ${JSON.stringify([
|
|
321
|
+
field,
|
|
322
|
+
...fields,
|
|
323
|
+
])}.`
|
|
324
|
+
);
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return true;
|
|
330
|
+
};
|
|
331
|
+
};
|
|
332
|
+
|
|
214
333
|
/**
|
|
215
334
|
* Validate that the field is a boolean.
|
|
216
335
|
*/
|
|
217
336
|
export const isBoolean: ValidatorFn = (diagnostics, field, value) => {
|
|
218
337
|
if (value !== undefined && typeof value !== "boolean") {
|
|
219
338
|
diagnostics.errors.push(
|
|
220
|
-
`Expected "${field}"
|
|
339
|
+
`Expected "${field}" to be of type boolean but got ${JSON.stringify(
|
|
221
340
|
value
|
|
222
341
|
)}.`
|
|
223
342
|
);
|
|
@@ -234,21 +353,28 @@ export const validateRequiredProperty = (
|
|
|
234
353
|
container: string,
|
|
235
354
|
key: string,
|
|
236
355
|
value: unknown,
|
|
237
|
-
type: string
|
|
356
|
+
type: string,
|
|
357
|
+
choices?: unknown[]
|
|
238
358
|
): boolean => {
|
|
239
359
|
if (container) {
|
|
240
360
|
container += ".";
|
|
241
361
|
}
|
|
242
362
|
if (value === undefined) {
|
|
243
|
-
diagnostics.errors.push(`
|
|
363
|
+
diagnostics.errors.push(`"${container}${key}" is a required field.`);
|
|
244
364
|
return false;
|
|
245
365
|
} else if (typeof value !== type) {
|
|
246
366
|
diagnostics.errors.push(
|
|
247
|
-
`
|
|
367
|
+
`Expected "${container}${key}" to be of type ${type} but got ${JSON.stringify(
|
|
248
368
|
value
|
|
249
369
|
)}.`
|
|
250
370
|
);
|
|
251
371
|
return false;
|
|
372
|
+
} else if (choices) {
|
|
373
|
+
if (
|
|
374
|
+
!isOneOf(...choices)(diagnostics, `${container}${key}`, value, undefined)
|
|
375
|
+
) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
252
378
|
}
|
|
253
379
|
return true;
|
|
254
380
|
};
|
|
@@ -261,10 +387,18 @@ export const validateOptionalProperty = (
|
|
|
261
387
|
container: string,
|
|
262
388
|
key: string,
|
|
263
389
|
value: unknown,
|
|
264
|
-
type: string
|
|
390
|
+
type: string,
|
|
391
|
+
choices?: unknown[]
|
|
265
392
|
): boolean => {
|
|
266
393
|
if (value !== undefined) {
|
|
267
|
-
return validateRequiredProperty(
|
|
394
|
+
return validateRequiredProperty(
|
|
395
|
+
diagnostics,
|
|
396
|
+
container,
|
|
397
|
+
key,
|
|
398
|
+
value,
|
|
399
|
+
type,
|
|
400
|
+
choices
|
|
401
|
+
);
|
|
268
402
|
}
|
|
269
403
|
return true;
|
|
270
404
|
};
|
|
@@ -281,7 +415,7 @@ export const validateTypedArray = (
|
|
|
281
415
|
let isValid = true;
|
|
282
416
|
if (!Array.isArray(value)) {
|
|
283
417
|
diagnostics.errors.push(
|
|
284
|
-
`"${container}"
|
|
418
|
+
`Expected "${container}" to be an array of ${type}s but got ${JSON.stringify(
|
|
285
419
|
value
|
|
286
420
|
)}`
|
|
287
421
|
);
|
|
@@ -368,3 +502,53 @@ export const validateAdditionalProperties = (
|
|
|
368
502
|
}
|
|
369
503
|
return true;
|
|
370
504
|
};
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Get the names of the bindings collection in `value`.
|
|
508
|
+
*
|
|
509
|
+
* Will return an empty array if it doesn't understand the value
|
|
510
|
+
* passed in, so another form of validation should be
|
|
511
|
+
* performed externally.
|
|
512
|
+
*/
|
|
513
|
+
export const getBindingNames = (value: unknown): string[] => {
|
|
514
|
+
if (typeof value !== "object" || value === null) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (isBindingList(value)) {
|
|
519
|
+
return value.bindings.map(({ name }) => name);
|
|
520
|
+
} else if (isNamespaceList(value)) {
|
|
521
|
+
return value.map(({ binding }) => binding);
|
|
522
|
+
} else if (isRecord(value)) {
|
|
523
|
+
return Object.keys(value);
|
|
524
|
+
} else {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const isBindingList = (
|
|
530
|
+
value: unknown
|
|
531
|
+
): value is {
|
|
532
|
+
bindings: {
|
|
533
|
+
name: string;
|
|
534
|
+
}[];
|
|
535
|
+
} =>
|
|
536
|
+
isRecord(value) &&
|
|
537
|
+
"bindings" in value &&
|
|
538
|
+
Array.isArray(value.bindings) &&
|
|
539
|
+
value.bindings.every(
|
|
540
|
+
(binding) =>
|
|
541
|
+
isRecord(binding) && "name" in binding && typeof binding.name === "string"
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
const isNamespaceList = (value: unknown): value is { binding: string }[] =>
|
|
545
|
+
Array.isArray(value) &&
|
|
546
|
+
value.every(
|
|
547
|
+
(entry) =>
|
|
548
|
+
isRecord(entry) && "binding" in entry && typeof entry.binding === "string"
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
const isRecord = (
|
|
552
|
+
value: unknown
|
|
553
|
+
): value is Record<string | number | symbol, unknown> =>
|
|
554
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|