wrangler 2.0.29 → 2.1.0
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/miniflare-dist/index.mjs +1136 -372
- package/package.json +2 -2
- package/src/__tests__/helpers/mock-cfetch.ts +4 -4
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/index.test.ts +7 -7
- package/src/__tests__/init.test.ts +356 -5
- package/src/__tests__/middleware.test.ts +768 -0
- package/src/__tests__/pages.test.ts +11 -12
- package/src/__tests__/publish.test.ts +464 -438
- package/src/__tests__/tail.test.ts +47 -74
- package/src/api/dev.ts +23 -4
- package/src/bundle.ts +225 -1
- package/src/dev/dev.tsx +3 -1
- package/src/dev/local.tsx +2 -2
- package/src/dev/remote.tsx +6 -3
- package/src/dev/start-server.ts +11 -7
- package/src/dev/use-esbuild.ts +4 -0
- package/src/dev.tsx +6 -16
- package/src/dialogs.tsx +12 -0
- package/src/index.tsx +5 -4
- package/src/init.ts +286 -11
- package/src/miniflare-cli/assets.ts +130 -415
- package/src/miniflare-cli/index.ts +3 -1
- package/src/pages/dev.tsx +5 -1
- package/src/pages/hash.tsx +13 -0
- package/src/pages/upload.tsx +3 -18
- package/src/publish.ts +30 -4
- package/src/tail/filters.ts +1 -5
- package/src/tail/index.ts +6 -3
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +84 -0
- package/templates/middleware/loader-sw.ts +213 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +14 -0
- package/wrangler-dist/cli.js +65691 -65327
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ScheduledEvent,
|
|
15
15
|
AlarmEvent,
|
|
16
16
|
} from "../tail";
|
|
17
|
+
import type { RequestInit } from "undici";
|
|
17
18
|
import type WebSocket from "ws";
|
|
18
19
|
|
|
19
20
|
describe("tail", () => {
|
|
@@ -44,12 +45,12 @@ describe("tail", () => {
|
|
|
44
45
|
|
|
45
46
|
it("creates and then delete tails", async () => {
|
|
46
47
|
const api = mockWebsocketAPIs();
|
|
47
|
-
expect(api.requests.creation.
|
|
48
|
+
expect(api.requests.creation.length).toStrictEqual(0);
|
|
48
49
|
|
|
49
50
|
await runWrangler("tail test-worker");
|
|
50
51
|
|
|
51
52
|
await expect(api.ws.connected).resolves.toBeTruthy();
|
|
52
|
-
expect(api.requests.creation.
|
|
53
|
+
expect(api.requests.creation.length).toStrictEqual(1);
|
|
53
54
|
expect(api.requests.deletion.count).toStrictEqual(0);
|
|
54
55
|
|
|
55
56
|
api.ws.close();
|
|
@@ -57,7 +58,7 @@ describe("tail", () => {
|
|
|
57
58
|
});
|
|
58
59
|
it("should connect to the worker assigned to a given route", async () => {
|
|
59
60
|
const api = mockWebsocketAPIs();
|
|
60
|
-
expect(api.requests.creation.
|
|
61
|
+
expect(api.requests.creation.length).toStrictEqual(0);
|
|
61
62
|
|
|
62
63
|
mockGetZoneFromHostRequest("example.com", "test-zone");
|
|
63
64
|
mockCollectKnownRoutesRequest([
|
|
@@ -69,7 +70,7 @@ describe("tail", () => {
|
|
|
69
70
|
await runWrangler("tail example.com/*");
|
|
70
71
|
|
|
71
72
|
await expect(api.ws.connected).resolves.toBeTruthy();
|
|
72
|
-
expect(api.requests.creation.
|
|
73
|
+
expect(api.requests.creation.length).toStrictEqual(1);
|
|
73
74
|
expect(api.requests.deletion.count).toStrictEqual(0);
|
|
74
75
|
|
|
75
76
|
api.ws.close();
|
|
@@ -90,12 +91,12 @@ describe("tail", () => {
|
|
|
90
91
|
|
|
91
92
|
it("creates and then delete tails: legacy envs", async () => {
|
|
92
93
|
const api = mockWebsocketAPIs("some-env", true);
|
|
93
|
-
expect(api.requests.creation.
|
|
94
|
+
expect(api.requests.creation.length).toStrictEqual(0);
|
|
94
95
|
|
|
95
96
|
await runWrangler("tail test-worker --env some-env --legacy-env true");
|
|
96
97
|
|
|
97
98
|
await expect(api.ws.connected).resolves.toBeTruthy();
|
|
98
|
-
expect(api.requests.creation.
|
|
99
|
+
expect(api.requests.creation.length).toStrictEqual(1);
|
|
99
100
|
expect(api.requests.deletion.count).toStrictEqual(0);
|
|
100
101
|
|
|
101
102
|
api.ws.close();
|
|
@@ -104,12 +105,12 @@ describe("tail", () => {
|
|
|
104
105
|
|
|
105
106
|
it("creates and then delete tails: service envs", async () => {
|
|
106
107
|
const api = mockWebsocketAPIs("some-env");
|
|
107
|
-
expect(api.requests.creation.
|
|
108
|
+
expect(api.requests.creation.length).toStrictEqual(0);
|
|
108
109
|
|
|
109
110
|
await runWrangler("tail test-worker --env some-env --legacy-env false");
|
|
110
111
|
|
|
111
112
|
await expect(api.ws.connected).resolves.toBeTruthy();
|
|
112
|
-
expect(api.requests.creation.
|
|
113
|
+
expect(api.requests.creation.length).toStrictEqual(1);
|
|
113
114
|
expect(api.requests.deletion.count).toStrictEqual(0);
|
|
114
115
|
|
|
115
116
|
api.ws.close();
|
|
@@ -143,65 +144,57 @@ describe("tail", () => {
|
|
|
143
144
|
await expect(tooLow).rejects.toThrow();
|
|
144
145
|
|
|
145
146
|
await runWrangler("tail test-worker --sampling-rate 0.25");
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
|
|
147
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
148
|
+
`{"filters":[{"sampling_rate":0.25}]}`
|
|
149
|
+
);
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
it("sends single status filters", async () => {
|
|
152
153
|
const api = mockWebsocketAPIs();
|
|
153
154
|
await runWrangler("tail test-worker --status error");
|
|
154
|
-
|
|
155
|
-
{
|
|
156
|
-
|
|
155
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
156
|
+
`{"filters":[{"outcome":["exception","exceededCpu","exceededMemory","unknown"]}]}`
|
|
157
|
+
);
|
|
157
158
|
});
|
|
158
159
|
|
|
159
160
|
it("sends multiple status filters", async () => {
|
|
160
161
|
const api = mockWebsocketAPIs();
|
|
161
162
|
await runWrangler("tail test-worker --status error --status canceled");
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
"exception",
|
|
166
|
-
"exceededCpu",
|
|
167
|
-
"exceededMemory",
|
|
168
|
-
"unknown",
|
|
169
|
-
"canceled",
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
]);
|
|
163
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
164
|
+
`{"filters":[{"outcome":["exception","exceededCpu","exceededMemory","unknown","canceled"]}]}`
|
|
165
|
+
);
|
|
173
166
|
});
|
|
174
167
|
|
|
175
168
|
it("sends single HTTP method filters", async () => {
|
|
176
169
|
const api = mockWebsocketAPIs();
|
|
177
170
|
await runWrangler("tail test-worker --method POST");
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
|
|
171
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
172
|
+
`{"filters":[{"method":["POST"]}]}`
|
|
173
|
+
);
|
|
181
174
|
});
|
|
182
175
|
|
|
183
176
|
it("sends multiple HTTP method filters", async () => {
|
|
184
177
|
const api = mockWebsocketAPIs();
|
|
185
178
|
await runWrangler("tail test-worker --method POST --method GET");
|
|
186
|
-
|
|
187
|
-
{
|
|
188
|
-
|
|
179
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
180
|
+
`{"filters":[{"method":["POST","GET"]}]}`
|
|
181
|
+
);
|
|
189
182
|
});
|
|
190
183
|
|
|
191
184
|
it("sends header filters without a query", async () => {
|
|
192
185
|
const api = mockWebsocketAPIs();
|
|
193
186
|
await runWrangler("tail test-worker --header X-CUSTOM-HEADER");
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
|
|
187
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
188
|
+
`{"filters":[{"header":{"key":"X-CUSTOM-HEADER"}}]}`
|
|
189
|
+
);
|
|
197
190
|
});
|
|
198
191
|
|
|
199
192
|
it("sends header filters with a query", async () => {
|
|
200
193
|
const api = mockWebsocketAPIs();
|
|
201
194
|
await runWrangler("tail test-worker --header X-CUSTOM-HEADER:some-value");
|
|
202
|
-
|
|
203
|
-
{
|
|
204
|
-
|
|
195
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
196
|
+
`{"filters":[{"header":{"key":"X-CUSTOM-HEADER","query":"some-value"}}]}`
|
|
197
|
+
);
|
|
205
198
|
});
|
|
206
199
|
|
|
207
200
|
it("sends single IP filters", async () => {
|
|
@@ -209,9 +202,9 @@ describe("tail", () => {
|
|
|
209
202
|
const fakeIp = "192.0.2.1";
|
|
210
203
|
|
|
211
204
|
await runWrangler(`tail test-worker --ip ${fakeIp}`);
|
|
212
|
-
|
|
213
|
-
{
|
|
214
|
-
|
|
205
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
206
|
+
`{"filters":[{"client_ip":["${fakeIp}"]}]}`
|
|
207
|
+
);
|
|
215
208
|
});
|
|
216
209
|
|
|
217
210
|
it("sends multiple IP filters", async () => {
|
|
@@ -219,9 +212,9 @@ describe("tail", () => {
|
|
|
219
212
|
const fakeIp = "192.0.2.1";
|
|
220
213
|
|
|
221
214
|
await runWrangler(`tail test-worker --ip ${fakeIp} --ip self`);
|
|
222
|
-
|
|
223
|
-
{
|
|
224
|
-
|
|
215
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
216
|
+
`{"filters":[{"client_ip":["${fakeIp}","self"]}]}`
|
|
217
|
+
);
|
|
225
218
|
});
|
|
226
219
|
|
|
227
220
|
it("sends search filters", async () => {
|
|
@@ -229,9 +222,9 @@ describe("tail", () => {
|
|
|
229
222
|
const search = "filterMe";
|
|
230
223
|
|
|
231
224
|
await runWrangler(`tail test-worker --search ${search}`);
|
|
232
|
-
|
|
233
|
-
{
|
|
234
|
-
|
|
225
|
+
expect(api.requests.creation[0].body).toEqual(
|
|
226
|
+
`{"filters":[{"query":"${search}"}]}`
|
|
227
|
+
);
|
|
235
228
|
});
|
|
236
229
|
|
|
237
230
|
it("sends everything but the kitchen sink", async () => {
|
|
@@ -252,30 +245,10 @@ describe("tail", () => {
|
|
|
252
245
|
`--search ${query} ` +
|
|
253
246
|
`--debug`;
|
|
254
247
|
|
|
255
|
-
const expectedWebsocketMessage = {
|
|
256
|
-
filters: [
|
|
257
|
-
{ sampling_rate },
|
|
258
|
-
{
|
|
259
|
-
outcome: [
|
|
260
|
-
"ok",
|
|
261
|
-
"exception",
|
|
262
|
-
"exceededCpu",
|
|
263
|
-
"exceededMemory",
|
|
264
|
-
"unknown",
|
|
265
|
-
],
|
|
266
|
-
},
|
|
267
|
-
{ method },
|
|
268
|
-
{ header: { key: "X-HELLO", query: "world" } },
|
|
269
|
-
{ client_ip },
|
|
270
|
-
{ query },
|
|
271
|
-
],
|
|
272
|
-
debug: true,
|
|
273
|
-
};
|
|
248
|
+
const expectedWebsocketMessage = `{"filters":[{"sampling_rate":0.69},{"outcome":["ok","exception","exceededCpu","exceededMemory","unknown"]},{"method":["GET","POST","PUT"]},{"header":{"key":"X-HELLO","query":"world"}},{"client_ip":["192.0.2.1","self"]},{"query":"onlyTheseMessagesPlease"}]}`;
|
|
274
249
|
|
|
275
250
|
await runWrangler(`tail test-worker ${cliFilters}`);
|
|
276
|
-
|
|
277
|
-
expectedWebsocketMessage
|
|
278
|
-
);
|
|
251
|
+
expect(api.requests.creation[0].body).toEqual(expectedWebsocketMessage);
|
|
279
252
|
});
|
|
280
253
|
});
|
|
281
254
|
|
|
@@ -589,7 +562,7 @@ function deserializeToJson(message: WebSocket.RawData): string {
|
|
|
589
562
|
*/
|
|
590
563
|
type MockAPI = {
|
|
591
564
|
requests: {
|
|
592
|
-
creation:
|
|
565
|
+
creation: RequestInit[];
|
|
593
566
|
deletion: RequestCounter;
|
|
594
567
|
};
|
|
595
568
|
ws: MockWebSocket;
|
|
@@ -615,15 +588,15 @@ function mockCreateTailRequest(
|
|
|
615
588
|
websocketURL: string,
|
|
616
589
|
env?: string,
|
|
617
590
|
legacyEnv = false
|
|
618
|
-
):
|
|
619
|
-
const requests =
|
|
591
|
+
): RequestInit[] {
|
|
592
|
+
const requests: RequestInit[] = [];
|
|
620
593
|
const servicesOrScripts = env && !legacyEnv ? "services" : "scripts";
|
|
621
594
|
const environment = env && !legacyEnv ? "/environments/:envName" : "";
|
|
622
595
|
setMockResponse(
|
|
623
596
|
`/accounts/:accountId/workers/${servicesOrScripts}/:scriptName${environment}/tails`,
|
|
624
597
|
"POST",
|
|
625
|
-
([_url, accountId, scriptName, envName]) => {
|
|
626
|
-
requests.
|
|
598
|
+
([_url, accountId, scriptName, envName], req) => {
|
|
599
|
+
requests.push(req);
|
|
627
600
|
expect(accountId).toEqual("some-account-id");
|
|
628
601
|
expect(scriptName).toEqual(
|
|
629
602
|
legacyEnv && env ? `test-worker-${env}` : "test-worker"
|
|
@@ -710,7 +683,7 @@ function mockWebsocketAPIs(env?: string, legacyEnv = false): MockAPI {
|
|
|
710
683
|
const api: MockAPI = {
|
|
711
684
|
requests: {
|
|
712
685
|
deletion: { count: 0 },
|
|
713
|
-
creation:
|
|
686
|
+
creation: [],
|
|
714
687
|
},
|
|
715
688
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
716
689
|
ws: null!, // will be set in the `beforeEach()` below.
|
package/src/api/dev.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
1
2
|
import { startApiDev, startDev } from "../dev";
|
|
2
3
|
import { logger } from "../logger";
|
|
3
4
|
|
|
@@ -79,6 +80,8 @@ export async function unstable_dev(
|
|
|
79
80
|
`unstable_dev() is experimental\nunstable_dev()'s behaviour will likely change in future releases`
|
|
80
81
|
);
|
|
81
82
|
}
|
|
83
|
+
let readyPort: number;
|
|
84
|
+
let readyAddress: string;
|
|
82
85
|
//due to Pages adoption of unstable_dev, we can't *just* disable rebuilds and watching. instead, we'll have two versions of startDev, which will converge.
|
|
83
86
|
if (testMode) {
|
|
84
87
|
//in testMode, we can run multiple wranglers in parallel, but rebuilds might not work out of the box
|
|
@@ -94,16 +97,24 @@ export async function unstable_dev(
|
|
|
94
97
|
showInteractiveDevSession: false,
|
|
95
98
|
_: [],
|
|
96
99
|
$0: "",
|
|
100
|
+
port: options?.port ?? 0,
|
|
97
101
|
...options,
|
|
98
102
|
local: true,
|
|
99
|
-
onReady: () =>
|
|
103
|
+
onReady: (address, port) => {
|
|
104
|
+
readyPort = port;
|
|
105
|
+
readyAddress = address;
|
|
106
|
+
ready(devServer);
|
|
107
|
+
},
|
|
100
108
|
});
|
|
101
109
|
}).then((devServer) => {
|
|
102
110
|
// now that the inner promise has resolved, we can resolve the outer promise
|
|
103
111
|
// with an object that lets you fetch and stop the dev server
|
|
104
112
|
resolve({
|
|
105
113
|
stop: devServer.stop,
|
|
106
|
-
fetch:
|
|
114
|
+
fetch: async (init?: RequestInit) => {
|
|
115
|
+
const urlToFetch = `http://${readyAddress}:${readyPort}/`;
|
|
116
|
+
return await fetch(urlToFetch, init);
|
|
117
|
+
},
|
|
107
118
|
//no-op, does nothing in tests
|
|
108
119
|
waitUntilExit: async () => {
|
|
109
120
|
return;
|
|
@@ -113,6 +124,7 @@ export async function unstable_dev(
|
|
|
113
124
|
});
|
|
114
125
|
} else {
|
|
115
126
|
//outside of test mode, rebuilds work fine, but only one instance of wrangler will work at a time
|
|
127
|
+
|
|
116
128
|
return new Promise<UnstableDev>((resolve) => {
|
|
117
129
|
//lmao
|
|
118
130
|
return new Promise<Awaited<ReturnType<typeof startDev>>>((ready) => {
|
|
@@ -125,12 +137,19 @@ export async function unstable_dev(
|
|
|
125
137
|
$0: "",
|
|
126
138
|
...options,
|
|
127
139
|
local: true,
|
|
128
|
-
onReady: () =>
|
|
140
|
+
onReady: (address, port) => {
|
|
141
|
+
readyPort = port;
|
|
142
|
+
readyAddress = address;
|
|
143
|
+
ready(devServer);
|
|
144
|
+
},
|
|
129
145
|
});
|
|
130
146
|
}).then((devServer) => {
|
|
131
147
|
resolve({
|
|
132
148
|
stop: devServer.stop,
|
|
133
|
-
fetch:
|
|
149
|
+
fetch: async (init?: RequestInit) => {
|
|
150
|
+
const urlToFetch = `http://${readyAddress}:${readyPort}/`;
|
|
151
|
+
return await fetch(urlToFetch, init);
|
|
152
|
+
},
|
|
134
153
|
waitUntilExit: devServer.devReactElement.waitUntilExit,
|
|
135
154
|
});
|
|
136
155
|
});
|
package/src/bundle.ts
CHANGED
|
@@ -75,6 +75,7 @@ export async function bundleWorker(
|
|
|
75
75
|
services: Config["services"] | undefined;
|
|
76
76
|
workerDefinitions: WorkerRegistry | undefined;
|
|
77
77
|
firstPartyWorkerDevFacade: boolean | undefined;
|
|
78
|
+
targetConsumer: "dev" | "publish";
|
|
78
79
|
}
|
|
79
80
|
): Promise<BundleResult> {
|
|
80
81
|
const {
|
|
@@ -91,6 +92,7 @@ export async function bundleWorker(
|
|
|
91
92
|
workerDefinitions,
|
|
92
93
|
services,
|
|
93
94
|
firstPartyWorkerDevFacade,
|
|
95
|
+
targetConsumer,
|
|
94
96
|
} = options;
|
|
95
97
|
|
|
96
98
|
// We create a temporary directory for any oneoff files we
|
|
@@ -140,6 +142,19 @@ export async function bundleWorker(
|
|
|
140
142
|
// a new entry point, that we call "middleware" or "facades".
|
|
141
143
|
// Look at implementations of these functions to learn more.
|
|
142
144
|
|
|
145
|
+
// We also have middleware that uses a more "traditional" middleware stack,
|
|
146
|
+
// which is all loaded as one in a stack.
|
|
147
|
+
const middlewareToLoad: MiddlewareLoader[] = [
|
|
148
|
+
// {
|
|
149
|
+
// path: "templates/middleware/middleware-pretty-error.ts",
|
|
150
|
+
// publish: true,
|
|
151
|
+
// dev: false,
|
|
152
|
+
// },
|
|
153
|
+
// {
|
|
154
|
+
// path: "../templates/middleware/middleware-scheduled.ts",
|
|
155
|
+
// },
|
|
156
|
+
];
|
|
157
|
+
|
|
143
158
|
type MiddlewareFn = (arg0: Entry) => Promise<Entry>;
|
|
144
159
|
const middleware: (false | undefined | MiddlewareFn)[] = [
|
|
145
160
|
// serve static assets
|
|
@@ -173,6 +188,32 @@ export async function bundleWorker(
|
|
|
173
188
|
((currentEntry: Entry) => {
|
|
174
189
|
return applyFirstPartyWorkerDevFacade(currentEntry, tmpDir.path);
|
|
175
190
|
}),
|
|
191
|
+
|
|
192
|
+
// Middleware loader: to add middleware, we add the path to the middleware
|
|
193
|
+
// Currently for demonstration purposes we have two example middlewares
|
|
194
|
+
// Middlewares are togglable by changing the `publish` (default=false) and `dev` (default=true) options
|
|
195
|
+
// As we are not yet supporting user created middlewares yet, if no wrangler applied middleware
|
|
196
|
+
// are found, we will not load any middleware. We also need to check if there are middlewares compatible with
|
|
197
|
+
// the target consumer (dev / publish).
|
|
198
|
+
(middlewareToLoad.filter(
|
|
199
|
+
(m) =>
|
|
200
|
+
(m.publish && targetConsumer === "publish") ||
|
|
201
|
+
(m.dev !== false && targetConsumer === "dev")
|
|
202
|
+
).length > 0 ||
|
|
203
|
+
process.env.EXPERIMENTAL_MIDDLEWARE === "true") &&
|
|
204
|
+
((currentEntry: Entry) => {
|
|
205
|
+
return applyMiddlewareLoaderFacade(
|
|
206
|
+
currentEntry,
|
|
207
|
+
tmpDir.path,
|
|
208
|
+
middlewareToLoad.filter(
|
|
209
|
+
// We dynamically filter the middleware depending on where we are bundling for
|
|
210
|
+
(m) =>
|
|
211
|
+
(targetConsumer === "dev" && m.dev !== false) ||
|
|
212
|
+
(m.publish && targetConsumer === "publish")
|
|
213
|
+
),
|
|
214
|
+
moduleCollector.plugin
|
|
215
|
+
);
|
|
216
|
+
}),
|
|
176
217
|
].filter(Boolean);
|
|
177
218
|
|
|
178
219
|
let inputEntry = entry;
|
|
@@ -215,7 +256,11 @@ export async function bundleWorker(
|
|
|
215
256
|
".cjs": "jsx",
|
|
216
257
|
},
|
|
217
258
|
plugins: [
|
|
218
|
-
moduleCollector
|
|
259
|
+
// We run the moduleCollector plugin for service workers as part of the middleware loader
|
|
260
|
+
// so we only run here for modules or with no middleware to load
|
|
261
|
+
...(entry.format === "modules" || middlewareToLoad.length === 0
|
|
262
|
+
? [moduleCollector.plugin]
|
|
263
|
+
: []),
|
|
219
264
|
...(nodeCompat
|
|
220
265
|
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
221
266
|
: // we use checkForNodeBuiltinsPlugin to throw a nicer error
|
|
@@ -323,6 +368,185 @@ async function applyFormatDevErrorsFacade(
|
|
|
323
368
|
};
|
|
324
369
|
}
|
|
325
370
|
|
|
371
|
+
/**
|
|
372
|
+
* A facade that acts as a "middleware loader".
|
|
373
|
+
* Instead of needing to apply a facade for each individual middleware, this allows
|
|
374
|
+
* middleware to be written in a more traditional manner and then be applied all
|
|
375
|
+
* at once, requiring just two esbuild steps, rather than 1 per middleware.
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
interface MiddlewareLoader {
|
|
379
|
+
path: string;
|
|
380
|
+
// By default all middleware will run on dev, but will not be run when published
|
|
381
|
+
publish?: boolean;
|
|
382
|
+
dev?: boolean;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function applyMiddlewareLoaderFacade(
|
|
386
|
+
entry: Entry,
|
|
387
|
+
tmpDirPath: string,
|
|
388
|
+
middleware: MiddlewareLoader[], // a list of paths to middleware files
|
|
389
|
+
moduleCollectorPlugin: esbuild.Plugin
|
|
390
|
+
): Promise<Entry> {
|
|
391
|
+
// Firstly we need to insert the middleware array into the project,
|
|
392
|
+
// and then we load the middleware - this insertion and loading is
|
|
393
|
+
// different for each format.
|
|
394
|
+
|
|
395
|
+
// STEP 1: Insert the middleware
|
|
396
|
+
const targetPathInsertion = path.join(
|
|
397
|
+
tmpDirPath,
|
|
398
|
+
"middleware-insertion.entry.js"
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// We need to import each of the middlewares, so we need to generate a
|
|
402
|
+
// random, unique identifier that we can use for the import.
|
|
403
|
+
// Middlewares are required to be default exports so we can import to any name.
|
|
404
|
+
const middlewareIdentifiers = middleware.map(
|
|
405
|
+
(_, index) => `__MIDDLEWARE_${index}__`
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const dynamicFacadePath = path.join(
|
|
409
|
+
tmpDirPath,
|
|
410
|
+
"middleware-insertion-facade.js"
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
if (entry.format === "modules") {
|
|
414
|
+
// We use a facade to expose the required middleware alongside any user defined
|
|
415
|
+
// middleware on the worker object
|
|
416
|
+
|
|
417
|
+
const imports = middlewareIdentifiers
|
|
418
|
+
.map((m) => `import ${m} from "${m}";`)
|
|
419
|
+
.join("\n");
|
|
420
|
+
|
|
421
|
+
// write a file with all of the imports required
|
|
422
|
+
fs.writeFileSync(
|
|
423
|
+
dynamicFacadePath,
|
|
424
|
+
`import worker from "__ENTRY_POINT__";
|
|
425
|
+
${imports}
|
|
426
|
+
const facade = {
|
|
427
|
+
...worker,
|
|
428
|
+
middleware: [
|
|
429
|
+
${middlewareIdentifiers.join(",")}${middlewareIdentifiers.length > 0 ? "," : ""}
|
|
430
|
+
...(worker.middleware ? worker.middleware : []),
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
export * from "__ENTRY_POINT__";
|
|
434
|
+
export default facade;`
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
await esbuild.build({
|
|
438
|
+
entryPoints: [path.resolve(getBasePath(), dynamicFacadePath)],
|
|
439
|
+
bundle: true,
|
|
440
|
+
sourcemap: true,
|
|
441
|
+
format: "esm",
|
|
442
|
+
plugins: [
|
|
443
|
+
esbuildAliasExternalPlugin({
|
|
444
|
+
__ENTRY_POINT__: entry.file,
|
|
445
|
+
...Object.fromEntries(
|
|
446
|
+
middleware.map((val, index) => [
|
|
447
|
+
middlewareIdentifiers[index],
|
|
448
|
+
path.resolve(getBasePath(), val.path),
|
|
449
|
+
])
|
|
450
|
+
),
|
|
451
|
+
}),
|
|
452
|
+
],
|
|
453
|
+
outfile: targetPathInsertion,
|
|
454
|
+
});
|
|
455
|
+
} else {
|
|
456
|
+
// We handle service workers slightly differently as we have to overwrite
|
|
457
|
+
// the event listeners and reimplement them
|
|
458
|
+
|
|
459
|
+
await esbuild.build({
|
|
460
|
+
entryPoints: [entry.file],
|
|
461
|
+
bundle: true,
|
|
462
|
+
sourcemap: true,
|
|
463
|
+
define: {
|
|
464
|
+
"process.env.NODE_ENV": `"${process.env["NODE_ENV" + ""]}"`,
|
|
465
|
+
},
|
|
466
|
+
format: "esm",
|
|
467
|
+
outfile: targetPathInsertion,
|
|
468
|
+
plugins: [moduleCollectorPlugin],
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const imports = middlewareIdentifiers
|
|
472
|
+
.map(
|
|
473
|
+
(m, i) =>
|
|
474
|
+
`import ${m} from "${path.resolve(
|
|
475
|
+
getBasePath(),
|
|
476
|
+
middleware[i].path
|
|
477
|
+
)}";`
|
|
478
|
+
)
|
|
479
|
+
.join("\n");
|
|
480
|
+
|
|
481
|
+
// We add the new modules with imports and then register using the
|
|
482
|
+
// addMiddleware function (which gets rewritten in the next build step)
|
|
483
|
+
|
|
484
|
+
// We choose to run middleware inserted in wrangler before user inserted
|
|
485
|
+
// middleware in the stack
|
|
486
|
+
// To do this, we either need to execute the addMiddleware function first
|
|
487
|
+
// before any user middleware, or use a separate handling function.
|
|
488
|
+
// We choose to do the latter as to prepend, we would have to load the entire
|
|
489
|
+
// script into memory as a prepend function doesn't exist or work in the same
|
|
490
|
+
// way that an append function does.
|
|
491
|
+
|
|
492
|
+
fs.copyFileSync(targetPathInsertion, dynamicFacadePath);
|
|
493
|
+
fs.appendFileSync(
|
|
494
|
+
dynamicFacadePath,
|
|
495
|
+
`
|
|
496
|
+
${imports}
|
|
497
|
+
addMiddlewareInternal([${middlewareIdentifiers.join(",")}])
|
|
498
|
+
`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// STEP 2: Load the middleware
|
|
503
|
+
// We want to get the filename of the orginal entry point
|
|
504
|
+
let targetPathLoader = path.join(tmpDirPath, path.basename(entry.file));
|
|
505
|
+
if (path.extname(entry.file) === "") targetPathLoader += ".js";
|
|
506
|
+
|
|
507
|
+
const loaderPath =
|
|
508
|
+
entry.format === "modules"
|
|
509
|
+
? path.resolve(getBasePath(), "templates/middleware/loader-modules.ts")
|
|
510
|
+
: dynamicFacadePath;
|
|
511
|
+
|
|
512
|
+
await esbuild.build({
|
|
513
|
+
entryPoints: [loaderPath],
|
|
514
|
+
bundle: true,
|
|
515
|
+
sourcemap: true,
|
|
516
|
+
format: "esm",
|
|
517
|
+
...(entry.format === "service-worker"
|
|
518
|
+
? {
|
|
519
|
+
inject: [
|
|
520
|
+
path.resolve(getBasePath(), "templates/middleware/loader-sw.ts"),
|
|
521
|
+
],
|
|
522
|
+
define: {
|
|
523
|
+
addEventListener: "__facade_addEventListener__",
|
|
524
|
+
removeEventListener: "__facade_removeEventListener__",
|
|
525
|
+
dispatchEvent: "__facade_dispatchEvent__",
|
|
526
|
+
addMiddleware: "__facade_register__",
|
|
527
|
+
addMiddlewareInternal: "__facade_registerInternal__",
|
|
528
|
+
},
|
|
529
|
+
}
|
|
530
|
+
: {
|
|
531
|
+
plugins: [
|
|
532
|
+
esbuildAliasExternalPlugin({
|
|
533
|
+
__ENTRY_POINT__: targetPathInsertion,
|
|
534
|
+
"./common": path.resolve(
|
|
535
|
+
getBasePath(),
|
|
536
|
+
"templates/middleware/common.ts"
|
|
537
|
+
),
|
|
538
|
+
}),
|
|
539
|
+
],
|
|
540
|
+
}),
|
|
541
|
+
outfile: targetPathLoader,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
...entry,
|
|
546
|
+
file: targetPathLoader,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
326
550
|
/**
|
|
327
551
|
* A middleware that serves static assets from a worker.
|
|
328
552
|
* This powers --assets / config.assets
|
package/src/dev/dev.tsx
CHANGED
|
@@ -158,7 +158,7 @@ export type DevProps = {
|
|
|
158
158
|
inspect: boolean;
|
|
159
159
|
logLevel: "none" | "error" | "log" | "warn" | "debug" | undefined;
|
|
160
160
|
logPrefix?: string;
|
|
161
|
-
onReady: (() => void) | undefined;
|
|
161
|
+
onReady: ((ip: string, port: number) => void) | undefined;
|
|
162
162
|
showInteractiveDevSession: boolean | undefined;
|
|
163
163
|
forceLocal: boolean | undefined;
|
|
164
164
|
enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
|
|
@@ -257,6 +257,8 @@ function DevSession(props: DevSessionProps) {
|
|
|
257
257
|
services: props.bindings.services,
|
|
258
258
|
durableObjects: props.bindings.durable_objects || { bindings: [] },
|
|
259
259
|
firstPartyWorkerDevFacade: props.firstPartyWorker,
|
|
260
|
+
// Enable the bundling to know whether we are using dev or publish
|
|
261
|
+
targetConsumer: "dev",
|
|
260
262
|
});
|
|
261
263
|
|
|
262
264
|
return props.local ? (
|
package/src/dev/local.tsx
CHANGED
|
@@ -48,7 +48,7 @@ export interface LocalProps {
|
|
|
48
48
|
localProtocol: "http" | "https";
|
|
49
49
|
localUpstream: string | undefined;
|
|
50
50
|
inspect: boolean;
|
|
51
|
-
onReady: (() => void) | undefined;
|
|
51
|
+
onReady: ((ip: string, port: number) => void) | undefined;
|
|
52
52
|
logLevel: "none" | "error" | "log" | "warn" | "debug" | undefined;
|
|
53
53
|
logPrefix?: string;
|
|
54
54
|
enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
|
|
@@ -228,7 +228,7 @@ function useLocalWorker({
|
|
|
228
228
|
: {}),
|
|
229
229
|
});
|
|
230
230
|
}
|
|
231
|
-
onReady?.();
|
|
231
|
+
onReady?.(ip, message.mfPort);
|
|
232
232
|
}
|
|
233
233
|
});
|
|
234
234
|
|
package/src/dev/remote.tsx
CHANGED
|
@@ -54,7 +54,7 @@ export function Remote(props: {
|
|
|
54
54
|
zone: string | undefined;
|
|
55
55
|
host: string | undefined;
|
|
56
56
|
routes: Route[] | undefined;
|
|
57
|
-
onReady?: (() => void) | undefined;
|
|
57
|
+
onReady?: ((ip: string, port: number) => void) | undefined;
|
|
58
58
|
sourceMapPath: string | undefined;
|
|
59
59
|
sendMetrics: boolean | undefined;
|
|
60
60
|
}) {
|
|
@@ -81,6 +81,7 @@ export function Remote(props: {
|
|
|
81
81
|
routes: props.routes,
|
|
82
82
|
onReady: props.onReady,
|
|
83
83
|
sendMetrics: props.sendMetrics,
|
|
84
|
+
port: props.port,
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
usePreviewServer({
|
|
@@ -164,8 +165,9 @@ export function useWorker(props: {
|
|
|
164
165
|
zone: string | undefined;
|
|
165
166
|
host: string | undefined;
|
|
166
167
|
routes: Route[] | undefined;
|
|
167
|
-
onReady: (() => void) | undefined;
|
|
168
|
+
onReady: ((ip: string, port: number) => void) | undefined;
|
|
168
169
|
sendMetrics: boolean | undefined;
|
|
170
|
+
port: number;
|
|
169
171
|
}): CfPreviewToken | undefined {
|
|
170
172
|
const {
|
|
171
173
|
name,
|
|
@@ -363,7 +365,7 @@ export function useWorker(props: {
|
|
|
363
365
|
}
|
|
364
366
|
*/
|
|
365
367
|
|
|
366
|
-
onReady?.();
|
|
368
|
+
onReady?.(props.host || "localhost", props.port);
|
|
367
369
|
}
|
|
368
370
|
start().catch((err) => {
|
|
369
371
|
// we want to log the error, but not end the process
|
|
@@ -417,6 +419,7 @@ export function useWorker(props: {
|
|
|
417
419
|
session,
|
|
418
420
|
onReady,
|
|
419
421
|
props.sendMetrics,
|
|
422
|
+
props.port,
|
|
420
423
|
]);
|
|
421
424
|
return token;
|
|
422
425
|
}
|