wrangler 0.0.0-e2b7352 → 0.0.0-e2f9bb2
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 +24 -13
- package/bin/wrangler.js +3 -0
- package/miniflare-dist/index.mjs +4828 -21
- package/package.json +29 -22
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +80 -4
- package/pages/functions/filepath-routing.test.ts +11 -0
- package/pages/functions/filepath-routing.ts +8 -0
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +146 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +386 -81
- package/src/__tests__/dev.test.tsx +242 -21
- package/src/__tests__/guess-worker-format.test.ts +18 -1
- package/src/__tests__/helpers/mock-cfetch.ts +5 -1
- package/src/__tests__/helpers/mock-console.ts +20 -3
- package/src/__tests__/helpers/mock-dialogs.ts +24 -0
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/run-in-tmp.ts +7 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +5 -2
- package/src/__tests__/index.test.ts +25 -759
- package/src/__tests__/init.test.ts +1042 -0
- package/src/__tests__/jest.setup.ts +4 -4
- package/src/__tests__/kv.test.ts +130 -56
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/pages.test.ts +290 -43
- package/src/__tests__/publish.test.ts +1699 -333
- package/src/__tests__/r2.test.ts +24 -8
- package/src/__tests__/route.test.ts +4 -4
- package/src/__tests__/secret.test.ts +55 -34
- package/src/__tests__/tail.test.ts +97 -45
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +14 -9
- package/src/bundle.ts +15 -1
- package/src/cfetch/internal.ts +0 -2
- package/src/cli.ts +5 -11
- package/src/config/README.md +1 -1
- package/src/config/config.ts +20 -4
- package/src/config/environment.ts +26 -3
- package/src/config/index.ts +2 -1
- package/src/config/validation-helpers.ts +26 -5
- package/src/config/validation.ts +233 -41
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +22 -13
- package/src/create-worker-upload-form.ts +32 -9
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +60 -34
- package/src/dev/local.tsx +101 -21
- package/src/dev/remote.tsx +23 -13
- package/src/dev/use-esbuild.ts +19 -5
- package/src/dialogs.tsx +16 -6
- package/src/entry.ts +38 -11
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +3 -2
- package/src/index.tsx +529 -303
- package/src/inspect.ts +17 -11
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/index.ts +4 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +7 -7
- package/src/open-in-browser.ts +5 -4
- package/src/package-manager.ts +9 -8
- package/src/pages.tsx +860 -55
- package/src/parse.ts +1 -1
- package/src/proxy.ts +25 -11
- package/src/publish.ts +260 -141
- package/src/sites.tsx +27 -14
- package/src/tail/index.ts +1 -1
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +104 -38
- package/src/whoami.tsx +2 -1
- package/src/worker.ts +15 -6
- package/templates/gitignore +171 -0
- package/templates/static-asset-facade.js +1 -1
- package/wrangler-dist/cli.js +20948 -50169
- package/miniflare-dist/index.mjs.map +0 -7
- package/src/__tests__/logout.test.ts +0 -52
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/reporting.ts +0 -131
- package/wrangler-dist/cli.js.map +0 -7
package/src/config/validation.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
inheritableInLegacyEnvironments,
|
|
25
25
|
appendEnvName,
|
|
26
26
|
getBindingNames,
|
|
27
|
+
isValidName,
|
|
27
28
|
} from "./validation-helpers";
|
|
28
29
|
import type { Config, DevConfig, RawConfig, RawDevConfig } from "./config";
|
|
29
30
|
import type {
|
|
@@ -61,26 +62,28 @@ export function normalizeAndValidateConfig(
|
|
|
61
62
|
deprecated(
|
|
62
63
|
diagnostics,
|
|
63
64
|
rawConfig,
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
true
|
|
65
|
+
"miniflare",
|
|
66
|
+
"Wrangler does not use configuration in the `miniflare` section. Unless you are using Miniflare directly you can remove this section.",
|
|
67
|
+
true,
|
|
68
|
+
"😶 Ignored"
|
|
67
69
|
);
|
|
68
70
|
|
|
69
71
|
deprecated(
|
|
70
72
|
diagnostics,
|
|
71
73
|
rawConfig,
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
true
|
|
74
|
+
"type",
|
|
75
|
+
"Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
|
|
76
|
+
true,
|
|
77
|
+
"😶 Ignored"
|
|
75
78
|
);
|
|
76
79
|
|
|
77
80
|
deprecated(
|
|
78
81
|
diagnostics,
|
|
79
82
|
rawConfig,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
"webpack_config",
|
|
84
|
+
"Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
|
|
85
|
+
true,
|
|
86
|
+
"😶 Ignored"
|
|
84
87
|
);
|
|
85
88
|
|
|
86
89
|
validateOptionalProperty(
|
|
@@ -91,12 +94,35 @@ export function normalizeAndValidateConfig(
|
|
|
91
94
|
"boolean"
|
|
92
95
|
);
|
|
93
96
|
|
|
97
|
+
validateOptionalProperty(
|
|
98
|
+
diagnostics,
|
|
99
|
+
"",
|
|
100
|
+
"minify",
|
|
101
|
+
rawConfig.minify,
|
|
102
|
+
"boolean"
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
validateOptionalProperty(
|
|
106
|
+
diagnostics,
|
|
107
|
+
"",
|
|
108
|
+
"node_compat",
|
|
109
|
+
rawConfig.node_compat,
|
|
110
|
+
"boolean"
|
|
111
|
+
);
|
|
112
|
+
|
|
94
113
|
// TODO: set the default to false to turn on service environments as the default
|
|
95
114
|
const isLegacyEnv =
|
|
96
115
|
(args as { "legacy-env": boolean | undefined })["legacy-env"] ??
|
|
97
116
|
rawConfig.legacy_env ??
|
|
98
117
|
true;
|
|
99
118
|
|
|
119
|
+
// TODO: remove this once service environments goes GA.
|
|
120
|
+
if (!isLegacyEnv) {
|
|
121
|
+
diagnostics.warnings.push(
|
|
122
|
+
"Experimental: Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION."
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
100
126
|
const topLevelEnv = normalizeAndValidateEnvironment(
|
|
101
127
|
diagnostics,
|
|
102
128
|
configPath,
|
|
@@ -169,7 +195,12 @@ export function normalizeAndValidateConfig(
|
|
|
169
195
|
diagnostics,
|
|
170
196
|
rawConfig.migrations ?? []
|
|
171
197
|
),
|
|
172
|
-
site: normalizeAndValidateSite(
|
|
198
|
+
site: normalizeAndValidateSite(
|
|
199
|
+
diagnostics,
|
|
200
|
+
configPath,
|
|
201
|
+
rawConfig,
|
|
202
|
+
activeEnv.main
|
|
203
|
+
),
|
|
173
204
|
wasm_modules: normalizeAndValidateModulePaths(
|
|
174
205
|
diagnostics,
|
|
175
206
|
configPath,
|
|
@@ -182,6 +213,12 @@ export function normalizeAndValidateConfig(
|
|
|
182
213
|
"text_blobs",
|
|
183
214
|
rawConfig.text_blobs
|
|
184
215
|
),
|
|
216
|
+
data_blobs: normalizeAndValidateModulePaths(
|
|
217
|
+
diagnostics,
|
|
218
|
+
configPath,
|
|
219
|
+
"data_blobs",
|
|
220
|
+
rawConfig.data_blobs
|
|
221
|
+
),
|
|
185
222
|
};
|
|
186
223
|
|
|
187
224
|
validateBindingsHaveUniqueNames(diagnostics, config);
|
|
@@ -190,7 +227,7 @@ export function normalizeAndValidateConfig(
|
|
|
190
227
|
diagnostics,
|
|
191
228
|
"top-level",
|
|
192
229
|
Object.keys(rawConfig),
|
|
193
|
-
[...Object.keys(config), "env"
|
|
230
|
+
[...Object.keys(config), "env"]
|
|
194
231
|
);
|
|
195
232
|
|
|
196
233
|
return { config, diagnostics };
|
|
@@ -413,26 +450,91 @@ function normalizeAndValidateMigrations(
|
|
|
413
450
|
*/
|
|
414
451
|
function normalizeAndValidateSite(
|
|
415
452
|
diagnostics: Diagnostics,
|
|
416
|
-
|
|
453
|
+
configPath: string | undefined,
|
|
454
|
+
rawConfig: RawConfig,
|
|
455
|
+
mainEntryPoint: string | undefined
|
|
417
456
|
): Config["site"] {
|
|
418
|
-
if (
|
|
419
|
-
const { bucket, include = [], exclude = [], ...rest } =
|
|
420
|
-
|
|
457
|
+
if (rawConfig?.site !== undefined) {
|
|
458
|
+
const { bucket, include = [], exclude = [], ...rest } = rawConfig.site;
|
|
459
|
+
|
|
460
|
+
validateAdditionalProperties(diagnostics, "site", Object.keys(rest), [
|
|
461
|
+
"entry-point",
|
|
462
|
+
]);
|
|
421
463
|
validateRequiredProperty(diagnostics, "site", "bucket", bucket, "string");
|
|
422
464
|
validateTypedArray(diagnostics, "sites.include", include, "string");
|
|
423
465
|
validateTypedArray(diagnostics, "sites.exclude", exclude, "string");
|
|
424
|
-
|
|
466
|
+
validateOptionalProperty(
|
|
467
|
+
diagnostics,
|
|
468
|
+
"site",
|
|
469
|
+
"entry-point",
|
|
470
|
+
rawConfig.site["entry-point"],
|
|
471
|
+
"string"
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
deprecated(
|
|
475
|
+
diagnostics,
|
|
476
|
+
rawConfig,
|
|
477
|
+
`site.entry-point`,
|
|
478
|
+
`Delete the \`site.entry-point\` field, then add the top level \`main\` field to your configuration file:\n` +
|
|
479
|
+
`\`\`\`\n` +
|
|
480
|
+
`main = "${path.join(
|
|
481
|
+
String(rawConfig.site["entry-point"]) || "workers-site",
|
|
482
|
+
path.extname(String(rawConfig.site["entry-point"]) || "workers-site")
|
|
483
|
+
? ""
|
|
484
|
+
: "index.js"
|
|
485
|
+
)}"\n` +
|
|
486
|
+
`\`\`\``,
|
|
487
|
+
false,
|
|
488
|
+
undefined,
|
|
489
|
+
"warning"
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
let siteEntryPoint = rawConfig.site["entry-point"];
|
|
493
|
+
|
|
494
|
+
if (!mainEntryPoint && !siteEntryPoint) {
|
|
495
|
+
// this means that we're defaulting to "workers-site"
|
|
496
|
+
// so let's add the deprecation warning
|
|
497
|
+
diagnostics.warnings.push(
|
|
498
|
+
`Because you've defined a [site] configuration, we're defaulting to "workers-site" for the deprecated \`site.entry-point\`field.\n` +
|
|
499
|
+
`Add the top level \`main\` field to your configuration file:\n` +
|
|
500
|
+
`\`\`\`\n` +
|
|
501
|
+
`main = "workers-site/index.js"\n` +
|
|
502
|
+
`\`\`\``
|
|
503
|
+
);
|
|
504
|
+
siteEntryPoint = "workers-site";
|
|
505
|
+
} else if (mainEntryPoint && siteEntryPoint) {
|
|
506
|
+
diagnostics.errors.push(
|
|
507
|
+
`Don't define both the \`main\` and \`site.entry-point\` fields in your configuration.\n` +
|
|
508
|
+
`They serve the same purpose: to point to the entry-point of your worker.\n` +
|
|
509
|
+
`Delete the deprecated \`site.entry-point\` field from your config.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (configPath && siteEntryPoint) {
|
|
514
|
+
// rewrite the path to be relative to the working directory
|
|
515
|
+
siteEntryPoint = path.relative(
|
|
516
|
+
process.cwd(),
|
|
517
|
+
path.join(path.dirname(configPath), siteEntryPoint)
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
bucket,
|
|
523
|
+
"entry-point": siteEntryPoint,
|
|
524
|
+
include,
|
|
525
|
+
exclude,
|
|
526
|
+
};
|
|
425
527
|
}
|
|
426
528
|
return undefined;
|
|
427
529
|
}
|
|
428
530
|
|
|
429
531
|
/**
|
|
430
|
-
* Map the paths of the `wasm_modules` or `
|
|
532
|
+
* Map the paths of the `wasm_modules`, `text_blobs` or `data_blobs` configuration to be relative to the current working directory.
|
|
431
533
|
*/
|
|
432
534
|
function normalizeAndValidateModulePaths(
|
|
433
535
|
diagnostics: Diagnostics,
|
|
434
536
|
configPath: string | undefined,
|
|
435
|
-
field: "wasm_modules" | "text_blobs",
|
|
537
|
+
field: "wasm_modules" | "text_blobs" | "data_blobs",
|
|
436
538
|
rawMapping: Record<string, string> | undefined
|
|
437
539
|
): Record<string, string> | undefined {
|
|
438
540
|
if (rawMapping === undefined) {
|
|
@@ -455,6 +557,102 @@ function normalizeAndValidateModulePaths(
|
|
|
455
557
|
return mapping;
|
|
456
558
|
}
|
|
457
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Check whether a value has the shape of a route, which can be a string
|
|
562
|
+
* or an object that looks like {pattern: string, zone_id: string }
|
|
563
|
+
*/
|
|
564
|
+
function isValidRouteValue(item: unknown): boolean {
|
|
565
|
+
return (
|
|
566
|
+
!!item &&
|
|
567
|
+
(typeof item === "string" ||
|
|
568
|
+
(typeof item === "object" &&
|
|
569
|
+
hasProperty(item, "pattern") &&
|
|
570
|
+
typeof item.pattern === "string" &&
|
|
571
|
+
// it could have a zone_name
|
|
572
|
+
((hasProperty(item, "zone_name") &&
|
|
573
|
+
typeof item.zone_name === "string") ||
|
|
574
|
+
// or a zone_id
|
|
575
|
+
(hasProperty(item, "zone_id") && typeof item.zone_id === "string")) &&
|
|
576
|
+
Object.keys(item).length === 2))
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Validate that the field is a route.
|
|
582
|
+
*/
|
|
583
|
+
const isRoute: ValidatorFn = (diagnostics, field, value) => {
|
|
584
|
+
if (value !== undefined && !isValidRouteValue(value)) {
|
|
585
|
+
diagnostics.errors.push(
|
|
586
|
+
`Expected "${field}" to be either a string, or an object with shape { pattern, zone_id | zone_name }, but got ${JSON.stringify(
|
|
587
|
+
value
|
|
588
|
+
)}.`
|
|
589
|
+
);
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Validate that the field is an array of routes.
|
|
597
|
+
*/
|
|
598
|
+
const isRouteArray: ValidatorFn = (diagnostics, field, value) => {
|
|
599
|
+
if (value === undefined) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
if (!Array.isArray(value)) {
|
|
603
|
+
diagnostics.errors.push(
|
|
604
|
+
`Expected "${field}" to be an array but got ${JSON.stringify(value)}.`
|
|
605
|
+
);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
const invalidRoutes = [];
|
|
609
|
+
for (const item of value) {
|
|
610
|
+
if (!isValidRouteValue(item)) {
|
|
611
|
+
invalidRoutes.push(item);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (invalidRoutes.length > 0) {
|
|
615
|
+
diagnostics.errors.push(
|
|
616
|
+
`Expected "${field}" to be an array of either strings or objects with the shape { pattern, zone_id | zone_name }, but these weren't valid: ${JSON.stringify(
|
|
617
|
+
invalidRoutes,
|
|
618
|
+
null,
|
|
619
|
+
2
|
|
620
|
+
)}.`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return invalidRoutes.length === 0;
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
function validateRoute(
|
|
627
|
+
diagnostics: Diagnostics,
|
|
628
|
+
topLevelEnv: Environment | undefined,
|
|
629
|
+
rawEnv: RawEnvironment
|
|
630
|
+
): Config["route"] {
|
|
631
|
+
return inheritable(
|
|
632
|
+
diagnostics,
|
|
633
|
+
topLevelEnv,
|
|
634
|
+
rawEnv,
|
|
635
|
+
"route",
|
|
636
|
+
isRoute,
|
|
637
|
+
undefined
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function validateRoutes(
|
|
642
|
+
diagnostics: Diagnostics,
|
|
643
|
+
topLevelEnv: Environment | undefined,
|
|
644
|
+
rawEnv: RawEnvironment
|
|
645
|
+
): Config["routes"] {
|
|
646
|
+
return inheritable(
|
|
647
|
+
diagnostics,
|
|
648
|
+
topLevelEnv,
|
|
649
|
+
rawEnv,
|
|
650
|
+
"routes",
|
|
651
|
+
all(isRouteArray, isMutuallyExclusiveWith(rawEnv, "route")),
|
|
652
|
+
undefined
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
458
656
|
/**
|
|
459
657
|
* Validate top-level environment configuration and return the normalized values.
|
|
460
658
|
*/
|
|
@@ -520,22 +718,10 @@ function normalizeAndValidateEnvironment(
|
|
|
520
718
|
|
|
521
719
|
experimental(diagnostics, rawEnv, "unsafe");
|
|
522
720
|
|
|
523
|
-
const route =
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
"route",
|
|
528
|
-
isString,
|
|
529
|
-
undefined
|
|
530
|
-
);
|
|
531
|
-
const routes = inheritable(
|
|
532
|
-
diagnostics,
|
|
533
|
-
topLevelEnv,
|
|
534
|
-
rawEnv,
|
|
535
|
-
"routes",
|
|
536
|
-
all(isStringArray, isMutuallyExclusiveWith(rawEnv, "route")),
|
|
537
|
-
undefined
|
|
538
|
-
);
|
|
721
|
+
const route = validateRoute(diagnostics, topLevelEnv, rawEnv);
|
|
722
|
+
|
|
723
|
+
const routes = validateRoutes(diagnostics, topLevelEnv, rawEnv);
|
|
724
|
+
|
|
539
725
|
const workers_dev = inheritable(
|
|
540
726
|
diagnostics,
|
|
541
727
|
topLevelEnv,
|
|
@@ -554,12 +740,14 @@ function normalizeAndValidateEnvironment(
|
|
|
554
740
|
|
|
555
741
|
const environment: Environment = {
|
|
556
742
|
// Inherited fields
|
|
557
|
-
account_id:
|
|
743
|
+
account_id: inheritableInLegacyEnvironments(
|
|
558
744
|
diagnostics,
|
|
745
|
+
isLegacyEnv,
|
|
559
746
|
topLevelEnv,
|
|
560
747
|
rawEnv,
|
|
561
748
|
"account_id",
|
|
562
749
|
isString,
|
|
750
|
+
undefined,
|
|
563
751
|
undefined
|
|
564
752
|
),
|
|
565
753
|
compatibility_date: inheritable(
|
|
@@ -613,7 +801,7 @@ function normalizeAndValidateEnvironment(
|
|
|
613
801
|
topLevelEnv,
|
|
614
802
|
rawEnv,
|
|
615
803
|
"name",
|
|
616
|
-
|
|
804
|
+
isValidName,
|
|
617
805
|
appendEnvName(envName),
|
|
618
806
|
undefined
|
|
619
807
|
),
|
|
@@ -705,6 +893,8 @@ function normalizeAndValidateEnvironment(
|
|
|
705
893
|
}
|
|
706
894
|
),
|
|
707
895
|
zone_id: rawEnv.zone_id,
|
|
896
|
+
minify: rawEnv.minify,
|
|
897
|
+
node_compat: rawEnv.node_compat,
|
|
708
898
|
};
|
|
709
899
|
|
|
710
900
|
return environment;
|
|
@@ -748,7 +938,7 @@ const validateAndNormalizeRules = (
|
|
|
748
938
|
);
|
|
749
939
|
} else if (deprecatedRules) {
|
|
750
940
|
diagnostics.warnings.push(
|
|
751
|
-
`
|
|
941
|
+
`Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:\n` +
|
|
752
942
|
"```\n" +
|
|
753
943
|
TOML.stringify({ rules: deprecatedRules }) +
|
|
754
944
|
"```"
|
|
@@ -921,7 +1111,7 @@ const validateBindingsProperty =
|
|
|
921
1111
|
diagnostics.warnings.push(
|
|
922
1112
|
`The following bindings are at the top level, but not on "env.${envName}".\n` +
|
|
923
1113
|
`This is not what you probably want, since "${field}" configuration is not inherited by environments.\n` +
|
|
924
|
-
`Please add a binding for each to "${fieldPath}.bindings"
|
|
1114
|
+
`Please add a binding for each to "${fieldPath}.bindings":\n` +
|
|
925
1115
|
missingBindings.map((name) => `- ${name}`).join("\n")
|
|
926
1116
|
);
|
|
927
1117
|
}
|
|
@@ -1100,7 +1290,7 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1100
1290
|
const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
|
|
1101
1291
|
if (typeof value !== "object" || value === null) {
|
|
1102
1292
|
diagnostics.errors.push(
|
|
1103
|
-
`"
|
|
1293
|
+
`"r2_buckets" bindings should be objects, but got ${JSON.stringify(
|
|
1104
1294
|
value
|
|
1105
1295
|
)}`
|
|
1106
1296
|
);
|
|
@@ -1140,7 +1330,7 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1140
1330
|
*
|
|
1141
1331
|
* We don't want to have, for example, a KV namespace named "DATA"
|
|
1142
1332
|
* and a Durable Object also named "DATA". Then it would be ambiguous
|
|
1143
|
-
* what exactly would live at `env.DATA` (or in the case of service
|
|
1333
|
+
* what exactly would live at `env.DATA` (or in the case of service-workers,
|
|
1144
1334
|
* the `DATA` global).
|
|
1145
1335
|
*/
|
|
1146
1336
|
const validateBindingsHaveUniqueNames = (
|
|
@@ -1153,6 +1343,7 @@ const validateBindingsHaveUniqueNames = (
|
|
|
1153
1343
|
unsafe,
|
|
1154
1344
|
vars,
|
|
1155
1345
|
wasm_modules,
|
|
1346
|
+
data_blobs,
|
|
1156
1347
|
}: Partial<Config>
|
|
1157
1348
|
): boolean => {
|
|
1158
1349
|
let hasDuplicates = false;
|
|
@@ -1165,6 +1356,7 @@ const validateBindingsHaveUniqueNames = (
|
|
|
1165
1356
|
Unsafe: getBindingNames(unsafe),
|
|
1166
1357
|
"Environment Variable": getBindingNames(vars),
|
|
1167
1358
|
"WASM Module": getBindingNames(wasm_modules),
|
|
1359
|
+
"Data Blob": getBindingNames(data_blobs),
|
|
1168
1360
|
} as Record<string, string[]>;
|
|
1169
1361
|
|
|
1170
1362
|
const bindingsGroupedByName: Record<string, string[]> = {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
|
|
4
|
+
let cacheMessageShown = false;
|
|
5
|
+
|
|
6
|
+
const cacheFolder = "node_modules/.cache/wrangler";
|
|
7
|
+
|
|
8
|
+
const showCacheMessage = () => {
|
|
9
|
+
if (!cacheMessageShown) {
|
|
10
|
+
console.log(
|
|
11
|
+
`Using cached values in '${cacheFolder}'. This is used as a temporary store to improve the developer experience for some commands. It may be purged at any time. It doesn't contain any sensitive information, but it should not be commited into source control.`
|
|
12
|
+
);
|
|
13
|
+
cacheMessageShown = true;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const getConfigCache = <T>(fileName: string): Partial<T> => {
|
|
18
|
+
try {
|
|
19
|
+
const configCacheLocation = join(cacheFolder, fileName);
|
|
20
|
+
const configCache = JSON.parse(readFileSync(configCacheLocation, "utf-8"));
|
|
21
|
+
showCacheMessage();
|
|
22
|
+
return configCache;
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const saveToConfigCache = <T>(
|
|
29
|
+
fileName: string,
|
|
30
|
+
newValues: Partial<T>
|
|
31
|
+
) => {
|
|
32
|
+
const configCacheLocation = join(cacheFolder, fileName);
|
|
33
|
+
const existingValues = getConfigCache(fileName);
|
|
34
|
+
|
|
35
|
+
mkdirSync(dirname(configCacheLocation), { recursive: true });
|
|
36
|
+
writeFileSync(
|
|
37
|
+
configCacheLocation,
|
|
38
|
+
JSON.stringify({ ...existingValues, ...newValues }, null, 2)
|
|
39
|
+
);
|
|
40
|
+
showCacheMessage();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const purgeConfigCaches = () => {
|
|
44
|
+
rmSync(cacheFolder, { recursive: true, force: true });
|
|
45
|
+
};
|
|
@@ -2,6 +2,7 @@ import { URL } from "node:url";
|
|
|
2
2
|
import { fetch } from "undici";
|
|
3
3
|
import { fetchResult } from "./cfetch";
|
|
4
4
|
import { createWorkerUploadForm } from "./create-worker-upload-form";
|
|
5
|
+
import { logger } from "./logger";
|
|
5
6
|
import type { CfAccount, CfWorkerContext, CfWorkerInit } from "./worker";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -57,7 +58,8 @@ export interface CfPreviewToken {
|
|
|
57
58
|
*/
|
|
58
59
|
async function sessionToken(
|
|
59
60
|
account: CfAccount,
|
|
60
|
-
ctx: CfWorkerContext
|
|
61
|
+
ctx: CfWorkerContext,
|
|
62
|
+
abortSignal: AbortSignal
|
|
61
63
|
): Promise<CfPreviewToken> {
|
|
62
64
|
const { accountId } = account;
|
|
63
65
|
const initUrl = ctx.zone
|
|
@@ -65,9 +67,9 @@ async function sessionToken(
|
|
|
65
67
|
: `/accounts/${accountId}/workers/subdomain/edge-preview`;
|
|
66
68
|
|
|
67
69
|
const { exchange_url } = await fetchResult<{ exchange_url: string }>(initUrl);
|
|
68
|
-
const { inspector_websocket, token } = (await (
|
|
69
|
-
await fetch(exchange_url)
|
|
70
|
-
).json()) as { inspector_websocket: string; token: string };
|
|
70
|
+
const { inspector_websocket, prewarm, token } = (await (
|
|
71
|
+
await fetch(exchange_url, { signal: abortSignal })
|
|
72
|
+
).json()) as { inspector_websocket: string; token: string; prewarm: string };
|
|
71
73
|
const { host } = new URL(inspector_websocket);
|
|
72
74
|
const query = `cf_workers_preview_token=${token}`;
|
|
73
75
|
|
|
@@ -75,9 +77,7 @@ async function sessionToken(
|
|
|
75
77
|
value: token,
|
|
76
78
|
host,
|
|
77
79
|
inspectorUrl: new URL(`${inspector_websocket}?${query}`),
|
|
78
|
-
prewarmUrl: new URL(
|
|
79
|
-
`https://${host}/cdn-cgi/workers/preview/prewarm?${query}`
|
|
80
|
-
),
|
|
80
|
+
prewarmUrl: new URL(prewarm),
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -96,11 +96,13 @@ function randomId(): string {
|
|
|
96
96
|
async function createPreviewToken(
|
|
97
97
|
account: CfAccount,
|
|
98
98
|
worker: CfWorkerInit,
|
|
99
|
-
ctx: CfWorkerContext
|
|
99
|
+
ctx: CfWorkerContext,
|
|
100
|
+
abortSignal: AbortSignal
|
|
100
101
|
): Promise<CfPreviewToken> {
|
|
101
102
|
const { value, host, inspectorUrl, prewarmUrl } = await sessionToken(
|
|
102
103
|
account,
|
|
103
|
-
ctx
|
|
104
|
+
ctx,
|
|
105
|
+
abortSignal
|
|
104
106
|
);
|
|
105
107
|
|
|
106
108
|
const { accountId } = account;
|
|
@@ -155,12 +157,19 @@ async function createPreviewToken(
|
|
|
155
157
|
export async function createWorkerPreview(
|
|
156
158
|
init: CfWorkerInit,
|
|
157
159
|
account: CfAccount,
|
|
158
|
-
ctx: CfWorkerContext
|
|
160
|
+
ctx: CfWorkerContext,
|
|
161
|
+
abortSignal: AbortSignal
|
|
159
162
|
): Promise<CfPreviewToken> {
|
|
160
|
-
const token = await createPreviewToken(account, init, ctx);
|
|
161
|
-
const response = await fetch(token.prewarmUrl.href, {
|
|
163
|
+
const token = await createPreviewToken(account, init, ctx, abortSignal);
|
|
164
|
+
const response = await fetch(token.prewarmUrl.href, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
signal: abortSignal,
|
|
167
|
+
headers: {
|
|
168
|
+
"cf-workers-preview-token": token.value,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
162
171
|
if (!response.ok) {
|
|
163
|
-
|
|
172
|
+
logger.warn("worker failed to prewarm: ", response.statusText);
|
|
164
173
|
}
|
|
165
174
|
return token;
|
|
166
175
|
}
|
|
@@ -26,7 +26,7 @@ export function toMimeType(type: CfModuleType): string {
|
|
|
26
26
|
export interface WorkerMetadata {
|
|
27
27
|
/** The name of the entry point module. Only exists when the worker is in the ES module format */
|
|
28
28
|
main_module?: string;
|
|
29
|
-
/** The name of the entry point module. Only exists when the worker is in the
|
|
29
|
+
/** The name of the entry point module. Only exists when the worker is in the service-worker format */
|
|
30
30
|
body_part?: string;
|
|
31
31
|
compatibility_date?: string;
|
|
32
32
|
compatibility_flags?: string[];
|
|
@@ -38,6 +38,7 @@ export interface WorkerMetadata {
|
|
|
38
38
|
| { type: "json"; name: string; json: unknown }
|
|
39
39
|
| { type: "wasm_module"; name: string; part: string }
|
|
40
40
|
| { type: "text_blob"; name: string; part: string }
|
|
41
|
+
| { type: "data_blob"; name: string; part: string }
|
|
41
42
|
| {
|
|
42
43
|
type: "durable_object_namespace";
|
|
43
44
|
name: string;
|
|
@@ -133,6 +134,21 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
for (const [name, filePath] of Object.entries(bindings.data_blobs || {})) {
|
|
138
|
+
metadataBindings.push({
|
|
139
|
+
name,
|
|
140
|
+
type: "data_blob",
|
|
141
|
+
part: name,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
formData.set(
|
|
145
|
+
name,
|
|
146
|
+
new File([readFileSync(filePath)], filePath, {
|
|
147
|
+
type: "application/octet-stream",
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
if (main.type === "commonjs") {
|
|
137
153
|
// This is a service-worker format worker.
|
|
138
154
|
for (const module of Object.values([...(modules || [])])) {
|
|
@@ -146,8 +162,12 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
146
162
|
);
|
|
147
163
|
// And then remove it from the modules collection
|
|
148
164
|
modules = modules?.filter((m) => m !== module);
|
|
149
|
-
} else if (
|
|
150
|
-
|
|
165
|
+
} else if (
|
|
166
|
+
module.type === "compiled-wasm" ||
|
|
167
|
+
module.type === "text" ||
|
|
168
|
+
module.type === "buffer"
|
|
169
|
+
) {
|
|
170
|
+
// Convert all wasm/text/data modules into `wasm_module`/`text_blob`/`data_blob` bindings.
|
|
151
171
|
// The "name" of the module is a file path. We use it
|
|
152
172
|
// to instead be a "part" of the body, and a reference
|
|
153
173
|
// that we can use inside our source. This identifier has to be a valid
|
|
@@ -156,7 +176,12 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
156
176
|
const name = module.name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
157
177
|
metadataBindings.push({
|
|
158
178
|
name,
|
|
159
|
-
type:
|
|
179
|
+
type:
|
|
180
|
+
module.type === "compiled-wasm"
|
|
181
|
+
? "wasm_module"
|
|
182
|
+
: module.type === "text"
|
|
183
|
+
? "text_blob"
|
|
184
|
+
: "data_blob",
|
|
160
185
|
part: name,
|
|
161
186
|
});
|
|
162
187
|
|
|
@@ -167,15 +192,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
167
192
|
type:
|
|
168
193
|
module.type === "compiled-wasm"
|
|
169
194
|
? "application/wasm"
|
|
170
|
-
: "text
|
|
195
|
+
: module.type === "text"
|
|
196
|
+
? "text/plain"
|
|
197
|
+
: "application/octet-stream",
|
|
171
198
|
})
|
|
172
199
|
);
|
|
173
200
|
// And then remove it from the modules collection
|
|
174
201
|
modules = modules?.filter((m) => m !== module);
|
|
175
|
-
} else if (module.type === "buffer") {
|
|
176
|
-
throw new Error(
|
|
177
|
-
'The "buffer" module type is not yet supported in service worker format workers'
|
|
178
|
-
);
|
|
179
202
|
}
|
|
180
203
|
}
|
|
181
204
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import type { Config } from "../config";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the Worker `vars` bindings for a `wrangler dev` instance of a Worker.
|
|
9
|
+
*
|
|
10
|
+
* The `vars` bindings can be specified in the `wrangler.toml` configuration file.
|
|
11
|
+
* But "secret" `vars` are usually only provided at the server -
|
|
12
|
+
* either by creating them in the Dashboard UI, or using the `wrangler secret` command.
|
|
13
|
+
*
|
|
14
|
+
* It is useful during development, to provide these types of variable locally.
|
|
15
|
+
* When running `wrangler dev` we will look for a file called `.dev.vars`, situated
|
|
16
|
+
* next to the `wrangler.toml` file (or in the current working directory if there is no
|
|
17
|
+
* `wrangler.toml`).
|
|
18
|
+
*
|
|
19
|
+
* Any values in this file, formatted like a `dotenv` file, will add to or override `vars`
|
|
20
|
+
* bindings provided in the `wrangler.toml`.
|
|
21
|
+
*/
|
|
22
|
+
export function getVarsForDev(config: Config): Config["vars"] {
|
|
23
|
+
const configDir = path.resolve(path.dirname(config.configPath ?? "."));
|
|
24
|
+
const devVarsPath = path.resolve(configDir, ".dev.vars");
|
|
25
|
+
if (fs.existsSync(devVarsPath)) {
|
|
26
|
+
const devVarsRelativePath = path.relative(process.cwd(), devVarsPath);
|
|
27
|
+
logger.log(`Using vars defined in ${devVarsRelativePath}`);
|
|
28
|
+
const devVars = dotenv.parse(fs.readFileSync(devVarsPath, "utf8"));
|
|
29
|
+
return {
|
|
30
|
+
...config.vars,
|
|
31
|
+
...devVars,
|
|
32
|
+
};
|
|
33
|
+
} else {
|
|
34
|
+
return config.vars;
|
|
35
|
+
}
|
|
36
|
+
}
|