wrangler 0.0.0-e043479 → 0.0.0-e0b9366
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/package.json +10 -4
- package/pages/functions/filepath-routing.test.ts +125 -25
- package/pages/functions/filepath-routing.ts +6 -3
- package/src/__tests__/configuration.test.ts +479 -114
- package/src/__tests__/dev.test.tsx +162 -8
- package/src/__tests__/dev2.test.tsx +152 -0
- package/src/__tests__/guess-worker-format.test.ts +9 -17
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +3 -3
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- 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 +125 -0
- package/src/__tests__/jest.setup.ts +1 -1
- package/src/__tests__/kv.test.ts +27 -53
- package/src/__tests__/logout.test.ts +15 -13
- package/src/__tests__/pages.test.ts +44 -0
- package/src/__tests__/publish.test.ts +504 -32
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/whoami.test.tsx +21 -7
- package/src/bundle.ts +20 -11
- package/src/cfetch/internal.ts +11 -5
- package/src/config/config.ts +53 -44
- package/src/config/validation-helpers.ts +110 -12
- package/src/config/validation.ts +59 -41
- package/src/{api/preview.ts → create-worker-preview.ts} +23 -4
- package/src/{api/form_data.ts → create-worker-upload-form.ts} +1 -1
- package/src/dev/dev.tsx +357 -0
- package/src/dev/local.tsx +241 -0
- package/src/dev/remote.tsx +201 -0
- package/src/dev/use-esbuild.ts +100 -0
- package/src/entry.ts +111 -9
- package/src/https-options.ts +122 -0
- package/src/index.tsx +171 -109
- package/src/inspect.ts +9 -1
- package/src/{kv.tsx → kv.ts} +0 -0
- package/src/module-collection.ts +85 -2
- package/src/open-in-browser.ts +14 -2
- package/src/pages.tsx +1 -1
- package/src/proxy.ts +102 -40
- package/src/publish.ts +41 -50
- package/src/reporting.ts +25 -9
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +51 -16
- package/src/user.tsx +132 -77
- package/src/{api/worker.ts → worker.ts} +0 -23
- package/wrangler-dist/cli.js +5012 -3107
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/dev.tsx +0 -960
- package/src/guess-worker-format.ts +0 -68
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
2
|
+
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
3
|
+
import { runWrangler } from "./helpers/run-wrangler";
|
|
4
|
+
|
|
5
|
+
describe("wrangler route", () => {
|
|
6
|
+
mockConsoleMethods();
|
|
7
|
+
runInTempDir();
|
|
8
|
+
|
|
9
|
+
it("shows a deprecation notice when `wrangler route` is run", async () => {
|
|
10
|
+
await expect(runWrangler("route")).rejects
|
|
11
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
12
|
+
"DEPRECATION WARNING:
|
|
13
|
+
\`wrangler route\` has been deprecated.
|
|
14
|
+
Please use wrangler.toml and/or \`wrangler publish --routes\` to modify routes"
|
|
15
|
+
`);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("shows a deprecation notice when `wrangler route delete` is run", async () => {
|
|
19
|
+
await expect(runWrangler("route delete")).rejects
|
|
20
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
21
|
+
"DEPRECATION WARNING:
|
|
22
|
+
\`wrangler route delete\` has been deprecated.
|
|
23
|
+
Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes."
|
|
24
|
+
`);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("shows a deprecation notice when `wrangler route delete <id>` is run", async () => {
|
|
28
|
+
await expect(runWrangler("route delete some-zone-id")).rejects
|
|
29
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
30
|
+
"DEPRECATION WARNING:
|
|
31
|
+
\`wrangler route delete\` has been deprecated.
|
|
32
|
+
Remove the unwanted route(s) from wrangler.toml and run \`wrangler publish\` to remove your worker from those routes."
|
|
33
|
+
`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("shows a deprecation notice when `wrangler route list` is run", async () => {
|
|
37
|
+
await expect(runWrangler("route list")).rejects
|
|
38
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
39
|
+
"DEPRECATION WARNING:
|
|
40
|
+
\`wrangler route list\` has been deprecated.
|
|
41
|
+
Refer to wrangler.toml for a list of routes the worker will be deployed to upon publishing.
|
|
42
|
+
Refer to the Cloudflare Dashboard to see the routes this worker is currently running on."
|
|
43
|
+
`);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import { render } from "ink-testing-library";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { reinitialiseAuthTokens, writeAuthConfigFile } from "../user";
|
|
4
4
|
import { getUserInfo, WhoAmI } from "../whoami";
|
|
5
5
|
import { setMockResponse } from "./helpers/mock-cfetch";
|
|
6
|
-
import {
|
|
6
|
+
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
7
7
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
8
8
|
import type { UserInfo } from "../whoami";
|
|
9
9
|
|
|
10
10
|
describe("getUserInfo()", () => {
|
|
11
11
|
runInTempDir({ homedir: "./home" });
|
|
12
|
+
const std = mockConsoleMethods();
|
|
12
13
|
|
|
13
14
|
it("should return undefined if there is no config file", async () => {
|
|
14
|
-
await initialise();
|
|
15
15
|
const userInfo = await getUserInfo();
|
|
16
16
|
expect(userInfo).toBeUndefined();
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("should return undefined if there is an empty config file", async () => {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
writeAuthConfigFile({});
|
|
21
|
+
// Now that we have changed the auth tokens in the wrangler.toml we must reinitialize the user auth state.
|
|
22
|
+
reinitialiseAuthTokens();
|
|
22
23
|
const userInfo = await getUserInfo();
|
|
23
24
|
expect(userInfo).toBeUndefined();
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
it("should return the user's email and accounts if authenticated via config token", async () => {
|
|
27
|
-
|
|
28
|
+
writeAuthConfigFile({ oauth_token: "some-oauth-token" });
|
|
29
|
+
// Now that we have changed the auth tokens in the wrangler.toml we must reinitialize the user auth state.
|
|
30
|
+
reinitialiseAuthTokens();
|
|
31
|
+
|
|
28
32
|
setMockResponse("/user", () => {
|
|
29
33
|
return { email: "user@example.com" };
|
|
30
34
|
});
|
|
@@ -36,7 +40,6 @@ describe("getUserInfo()", () => {
|
|
|
36
40
|
];
|
|
37
41
|
});
|
|
38
42
|
|
|
39
|
-
await initialise();
|
|
40
43
|
const userInfo = await getUserInfo();
|
|
41
44
|
|
|
42
45
|
expect(userInfo).toEqual({
|
|
@@ -50,6 +53,17 @@ describe("getUserInfo()", () => {
|
|
|
50
53
|
],
|
|
51
54
|
});
|
|
52
55
|
});
|
|
56
|
+
|
|
57
|
+
it("should display a warning message if the config file contains a legacy api_token field", async () => {
|
|
58
|
+
writeAuthConfigFile({ api_token: "API_TOKEN" });
|
|
59
|
+
await reinitialiseAuthTokens();
|
|
60
|
+
await getUserInfo();
|
|
61
|
+
expect(std.warn).toMatchInlineSnapshot(`
|
|
62
|
+
"It looks like you have used Wrangler 1's \`config\` command to login with an API token.
|
|
63
|
+
This is no longer supported in the current version of Wrangler.
|
|
64
|
+
If you wish to authenticate via an API token then please set the \`CLOUDFLARE_API_TOKEN\` environment variable."
|
|
65
|
+
`);
|
|
66
|
+
});
|
|
53
67
|
});
|
|
54
68
|
|
|
55
69
|
describe("WhoAmI component", () => {
|
package/src/bundle.ts
CHANGED
|
@@ -2,10 +2,10 @@ import assert from "node:assert";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
|
-
import
|
|
6
|
-
import type { CfModule, CfScriptFormat } from "./api/worker";
|
|
5
|
+
import createModuleCollector from "./module-collection";
|
|
7
6
|
import type { Config } from "./config";
|
|
8
7
|
import type { Entry } from "./entry";
|
|
8
|
+
import type { CfModule } from "./worker";
|
|
9
9
|
|
|
10
10
|
type BundleResult = {
|
|
11
11
|
modules: CfModule[];
|
|
@@ -24,20 +24,29 @@ export async function bundleWorker(
|
|
|
24
24
|
serveAssetsFromWorker: boolean;
|
|
25
25
|
jsxFactory: string | undefined;
|
|
26
26
|
jsxFragment: string | undefined;
|
|
27
|
-
format: CfScriptFormat;
|
|
28
27
|
rules: Config["rules"];
|
|
29
28
|
watch?: esbuild.WatchMode;
|
|
30
29
|
}
|
|
31
30
|
): Promise<BundleResult> {
|
|
32
|
-
const {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const { serveAssetsFromWorker, jsxFactory, jsxFragment, rules, watch } =
|
|
32
|
+
options;
|
|
33
|
+
const entryDirectory = path.dirname(entry.file);
|
|
34
|
+
const moduleCollector = createModuleCollector({
|
|
35
|
+
wrangler1xlegacyModuleReferences: {
|
|
36
|
+
rootDirectory: entryDirectory,
|
|
37
|
+
fileNames: new Set(
|
|
38
|
+
fs
|
|
39
|
+
.readdirSync(entryDirectory, { withFileTypes: true })
|
|
40
|
+
.filter(
|
|
41
|
+
(dirEntry) =>
|
|
42
|
+
dirEntry.isFile() && dirEntry.name !== path.basename(entry.file)
|
|
43
|
+
)
|
|
44
|
+
.map((dirEnt) => dirEnt.name)
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
format: entry.format,
|
|
37
48
|
rules,
|
|
38
|
-
|
|
39
|
-
} = options;
|
|
40
|
-
const moduleCollector = makeModuleCollector({ format, rules });
|
|
49
|
+
});
|
|
41
50
|
const result = await esbuild.build({
|
|
42
51
|
...getEntryPoint(entry.file, serveAssetsFromWorker),
|
|
43
52
|
bundle: true,
|
package/src/cfetch/internal.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { fetch } from "undici";
|
|
1
|
+
import { fetch, Headers } from "undici";
|
|
2
2
|
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
3
3
|
import { ParseError, parseJSON } from "../parse";
|
|
4
|
+
import { reportError } from "../reporting";
|
|
4
5
|
import { getAPIToken, loginOrRefreshIfRequired } from "../user";
|
|
5
6
|
import type { URLSearchParams } from "node:url";
|
|
6
7
|
import type { RequestInit, HeadersInit } from "undici";
|
|
@@ -47,6 +48,7 @@ export async function fetchInternal<ResponseType>(
|
|
|
47
48
|
try {
|
|
48
49
|
return parseJSON(jsonText) as ResponseType;
|
|
49
50
|
} catch (err) {
|
|
51
|
+
await reportError(err as Error);
|
|
50
52
|
throw new ParseError({
|
|
51
53
|
text: "Received a malformed response from the API",
|
|
52
54
|
notes: [
|
|
@@ -72,7 +74,11 @@ function truncate(text: string, maxLength: number): string {
|
|
|
72
74
|
function cloneHeaders(
|
|
73
75
|
headers: HeadersInit | undefined
|
|
74
76
|
): Record<string, string> {
|
|
75
|
-
return
|
|
77
|
+
return headers instanceof Headers
|
|
78
|
+
? Object.fromEntries(headers.entries())
|
|
79
|
+
: Array.isArray(headers)
|
|
80
|
+
? Object.fromEntries(headers)
|
|
81
|
+
: { ...headers };
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
async function requireLoggedIn(): Promise<void> {
|
|
@@ -83,11 +89,11 @@ async function requireLoggedIn(): Promise<void> {
|
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
function requireApiToken(): string {
|
|
86
|
-
const
|
|
87
|
-
if (!
|
|
92
|
+
const authToken = getAPIToken();
|
|
93
|
+
if (!authToken) {
|
|
88
94
|
throw new Error("No API token found.");
|
|
89
95
|
}
|
|
90
|
-
return
|
|
96
|
+
return authToken;
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
function addAuthorizationHeader(
|
package/src/config/config.ts
CHANGED
|
@@ -20,13 +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<Environment> & Environment;
|
|
23
|
+
export type Config = ConfigFields<Environment, DevConfig> & Environment;
|
|
24
24
|
|
|
25
|
-
export type RawConfig = Partial<ConfigFields<RawEnvironment>> &
|
|
25
|
+
export type RawConfig = Partial<ConfigFields<RawEnvironment, RawDevConfig>> &
|
|
26
26
|
RawEnvironment &
|
|
27
27
|
DeprecatedConfigFields;
|
|
28
28
|
|
|
29
|
-
export interface ConfigFields<
|
|
29
|
+
export interface ConfigFields<
|
|
30
|
+
Env extends RawEnvironment,
|
|
31
|
+
Dev extends RawDevConfig
|
|
32
|
+
> {
|
|
30
33
|
configPath: string | undefined;
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -60,46 +63,8 @@ export interface ConfigFields<Env extends RawEnvironment> {
|
|
|
60
63
|
|
|
61
64
|
/**
|
|
62
65
|
* Options to configure the development server that your worker will use.
|
|
63
|
-
*
|
|
64
|
-
* @default `{}`
|
|
65
66
|
*/
|
|
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
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Host to forward requests to, defaults to the zone of project
|
|
100
|
-
*/
|
|
101
|
-
host?: string;
|
|
102
|
-
};
|
|
67
|
+
dev: Dev;
|
|
103
68
|
|
|
104
69
|
/**
|
|
105
70
|
* A list of migrations that should be uploaded with your Worker.
|
|
@@ -209,11 +174,56 @@ export interface ConfigFields<Env extends RawEnvironment> {
|
|
|
209
174
|
cwd?: string;
|
|
210
175
|
/** The directory to watch for changes while using wrangler dev, defaults to the current working directory */
|
|
211
176
|
watch_dir?: string;
|
|
212
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* Deprecated field previously used to configure the build and upload of the script.
|
|
179
|
+
* @deprecated
|
|
180
|
+
*/
|
|
213
181
|
upload?: DeprecatedUpload;
|
|
214
182
|
};
|
|
215
183
|
}
|
|
216
184
|
|
|
185
|
+
export interface DevConfig {
|
|
186
|
+
/**
|
|
187
|
+
* IP address for the local dev server to listen on,
|
|
188
|
+
*
|
|
189
|
+
* @default `127.0.0.1`
|
|
190
|
+
* @todo this needs to be implemented
|
|
191
|
+
*/
|
|
192
|
+
ip: string;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Port for the local dev server to listen on
|
|
196
|
+
*
|
|
197
|
+
* @default `8787`
|
|
198
|
+
*/
|
|
199
|
+
port: number;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Protocol that local wrangler dev server listens to requests on.
|
|
203
|
+
*
|
|
204
|
+
* @default `http`
|
|
205
|
+
* @todo this needs to be implemented
|
|
206
|
+
*/
|
|
207
|
+
local_protocol: "http" | "https";
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Protocol that wrangler dev forwards requests on
|
|
211
|
+
*
|
|
212
|
+
* Setting this to `http` is not currently implemented.
|
|
213
|
+
* See https://github.com/cloudflare/wrangler2/issues/583
|
|
214
|
+
*
|
|
215
|
+
* @default `https`
|
|
216
|
+
*/
|
|
217
|
+
upstream_protocol: "https" | "http";
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Host to forward requests to, defaults to the zone of project
|
|
221
|
+
*/
|
|
222
|
+
host: string | undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export type RawDevConfig = Partial<DevConfig>;
|
|
226
|
+
|
|
217
227
|
export interface DeprecatedConfigFields {
|
|
218
228
|
/**
|
|
219
229
|
* The project "type". A holdover from wrangler 1.x.
|
|
@@ -252,7 +262,6 @@ export interface DeprecatedUpload {
|
|
|
252
262
|
* Defaults to the directory containing the wrangler.toml file.
|
|
253
263
|
*
|
|
254
264
|
* @deprecated
|
|
255
|
-
* @breaking In wrangler 1, this defaults to ./dist, whereas in wrangler 2 it defaults to ./
|
|
256
265
|
*/
|
|
257
266
|
dir?: string;
|
|
258
267
|
|
|
@@ -5,7 +5,7 @@ import type { Environment, RawEnvironment } from "./environment";
|
|
|
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
|
}
|
|
@@ -61,6 +64,41 @@ export function inheritable<K extends keyof Environment>(
|
|
|
61
64
|
return (rawEnv[field] as Environment[K]) ?? config?.[field] ?? defaultValue;
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Get an inheritable environment field, but only if we are in legacy environments
|
|
69
|
+
*/
|
|
70
|
+
export function inheritableInLegacyEnvironments<K extends keyof Environment>(
|
|
71
|
+
diagnostics: Diagnostics,
|
|
72
|
+
config: Config | undefined,
|
|
73
|
+
rawEnv: RawEnvironment,
|
|
74
|
+
field: K,
|
|
75
|
+
validate: ValidatorFn,
|
|
76
|
+
defaultValue: Environment[K]
|
|
77
|
+
): Environment[K] {
|
|
78
|
+
return config === undefined || config.legacy_env
|
|
79
|
+
? inheritable(diagnostics, config, rawEnv, field, validate, defaultValue)
|
|
80
|
+
: notAllowedInNamedServiceEnvironment(diagnostics, config, rawEnv, field);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Log an error if this named environment is trying to override the value in the top-level
|
|
85
|
+
* environment, which is not allow for this field.
|
|
86
|
+
*/
|
|
87
|
+
function notAllowedInNamedServiceEnvironment<K extends keyof Environment>(
|
|
88
|
+
diagnostics: Diagnostics,
|
|
89
|
+
config: Config,
|
|
90
|
+
rawEnv: RawEnvironment,
|
|
91
|
+
field: K
|
|
92
|
+
): Environment[K] {
|
|
93
|
+
if (field in rawEnv) {
|
|
94
|
+
diagnostics.errors.push(
|
|
95
|
+
`The "${field}" field is not allowed in named service environments.\n` +
|
|
96
|
+
`Please remove the field from this environment.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return config[field];
|
|
100
|
+
}
|
|
101
|
+
|
|
64
102
|
/**
|
|
65
103
|
* Get a not inheritable environment field, after computing and validating its value.
|
|
66
104
|
*
|
|
@@ -202,15 +240,60 @@ export const isOneOf =
|
|
|
202
240
|
(diagnostics, field, value) => {
|
|
203
241
|
if (value !== undefined && !choices.some((choice) => value === choice)) {
|
|
204
242
|
diagnostics.errors.push(
|
|
205
|
-
`Expected "${field}" field to be
|
|
206
|
-
|
|
207
|
-
)}.`
|
|
243
|
+
`Expected "${field}" field to be one of ${JSON.stringify(
|
|
244
|
+
choices
|
|
245
|
+
)} but got ${JSON.stringify(value)}.`
|
|
208
246
|
);
|
|
209
247
|
return false;
|
|
210
248
|
}
|
|
211
249
|
return true;
|
|
212
250
|
};
|
|
213
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Aggregate multiple validator functions
|
|
254
|
+
*/
|
|
255
|
+
export const all = (...validations: ValidatorFn[]): ValidatorFn => {
|
|
256
|
+
return (diagnostics, field, value, config) => {
|
|
257
|
+
let passedValidations = true;
|
|
258
|
+
|
|
259
|
+
for (const validate of validations) {
|
|
260
|
+
if (!validate(diagnostics, field, value, config)) {
|
|
261
|
+
passedValidations = false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return passedValidations;
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Check that the field is mutually exclusive with a list of other fields.
|
|
271
|
+
*
|
|
272
|
+
* @param container the container of the fields to check against.
|
|
273
|
+
* @param fields the names of the fields to check against.
|
|
274
|
+
*/
|
|
275
|
+
export const isMutuallyExclusiveWith = <T extends RawEnvironment | RawConfig>(
|
|
276
|
+
container: T,
|
|
277
|
+
...fields: (keyof T)[]
|
|
278
|
+
): ValidatorFn => {
|
|
279
|
+
return (diagnostics, field, value) => {
|
|
280
|
+
if (value === undefined) {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
for (const exclusiveWith of fields) {
|
|
285
|
+
if (container[exclusiveWith] !== undefined) {
|
|
286
|
+
diagnostics.errors.push(
|
|
287
|
+
`Expected exactly one of ${JSON.stringify([field, ...fields])}.`
|
|
288
|
+
);
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return true;
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
|
|
214
297
|
/**
|
|
215
298
|
* Validate that the field is a boolean.
|
|
216
299
|
*/
|
|
@@ -234,21 +317,28 @@ export const validateRequiredProperty = (
|
|
|
234
317
|
container: string,
|
|
235
318
|
key: string,
|
|
236
319
|
value: unknown,
|
|
237
|
-
type: string
|
|
320
|
+
type: string,
|
|
321
|
+
choices?: unknown[]
|
|
238
322
|
): boolean => {
|
|
239
323
|
if (container) {
|
|
240
324
|
container += ".";
|
|
241
325
|
}
|
|
242
326
|
if (value === undefined) {
|
|
243
|
-
diagnostics.errors.push(`
|
|
327
|
+
diagnostics.errors.push(`"${container}${key}" is a required field.`);
|
|
244
328
|
return false;
|
|
245
329
|
} else if (typeof value !== type) {
|
|
246
330
|
diagnostics.errors.push(
|
|
247
|
-
`
|
|
331
|
+
`Expected "${container}${key}" to be of type ${type} but got ${JSON.stringify(
|
|
248
332
|
value
|
|
249
333
|
)}.`
|
|
250
334
|
);
|
|
251
335
|
return false;
|
|
336
|
+
} else if (choices) {
|
|
337
|
+
if (
|
|
338
|
+
!isOneOf(...choices)(diagnostics, `${container}${key}`, value, undefined)
|
|
339
|
+
) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
252
342
|
}
|
|
253
343
|
return true;
|
|
254
344
|
};
|
|
@@ -261,10 +351,18 @@ export const validateOptionalProperty = (
|
|
|
261
351
|
container: string,
|
|
262
352
|
key: string,
|
|
263
353
|
value: unknown,
|
|
264
|
-
type: string
|
|
354
|
+
type: string,
|
|
355
|
+
choices?: unknown[]
|
|
265
356
|
): boolean => {
|
|
266
357
|
if (value !== undefined) {
|
|
267
|
-
return validateRequiredProperty(
|
|
358
|
+
return validateRequiredProperty(
|
|
359
|
+
diagnostics,
|
|
360
|
+
container,
|
|
361
|
+
key,
|
|
362
|
+
value,
|
|
363
|
+
type,
|
|
364
|
+
choices
|
|
365
|
+
);
|
|
268
366
|
}
|
|
269
367
|
return true;
|
|
270
368
|
};
|
|
@@ -281,7 +379,7 @@ export const validateTypedArray = (
|
|
|
281
379
|
let isValid = true;
|
|
282
380
|
if (!Array.isArray(value)) {
|
|
283
381
|
diagnostics.errors.push(
|
|
284
|
-
`"${container}"
|
|
382
|
+
`Expected "${container}" to be an array of ${type}s but got ${JSON.stringify(
|
|
285
383
|
value
|
|
286
384
|
)}`
|
|
287
385
|
);
|