wrangler 2.1.8 → 2.1.9
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 +2 -3
- package/src/__tests__/dev.test.tsx +13 -12
- package/src/__tests__/init.test.ts +99 -0
- package/src/__tests__/publish.test.ts +7 -4
- package/src/api/dev.ts +1 -0
- package/src/config/index.ts +14 -4
- package/src/dev/local.tsx +70 -34
- package/src/dev/start-server.ts +30 -2
- package/src/dev.tsx +6 -3
- package/src/init.ts +10 -6
- package/src/module-collection.ts +9 -0
- package/src/proxy.ts +59 -24
- package/templates/new-worker.js +1 -1
- package/wrangler-dist/cli.d.ts +1 -0
- package/wrangler-dist/cli.js +466 -3229
- package/src/faye-websocket.d.ts +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrangler",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.9",
|
|
4
4
|
"description": "Command-line interface for all things Cloudflare Workers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wrangler",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"@databases/sql": "^3.2.0",
|
|
113
113
|
"@iarna/toml": "^3.0.0",
|
|
114
114
|
"@microsoft/api-extractor": "^7.28.3",
|
|
115
|
-
"@miniflare/tre": "3.0.0-next.
|
|
115
|
+
"@miniflare/tre": "3.0.0-next.2",
|
|
116
116
|
"@types/better-sqlite3": "^7.6.0",
|
|
117
117
|
"@types/busboy": "^1.5.0",
|
|
118
118
|
"@types/command-exists": "^1.2.0",
|
|
@@ -139,7 +139,6 @@
|
|
|
139
139
|
"dotenv": "^16.0.0",
|
|
140
140
|
"execa": "^6.1.0",
|
|
141
141
|
"express": "^4.18.1",
|
|
142
|
-
"faye-websocket": "^0.11.4",
|
|
143
142
|
"finalhandler": "^1.2.0",
|
|
144
143
|
"find-up": "^6.3.0",
|
|
145
144
|
"get-port": "^6.1.2",
|
|
@@ -1022,6 +1022,7 @@ describe("wrangler dev", () => {
|
|
|
1022
1022
|
--jsx-fragment The function that is called for each JSX fragment [string]
|
|
1023
1023
|
--tsconfig Path to a custom tsconfig.json file [string]
|
|
1024
1024
|
-l, --local Run on my machine [boolean] [default: false]
|
|
1025
|
+
--experimental-local Run on my machine using the Cloudflare Workers runtime [boolean] [default: false]
|
|
1025
1026
|
--minify Minify the script [boolean]
|
|
1026
1027
|
--node-compat Enable node.js compatibility [boolean]
|
|
1027
1028
|
--persist Enable persistence for local mode, using default path: .wrangler/state [boolean]
|
|
@@ -1325,18 +1326,18 @@ describe("wrangler dev", () => {
|
|
|
1325
1326
|
fs.writeFileSync("index.js", `export default {};`);
|
|
1326
1327
|
await runWrangler("dev index.js");
|
|
1327
1328
|
expect(std).toMatchInlineSnapshot(`
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1329
|
+
Object {
|
|
1330
|
+
"debug": "",
|
|
1331
|
+
"err": "",
|
|
1332
|
+
"out": "Using vars defined in .dev.vars
|
|
1333
|
+
Your worker has access to the following bindings:
|
|
1334
|
+
- Vars:
|
|
1335
|
+
- variable: 123
|
|
1336
|
+
- overriden: \\"(hidden)\\"
|
|
1337
|
+
- SECRET: \\"(hidden)\\"",
|
|
1338
|
+
"warn": "",
|
|
1339
|
+
}
|
|
1340
|
+
`);
|
|
1340
1341
|
});
|
|
1341
1342
|
});
|
|
1342
1343
|
});
|
|
@@ -2608,6 +2608,105 @@ describe("init", () => {
|
|
|
2608
2608
|
).rejects.toThrowError();
|
|
2609
2609
|
});
|
|
2610
2610
|
|
|
2611
|
+
it("should not inlcude migrations in config file when none are necessary", async () => {
|
|
2612
|
+
const mockDate = "1988-08-07";
|
|
2613
|
+
jest
|
|
2614
|
+
.spyOn(Date.prototype, "toISOString")
|
|
2615
|
+
.mockImplementation(() => `${mockDate}T00:00:00.000Z`);
|
|
2616
|
+
const mockData = {
|
|
2617
|
+
id: "memory-crystal",
|
|
2618
|
+
default_environment: {
|
|
2619
|
+
environment: "test",
|
|
2620
|
+
created_on: "1988-08-07",
|
|
2621
|
+
modified_on: "1988-08-07",
|
|
2622
|
+
script: {
|
|
2623
|
+
id: "memory-crystal",
|
|
2624
|
+
tag: "test-tag",
|
|
2625
|
+
etag: "some-etag",
|
|
2626
|
+
handlers: [],
|
|
2627
|
+
modified_on: "1988-08-07",
|
|
2628
|
+
created_on: "1988-08-07",
|
|
2629
|
+
usage_model: "bundled",
|
|
2630
|
+
compatibility_date: "1988-08-07",
|
|
2631
|
+
},
|
|
2632
|
+
},
|
|
2633
|
+
environments: [],
|
|
2634
|
+
};
|
|
2635
|
+
|
|
2636
|
+
setMockResponse(
|
|
2637
|
+
`/accounts/:accountId/workers/services/:scriptName`,
|
|
2638
|
+
"GET",
|
|
2639
|
+
() => mockData
|
|
2640
|
+
);
|
|
2641
|
+
setMockResponse(
|
|
2642
|
+
`/accounts/:accountId/workers/services/:scriptName/environments/:environment/bindings`,
|
|
2643
|
+
"GET",
|
|
2644
|
+
() => []
|
|
2645
|
+
);
|
|
2646
|
+
setMockResponse(
|
|
2647
|
+
`/accounts/:accountId/workers/services/:scriptName/environments/:environment/routes`,
|
|
2648
|
+
"GET",
|
|
2649
|
+
() => []
|
|
2650
|
+
);
|
|
2651
|
+
setMockResponse(
|
|
2652
|
+
`/accounts/:accountId/workers/services/:scriptName/environments/:environment`,
|
|
2653
|
+
"GET",
|
|
2654
|
+
() => mockServiceMetadata.default_environment
|
|
2655
|
+
);
|
|
2656
|
+
setMockResponse(
|
|
2657
|
+
`/accounts/:accountId/workers/scripts/:scriptName/schedules`,
|
|
2658
|
+
"GET",
|
|
2659
|
+
() => {
|
|
2660
|
+
return {
|
|
2661
|
+
schedules: [],
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
);
|
|
2665
|
+
|
|
2666
|
+
setMockFetchDashScript({
|
|
2667
|
+
accountId: "LCARS",
|
|
2668
|
+
fromDashScriptName: "isolinear-optical-chip",
|
|
2669
|
+
environment: mockServiceMetadata.default_environment.environment,
|
|
2670
|
+
mockResponse: mockDashboardScript,
|
|
2671
|
+
});
|
|
2672
|
+
|
|
2673
|
+
mockConfirm(
|
|
2674
|
+
{
|
|
2675
|
+
text: "Would you like to use git to manage this Worker?",
|
|
2676
|
+
result: false,
|
|
2677
|
+
},
|
|
2678
|
+
{
|
|
2679
|
+
text: "Would you like to use TypeScript?",
|
|
2680
|
+
result: true,
|
|
2681
|
+
},
|
|
2682
|
+
{
|
|
2683
|
+
text: "No package.json found. Would you like to create one?",
|
|
2684
|
+
result: true,
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
text: "Would you like to install the type definitions for Workers into your package.json?",
|
|
2688
|
+
result: true,
|
|
2689
|
+
}
|
|
2690
|
+
);
|
|
2691
|
+
|
|
2692
|
+
await runWrangler("init --from-dash isolinear-optical-chip");
|
|
2693
|
+
|
|
2694
|
+
checkFiles({
|
|
2695
|
+
items: {
|
|
2696
|
+
"isolinear-optical-chip/wrangler.toml": wranglerToml({
|
|
2697
|
+
compatibility_date: "1988-08-07",
|
|
2698
|
+
env: {},
|
|
2699
|
+
main: "src/index.ts",
|
|
2700
|
+
triggers: {
|
|
2701
|
+
crons: [],
|
|
2702
|
+
},
|
|
2703
|
+
usage_model: "bundled",
|
|
2704
|
+
name: "isolinear-optical-chip",
|
|
2705
|
+
}),
|
|
2706
|
+
},
|
|
2707
|
+
});
|
|
2708
|
+
});
|
|
2709
|
+
|
|
2611
2710
|
it("should not continue if no worker name is provided", async () => {
|
|
2612
2711
|
await expect(
|
|
2613
2712
|
runWrangler("init --from-dash")
|
|
@@ -512,7 +512,7 @@ describe("publish", () => {
|
|
|
512
512
|
"Total Upload: xx KiB / gzip: xx KiB
|
|
513
513
|
Your worker has access to the following bindings:
|
|
514
514
|
- Vars:
|
|
515
|
-
- xyz:
|
|
515
|
+
- xyz: 123
|
|
516
516
|
Uploaded test-name (TIMINGS)
|
|
517
517
|
Published test-name (TIMINGS)
|
|
518
518
|
https://test-name.test-sub-domain.workers.dev"
|
|
@@ -4527,7 +4527,7 @@ addEventListener('fetch', event => {});`
|
|
|
4527
4527
|
- some unsafe thing: UNSAFE_BINDING_ONE
|
|
4528
4528
|
- another unsafe thing: UNSAFE_BINDING_TWO
|
|
4529
4529
|
- Vars:
|
|
4530
|
-
- ENV_VAR_ONE:
|
|
4530
|
+
- ENV_VAR_ONE: 123
|
|
4531
4531
|
- ENV_VAR_TWO: \\"Hello, I'm an environment variable\\"
|
|
4532
4532
|
- Wasm Modules:
|
|
4533
4533
|
- WASM_MODULE_ONE: some_wasm.wasm
|
|
@@ -5275,8 +5275,11 @@ addEventListener('fetch', event => {});`
|
|
|
5275
5275
|
Your worker has access to the following bindings:
|
|
5276
5276
|
- Vars:
|
|
5277
5277
|
- text: \\"plain ol' string\\"
|
|
5278
|
-
- count:
|
|
5279
|
-
- complex:
|
|
5278
|
+
- count: 1
|
|
5279
|
+
- complex: {
|
|
5280
|
+
\\"enabled\\": true,
|
|
5281
|
+
\\"id\\": 123
|
|
5282
|
+
}
|
|
5280
5283
|
Uploaded test-name (TIMINGS)
|
|
5281
5284
|
Published test-name (TIMINGS)
|
|
5282
5285
|
https://test-name.test-sub-domain.workers.dev"
|
package/src/api/dev.ts
CHANGED
package/src/config/index.ts
CHANGED
|
@@ -219,10 +219,20 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
219
219
|
if (vars !== undefined && Object.keys(vars).length > 0) {
|
|
220
220
|
output.push({
|
|
221
221
|
type: "Vars",
|
|
222
|
-
entries: Object.entries(vars).map(([key, value]) =>
|
|
223
|
-
|
|
224
|
-
value
|
|
225
|
-
|
|
222
|
+
entries: Object.entries(vars).map(([key, value]) => {
|
|
223
|
+
let parsedValue;
|
|
224
|
+
if (typeof value === "string") {
|
|
225
|
+
parsedValue = `"${truncate(value)}"`;
|
|
226
|
+
} else if (typeof value === "object") {
|
|
227
|
+
parsedValue = JSON.stringify(value, null, 1);
|
|
228
|
+
} else {
|
|
229
|
+
parsedValue = `${truncate(`${value}`)}`;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
key,
|
|
233
|
+
value: parsedValue,
|
|
234
|
+
};
|
|
235
|
+
}),
|
|
226
236
|
});
|
|
227
237
|
}
|
|
228
238
|
|
package/src/dev/local.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
1
2
|
import { fork } from "node:child_process";
|
|
2
3
|
import { realpathSync } from "node:fs";
|
|
3
|
-
import { writeFile } from "node:fs/promises";
|
|
4
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { npxImport } from "npx-import";
|
|
6
7
|
import { useState, useEffect, useRef } from "react";
|
|
@@ -8,7 +9,10 @@ import onExit from "signal-exit";
|
|
|
8
9
|
import { registerWorker } from "../dev-registry";
|
|
9
10
|
import useInspector from "../inspect";
|
|
10
11
|
import { logger } from "../logger";
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_MODULE_RULES,
|
|
14
|
+
ModuleTypeToRuleType,
|
|
15
|
+
} from "../module-collection";
|
|
12
16
|
import { getBasePath } from "../paths";
|
|
13
17
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
14
18
|
import type { Config } from "../config";
|
|
@@ -35,10 +39,6 @@ import type {
|
|
|
35
39
|
import type { MiniflareOptions } from "miniflare";
|
|
36
40
|
import type { ChildProcess } from "node:child_process";
|
|
37
41
|
|
|
38
|
-
// caching of the miniflare package
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
40
|
-
let Miniflare: typeof import("@miniflare/tre")["Miniflare"];
|
|
41
|
-
|
|
42
42
|
export interface LocalProps {
|
|
43
43
|
name: string | undefined;
|
|
44
44
|
bundle: EsbuildBundle | undefined;
|
|
@@ -76,10 +76,6 @@ export function Local(props: LocalProps) {
|
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function arrayToObject(values: string[] = []): Record<string, string> {
|
|
80
|
-
return Object.fromEntries(values.map((value) => [value, value]));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
79
|
function useLocalWorker({
|
|
84
80
|
name: workerName,
|
|
85
81
|
bundle,
|
|
@@ -206,35 +202,15 @@ function useLocalWorker({
|
|
|
206
202
|
|
|
207
203
|
if (experimentalLocal) {
|
|
208
204
|
// TODO: refactor setupMiniflareOptions so we don't need to parse here
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
// Miniflare 3 distinguishes between binding name and namespace/bucket
|
|
213
|
-
// IDs. For now, just use the same value as we did in Miniflare 2.
|
|
214
|
-
// TODO: use defined KV preview ID if any
|
|
215
|
-
kvNamespaces: arrayToObject(miniflare2Options.kvNamespaces),
|
|
216
|
-
r2Buckets: arrayToObject(miniflare2Options.r2Buckets),
|
|
217
|
-
// TODO: pass-through collected modules instead of getting Miniflare
|
|
218
|
-
// to collect them again
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
logger.log("⎔ Starting an experimental local server...");
|
|
222
|
-
|
|
223
|
-
if (Miniflare === undefined) {
|
|
224
|
-
({ Miniflare } = await npxImport<
|
|
225
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
226
|
-
typeof import("@miniflare/tre")
|
|
227
|
-
>("@miniflare/tre"));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const mf = new Miniflare(options);
|
|
205
|
+
const mf2Options: MiniflareOptions = JSON.parse(forkOptions[0]);
|
|
206
|
+
const mf = await setupExperimentalLocal(mf2Options, format, bundle);
|
|
207
|
+
await mf.ready;
|
|
231
208
|
experimentalLocalRef.current = mf;
|
|
232
|
-
removeSignalExitListener.current = onExit((
|
|
209
|
+
removeSignalExitListener.current = onExit(() => {
|
|
233
210
|
logger.log("⎔ Shutting down experimental local server.");
|
|
234
211
|
mf.dispose();
|
|
235
212
|
experimentalLocalRef.current = undefined;
|
|
236
213
|
});
|
|
237
|
-
await mf.ready;
|
|
238
214
|
return;
|
|
239
215
|
}
|
|
240
216
|
|
|
@@ -648,3 +624,63 @@ export function setupNodeOptions({
|
|
|
648
624
|
}
|
|
649
625
|
return nodeOptions;
|
|
650
626
|
}
|
|
627
|
+
|
|
628
|
+
// Caching of the `npx-import`ed `@miniflare/tre` package
|
|
629
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
630
|
+
let Miniflare: typeof import("@miniflare/tre").Miniflare;
|
|
631
|
+
|
|
632
|
+
function arrayToObject(values: string[] = []): Record<string, string> {
|
|
633
|
+
return Object.fromEntries(values.map((value) => [value, value]));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export async function setupExperimentalLocal(
|
|
637
|
+
mf2Options: MiniflareOptions,
|
|
638
|
+
format: CfScriptFormat,
|
|
639
|
+
bundle: EsbuildBundle
|
|
640
|
+
): Promise<Miniflare3Type> {
|
|
641
|
+
const options: Miniflare3Options = {
|
|
642
|
+
...mf2Options,
|
|
643
|
+
// Miniflare 3 distinguishes between binding name and namespace/bucket IDs.
|
|
644
|
+
// For now, just use the same value as we did in Miniflare 2.
|
|
645
|
+
// TODO: use defined KV preview ID if any
|
|
646
|
+
kvNamespaces: arrayToObject(mf2Options.kvNamespaces),
|
|
647
|
+
r2Buckets: arrayToObject(mf2Options.r2Buckets),
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
if (format === "modules") {
|
|
651
|
+
// Manually specify all modules from the bundle. If we didn't do this,
|
|
652
|
+
// Miniflare 3 would try collect them automatically again itself.
|
|
653
|
+
|
|
654
|
+
// Resolve entrypoint relative to the temporary directory, ensuring
|
|
655
|
+
// path doesn't start with `..`, which causes issues in `workerd`.
|
|
656
|
+
// Also ensures other modules with relative names can be resolved.
|
|
657
|
+
const root = path.dirname(bundle.path);
|
|
658
|
+
|
|
659
|
+
assert.strictEqual(bundle.type, "esm");
|
|
660
|
+
options.modules = [
|
|
661
|
+
// Entrypoint
|
|
662
|
+
{
|
|
663
|
+
type: "ESModule",
|
|
664
|
+
path: path.relative(root, bundle.path),
|
|
665
|
+
contents: await readFile(bundle.path, "utf-8"),
|
|
666
|
+
},
|
|
667
|
+
// Misc (WebAssembly, etc, ...)
|
|
668
|
+
...bundle.modules.map((module) => ({
|
|
669
|
+
type: ModuleTypeToRuleType[module.type ?? "esm"],
|
|
670
|
+
path: module.name,
|
|
671
|
+
contents: module.content,
|
|
672
|
+
})),
|
|
673
|
+
];
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
logger.log("⎔ Starting an experimental local server...");
|
|
677
|
+
|
|
678
|
+
if (Miniflare === undefined) {
|
|
679
|
+
({ Miniflare } = await npxImport<
|
|
680
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
681
|
+
typeof import("@miniflare/tre")
|
|
682
|
+
>("@miniflare/tre@next"));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return new Miniflare(options);
|
|
686
|
+
}
|
package/src/dev/start-server.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { logger } from "../logger";
|
|
|
17
17
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
18
18
|
import {
|
|
19
19
|
setupBindings,
|
|
20
|
+
setupExperimentalLocal,
|
|
20
21
|
setupMiniflareOptions,
|
|
21
22
|
setupNodeOptions,
|
|
22
23
|
} from "./local";
|
|
@@ -28,6 +29,8 @@ import type { Entry } from "../entry";
|
|
|
28
29
|
import type { DevProps, DirectorySyncResult } from "./dev";
|
|
29
30
|
import type { LocalProps } from "./local";
|
|
30
31
|
import type { EsbuildBundle } from "./use-esbuild";
|
|
32
|
+
import type { Miniflare as Miniflare3Type } from "@miniflare/tre";
|
|
33
|
+
import type { MiniflareOptions } from "miniflare";
|
|
31
34
|
|
|
32
35
|
import type { ChildProcess } from "node:child_process";
|
|
33
36
|
|
|
@@ -118,6 +121,7 @@ export async function startDevServer(
|
|
|
118
121
|
enablePagesAssetsServiceBinding: props.enablePagesAssetsServiceBinding,
|
|
119
122
|
usageModel: props.usageModel,
|
|
120
123
|
workerDefinitions,
|
|
124
|
+
experimentalLocal: props.experimentalLocal,
|
|
121
125
|
});
|
|
122
126
|
|
|
123
127
|
return {
|
|
@@ -252,8 +256,10 @@ export async function startLocalServer({
|
|
|
252
256
|
onReady,
|
|
253
257
|
logPrefix,
|
|
254
258
|
enablePagesAssetsServiceBinding,
|
|
259
|
+
experimentalLocal,
|
|
255
260
|
}: LocalProps) {
|
|
256
261
|
let local: ChildProcess | undefined;
|
|
262
|
+
let experimentalLocalRef: Miniflare3Type | undefined;
|
|
257
263
|
let removeSignalExitListener: (() => void) | undefined;
|
|
258
264
|
let inspectorUrl: string | undefined;
|
|
259
265
|
const setInspectorUrl = (url: string) => {
|
|
@@ -341,6 +347,21 @@ export async function startLocalServer({
|
|
|
341
347
|
enablePagesAssetsServiceBinding,
|
|
342
348
|
});
|
|
343
349
|
|
|
350
|
+
if (experimentalLocal) {
|
|
351
|
+
// TODO: refactor setupMiniflareOptions so we don't need to parse here
|
|
352
|
+
const mf2Options: MiniflareOptions = JSON.parse(forkOptions[0]);
|
|
353
|
+
const mf = await setupExperimentalLocal(mf2Options, format, bundle);
|
|
354
|
+
const runtimeURL = await mf.ready;
|
|
355
|
+
experimentalLocalRef = mf;
|
|
356
|
+
removeSignalExitListener = onExit((_code, _signal) => {
|
|
357
|
+
logger.log("⎔ Shutting down experimental local server.");
|
|
358
|
+
mf.dispose();
|
|
359
|
+
experimentalLocalRef = undefined;
|
|
360
|
+
});
|
|
361
|
+
onReady?.(runtimeURL.hostname, parseInt(runtimeURL.port ?? 8787));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
344
365
|
const nodeOptions = setupNodeOptions({ inspect, ip, inspectorPort });
|
|
345
366
|
logger.log("⎔ Starting a local server...");
|
|
346
367
|
|
|
@@ -435,9 +456,16 @@ export async function startLocalServer({
|
|
|
435
456
|
logger.log("⎔ Shutting down local server.");
|
|
436
457
|
local.kill();
|
|
437
458
|
local = undefined;
|
|
438
|
-
removeSignalExitListener && removeSignalExitListener();
|
|
439
|
-
removeSignalExitListener = undefined;
|
|
440
459
|
}
|
|
460
|
+
if (experimentalLocalRef) {
|
|
461
|
+
logger.log("⎔ Shutting down experimental local server.");
|
|
462
|
+
// Initialisation errors are also thrown asynchronously by dispose().
|
|
463
|
+
// The catch() above should've caught them though.
|
|
464
|
+
experimentalLocalRef?.dispose().catch(() => {});
|
|
465
|
+
experimentalLocalRef = undefined;
|
|
466
|
+
}
|
|
467
|
+
removeSignalExitListener?.();
|
|
468
|
+
removeSignalExitListener = undefined;
|
|
441
469
|
},
|
|
442
470
|
};
|
|
443
471
|
}
|
package/src/dev.tsx
CHANGED
|
@@ -561,10 +561,13 @@ export async function startApiDev(args: StartDevOptions) {
|
|
|
561
561
|
isWorkersSite: Boolean(args.site || configParam.site),
|
|
562
562
|
compatibilityDate: getDevCompatibilityDate(
|
|
563
563
|
config,
|
|
564
|
-
|
|
564
|
+
// Only `compatibilityDate` will be set when using `unstable_dev`
|
|
565
|
+
args["compatibility-date"] ?? args.compatibilityDate
|
|
565
566
|
),
|
|
566
567
|
compatibilityFlags:
|
|
567
|
-
args["compatibility-flags"]
|
|
568
|
+
args["compatibility-flags"] ??
|
|
569
|
+
args.compatibilityFlags ??
|
|
570
|
+
configParam.compatibility_flags,
|
|
568
571
|
usageModel: configParam.usage_model,
|
|
569
572
|
bindings: bindings,
|
|
570
573
|
crons: configParam.triggers.crons,
|
|
@@ -578,7 +581,7 @@ export async function startApiDev(args: StartDevOptions) {
|
|
|
578
581
|
firstPartyWorker: configParam.first_party_worker,
|
|
579
582
|
sendMetrics: configParam.send_metrics,
|
|
580
583
|
testScheduled: args.testScheduled,
|
|
581
|
-
experimentalLocal:
|
|
584
|
+
experimentalLocal: args.experimentalLocal,
|
|
582
585
|
});
|
|
583
586
|
}
|
|
584
587
|
|
package/src/init.ts
CHANGED
|
@@ -946,12 +946,16 @@ async function getWorkerConfig(
|
|
|
946
946
|
new Date().toISOString().substring(0, 10),
|
|
947
947
|
...routeOrRoutesToConfig,
|
|
948
948
|
usage_model: serviceEnvMetadata.script.usage_model,
|
|
949
|
-
|
|
950
|
-
{
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
949
|
+
...(durableObjectClassNames.length
|
|
950
|
+
? {
|
|
951
|
+
migrations: [
|
|
952
|
+
{
|
|
953
|
+
tag: serviceEnvMetadata.script.migration_tag,
|
|
954
|
+
new_classes: durableObjectClassNames,
|
|
955
|
+
},
|
|
956
|
+
],
|
|
957
|
+
}
|
|
958
|
+
: {}),
|
|
955
959
|
triggers: {
|
|
956
960
|
crons: cronTriggers.schedules.map((scheduled) => scheduled.cron),
|
|
957
961
|
},
|
package/src/module-collection.ts
CHANGED
|
@@ -7,6 +7,13 @@ import type { Config, ConfigModuleRuleType } from "./config";
|
|
|
7
7
|
import type { CfModule, CfModuleType, CfScriptFormat } from "./worker";
|
|
8
8
|
import type esbuild from "esbuild";
|
|
9
9
|
|
|
10
|
+
function flipObject<
|
|
11
|
+
K extends string | number | symbol,
|
|
12
|
+
V extends string | number | symbol
|
|
13
|
+
>(obj: Record<K, V>): Record<V, K> {
|
|
14
|
+
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [v, k]));
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
const RuleTypeToModuleType: Record<ConfigModuleRuleType, CfModuleType> = {
|
|
11
18
|
ESModule: "esm",
|
|
12
19
|
CommonJS: "commonjs",
|
|
@@ -15,6 +22,8 @@ const RuleTypeToModuleType: Record<ConfigModuleRuleType, CfModuleType> = {
|
|
|
15
22
|
Text: "text",
|
|
16
23
|
};
|
|
17
24
|
|
|
25
|
+
export const ModuleTypeToRuleType = flipObject(RuleTypeToModuleType);
|
|
26
|
+
|
|
18
27
|
// This is a combination of an esbuild plugin and a mutable array
|
|
19
28
|
// that we use to collect module references from source code.
|
|
20
29
|
// There will be modules that _shouldn't_ be inlined directly into
|
package/src/proxy.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createServer as createHttpServer } from "node:http";
|
|
2
2
|
import { connect } from "node:http2";
|
|
3
3
|
import { createServer as createHttpsServer } from "node:https";
|
|
4
|
+
import https from "node:https";
|
|
4
5
|
import { networkInterfaces } from "node:os";
|
|
5
|
-
import WebSocket from "faye-websocket";
|
|
6
6
|
import { createHttpTerminator } from "http-terminator";
|
|
7
7
|
import { useEffect, useRef, useState } from "react";
|
|
8
8
|
import serveStatic from "serve-static";
|
|
@@ -19,12 +19,7 @@ import type {
|
|
|
19
19
|
} from "node:http";
|
|
20
20
|
import type { ClientHttp2Session, ServerHttp2Stream } from "node:http2";
|
|
21
21
|
import type { Server as HttpsServer } from "node:https";
|
|
22
|
-
import type
|
|
23
|
-
|
|
24
|
-
interface IWebsocket extends ws {
|
|
25
|
-
// Pipe implements .on("message", ...)
|
|
26
|
-
pipe<T>(fn: T): IWebsocket;
|
|
27
|
-
}
|
|
22
|
+
import type { Duplex, Writable } from "node:stream";
|
|
28
23
|
|
|
29
24
|
/**
|
|
30
25
|
* `usePreviewServer` is a React hook that creates a local development
|
|
@@ -70,6 +65,26 @@ function rewriteRemoteHostToLocalHostInHeaders(
|
|
|
70
65
|
}
|
|
71
66
|
}
|
|
72
67
|
|
|
68
|
+
function writeHead(
|
|
69
|
+
socket: Writable,
|
|
70
|
+
res: Pick<
|
|
71
|
+
IncomingMessage,
|
|
72
|
+
"httpVersion" | "statusCode" | "statusMessage" | "headers"
|
|
73
|
+
>
|
|
74
|
+
) {
|
|
75
|
+
socket.write(
|
|
76
|
+
`HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage}\r\n`
|
|
77
|
+
);
|
|
78
|
+
for (const [key, values] of Object.entries(res.headers)) {
|
|
79
|
+
if (Array.isArray(values)) {
|
|
80
|
+
for (const value of values) socket.write(`${key}: ${value}\r\n`);
|
|
81
|
+
} else {
|
|
82
|
+
socket.write(`${key}: ${values}\r\n`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
socket.write("\r\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
73
88
|
type PreviewProxy = {
|
|
74
89
|
server: HttpServer | HttpsServer;
|
|
75
90
|
terminator: HttpTerminator;
|
|
@@ -273,27 +288,47 @@ export function usePreviewServer({
|
|
|
273
288
|
|
|
274
289
|
/** HTTP/1 -> WebSocket (over HTTP/1) */
|
|
275
290
|
const handleUpgrade = (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
291
|
+
originalMessage: IncomingMessage,
|
|
292
|
+
originalSocket: Duplex,
|
|
293
|
+
originalHead: Buffer
|
|
279
294
|
) => {
|
|
280
|
-
const { headers, url } =
|
|
295
|
+
const { headers, method, url } = originalMessage;
|
|
281
296
|
addCfPreviewTokenHeader(headers, previewToken.value);
|
|
282
297
|
headers["host"] = previewToken.host;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
298
|
+
|
|
299
|
+
if (originalHead?.byteLength) originalSocket.unshift(originalHead);
|
|
300
|
+
|
|
301
|
+
const runtimeRequest = https.request(
|
|
302
|
+
{
|
|
303
|
+
hostname: previewToken.host,
|
|
304
|
+
path: url,
|
|
305
|
+
method,
|
|
306
|
+
headers,
|
|
307
|
+
},
|
|
308
|
+
(runtimeResponse) => {
|
|
309
|
+
if (!(runtimeResponse as { upgrade?: boolean }).upgrade) {
|
|
310
|
+
writeHead(originalSocket, runtimeResponse);
|
|
311
|
+
runtimeResponse.pipe(originalSocket);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
runtimeRequest.on(
|
|
317
|
+
"upgrade",
|
|
318
|
+
(runtimeResponse, runtimeSocket, runtimeHead) => {
|
|
319
|
+
if (runtimeHead?.byteLength) runtimeSocket.unshift(runtimeHead);
|
|
320
|
+
writeHead(originalSocket, {
|
|
321
|
+
httpVersion: "1.1",
|
|
322
|
+
statusCode: 101,
|
|
323
|
+
statusMessage: "Switching Protocols",
|
|
324
|
+
headers: runtimeResponse.headers,
|
|
325
|
+
});
|
|
326
|
+
runtimeSocket.pipe(originalSocket).pipe(runtimeSocket);
|
|
327
|
+
}
|
|
328
|
+
);
|
|
329
|
+
originalMessage.pipe(runtimeRequest);
|
|
296
330
|
};
|
|
331
|
+
|
|
297
332
|
proxy.server.on("upgrade", handleUpgrade);
|
|
298
333
|
cleanupListeners.push(() => proxy.server.off("upgrade", handleUpgrade));
|
|
299
334
|
|
package/templates/new-worker.js
CHANGED