wrangler 2.0.22 → 2.0.25
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 +20 -2
- package/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +643 -7
- package/package.json +17 -5
- package/src/__tests__/configuration.test.ts +89 -17
- package/src/__tests__/dev.test.tsx +121 -8
- package/src/__tests__/generate.test.ts +93 -0
- package/src/__tests__/helpers/mock-cfetch.ts +54 -2
- package/src/__tests__/index.test.ts +10 -27
- package/src/__tests__/jest.setup.ts +31 -1
- package/src/__tests__/kv.test.ts +82 -61
- package/src/__tests__/metrics.test.ts +5 -0
- package/src/__tests__/publish.test.ts +573 -254
- package/src/__tests__/r2.test.ts +173 -71
- package/src/__tests__/tail.test.ts +93 -39
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/version.test.ts +35 -0
- package/src/__tests__/whoami.test.tsx +60 -1
- package/src/api/dev.ts +49 -9
- package/src/bundle.ts +298 -37
- package/src/cfetch/internal.ts +34 -2
- package/src/config/config.ts +15 -3
- package/src/config/environment.ts +40 -8
- package/src/config/index.ts +13 -0
- package/src/config/validation.ts +111 -9
- package/src/create-worker-preview.ts +3 -1
- package/src/create-worker-upload-form.ts +25 -0
- package/src/dev/dev.tsx +145 -31
- package/src/dev/local.tsx +116 -24
- package/src/dev/remote.tsx +39 -12
- package/src/dev/use-esbuild.ts +28 -0
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +160 -0
- package/src/dev.tsx +148 -67
- package/src/generate.ts +112 -14
- package/src/index.tsx +252 -7
- package/src/inspect.ts +90 -5
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +2 -2
- package/src/miniflare-cli/assets.ts +546 -0
- package/src/miniflare-cli/index.ts +157 -6
- package/src/module-collection.ts +3 -3
- package/src/pages/build.tsx +36 -28
- package/src/pages/constants.ts +4 -0
- package/src/pages/deployments.tsx +10 -10
- package/src/pages/dev.tsx +155 -651
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +66 -0
- package/src/pages/functions/routes-consolidation.ts +29 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +125 -0
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +57 -15
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +38 -21
- package/src/publish.ts +139 -112
- package/src/r2.ts +81 -0
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +41 -3
- package/src/user/choose-account.tsx +20 -11
- package/src/user/user.tsx +20 -2
- package/src/whoami.tsx +79 -1
- package/src/worker.ts +12 -0
- package/templates/first-party-worker-module-facade.ts +18 -0
- package/templates/format-dev-errors.ts +32 -0
- package/templates/pages-shim.ts +9 -0
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
- package/templates/service-bindings-module-facade.js +51 -0
- package/templates/service-bindings-sw-facade.js +39 -0
- package/wrangler-dist/cli.d.ts +38 -3
- package/wrangler-dist/cli.js +45244 -25199
package/src/dev/local.tsx
CHANGED
|
@@ -4,11 +4,14 @@ import { writeFile } from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { useState, useEffect, useRef } from "react";
|
|
6
6
|
import onExit from "signal-exit";
|
|
7
|
+
import { registerWorker } from "../dev-registry";
|
|
7
8
|
import useInspector from "../inspect";
|
|
8
9
|
import { logger } from "../logger";
|
|
9
10
|
import { DEFAULT_MODULE_RULES } from "../module-collection";
|
|
10
11
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
11
12
|
import type { Config } from "../config";
|
|
13
|
+
import type { WorkerRegistry } from "../dev-registry";
|
|
14
|
+
import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
|
|
12
15
|
import type { AssetPaths } from "../sites";
|
|
13
16
|
import type { CfWorkerInit, CfScriptFormat } from "../worker";
|
|
14
17
|
import type { EsbuildBundle } from "./use-esbuild";
|
|
@@ -20,20 +23,24 @@ interface LocalProps {
|
|
|
20
23
|
format: CfScriptFormat | undefined;
|
|
21
24
|
compatibilityDate: string;
|
|
22
25
|
compatibilityFlags: string[] | undefined;
|
|
26
|
+
usageModel: "bundled" | "unbound" | undefined;
|
|
23
27
|
bindings: CfWorkerInit["bindings"];
|
|
28
|
+
workerDefinitions: WorkerRegistry;
|
|
24
29
|
assetPaths: AssetPaths | undefined;
|
|
25
|
-
isWorkersSite: boolean;
|
|
26
30
|
port: number;
|
|
27
31
|
ip: string;
|
|
28
32
|
rules: Config["rules"];
|
|
29
33
|
inspectorPort: number;
|
|
30
34
|
enableLocalPersistence: boolean;
|
|
35
|
+
liveReload: boolean;
|
|
31
36
|
crons: Config["triggers"]["crons"];
|
|
32
37
|
localProtocol: "http" | "https";
|
|
33
38
|
localUpstream: string | undefined;
|
|
34
39
|
inspect: boolean;
|
|
35
40
|
onReady: (() => void) | undefined;
|
|
36
41
|
logLevel: "none" | "error" | "log" | "warn" | "debug" | undefined;
|
|
42
|
+
logPrefix?: string;
|
|
43
|
+
enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
export function Local(props: LocalProps) {
|
|
@@ -52,12 +59,15 @@ function useLocalWorker({
|
|
|
52
59
|
format,
|
|
53
60
|
compatibilityDate,
|
|
54
61
|
compatibilityFlags,
|
|
62
|
+
usageModel,
|
|
55
63
|
bindings,
|
|
64
|
+
workerDefinitions,
|
|
56
65
|
assetPaths,
|
|
57
|
-
isWorkersSite,
|
|
58
66
|
port,
|
|
67
|
+
inspectorPort,
|
|
59
68
|
rules,
|
|
60
69
|
enableLocalPersistence,
|
|
70
|
+
liveReload,
|
|
61
71
|
ip,
|
|
62
72
|
crons,
|
|
63
73
|
localProtocol,
|
|
@@ -65,6 +75,8 @@ function useLocalWorker({
|
|
|
65
75
|
inspect,
|
|
66
76
|
onReady,
|
|
67
77
|
logLevel,
|
|
78
|
+
logPrefix,
|
|
79
|
+
enablePagesAssetsServiceBinding,
|
|
68
80
|
}: LocalProps) {
|
|
69
81
|
// TODO: pass vars via command line
|
|
70
82
|
const local = useRef<ChildProcess>();
|
|
@@ -80,6 +92,27 @@ function useLocalWorker({
|
|
|
80
92
|
// so that it's persisted in the temp dir across a dev session
|
|
81
93
|
// even when we change source and reload
|
|
82
94
|
null;
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (bindings.services && bindings.services.length > 0) {
|
|
98
|
+
logger.warn(
|
|
99
|
+
"⎔ Support for service bindings in local mode is experimental and may change."
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}, [bindings.services]);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const externalDurableObjects = (
|
|
106
|
+
bindings.durable_objects?.bindings || []
|
|
107
|
+
).filter((binding) => binding.script_name);
|
|
108
|
+
|
|
109
|
+
if (externalDurableObjects.length > 0) {
|
|
110
|
+
logger.warn(
|
|
111
|
+
"⎔ Support for external Durable Objects in local mode is experimental and may change."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}, [bindings.durable_objects?.bindings]);
|
|
115
|
+
|
|
83
116
|
useEffect(() => {
|
|
84
117
|
const abortController = new AbortController();
|
|
85
118
|
async function startLocalWorker() {
|
|
@@ -92,12 +125,6 @@ function useLocalWorker({
|
|
|
92
125
|
abortSignal: abortController.signal,
|
|
93
126
|
});
|
|
94
127
|
|
|
95
|
-
if (bindings.services && bindings.services.length > 0) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"⎔ Service bindings are not yet supported in local mode."
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
128
|
// In local mode, we want to copy all referenced modules into
|
|
102
129
|
// the output bundle directory before starting up
|
|
103
130
|
for (const module of bundle.modules) {
|
|
@@ -170,6 +197,15 @@ function useLocalWorker({
|
|
|
170
197
|
? `${localProtocol}://${localUpstream}`
|
|
171
198
|
: undefined;
|
|
172
199
|
|
|
200
|
+
const internalDurableObjects = (
|
|
201
|
+
bindings.durable_objects?.bindings || []
|
|
202
|
+
).filter((binding) => !binding.script_name);
|
|
203
|
+
const externalDurableObjects = (
|
|
204
|
+
bindings.durable_objects?.bindings || []
|
|
205
|
+
).filter((binding) => binding.script_name);
|
|
206
|
+
|
|
207
|
+
// TODO: This was already messy with the custom `disableLogs` and `logOptions`.
|
|
208
|
+
// It's now getting _really_ messy now with Pages ASSETS binding outside and the external Durable Objects inside.
|
|
173
209
|
const options: MiniflareOptions = {
|
|
174
210
|
name: workerName,
|
|
175
211
|
port,
|
|
@@ -186,12 +222,37 @@ function useLocalWorker({
|
|
|
186
222
|
})),
|
|
187
223
|
compatibilityDate,
|
|
188
224
|
compatibilityFlags,
|
|
225
|
+
usageModel,
|
|
189
226
|
kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
|
|
190
227
|
r2Buckets: bindings.r2_buckets?.map((r2) => r2.binding),
|
|
191
228
|
durableObjects: Object.fromEntries(
|
|
192
|
-
(
|
|
193
|
-
|
|
194
|
-
|
|
229
|
+
internalDurableObjects.map((binding) => [
|
|
230
|
+
binding.name,
|
|
231
|
+
binding.class_name,
|
|
232
|
+
])
|
|
233
|
+
),
|
|
234
|
+
externalDurableObjects: Object.fromEntries(
|
|
235
|
+
externalDurableObjects
|
|
236
|
+
.map((binding) => {
|
|
237
|
+
const service = workerDefinitions[binding.script_name as string];
|
|
238
|
+
if (!service) return [binding.name, undefined];
|
|
239
|
+
|
|
240
|
+
const name = service.durableObjects.find(
|
|
241
|
+
(durableObject) =>
|
|
242
|
+
durableObject.className === binding.class_name
|
|
243
|
+
)?.name;
|
|
244
|
+
if (!name) return [binding.name, undefined];
|
|
245
|
+
|
|
246
|
+
return [
|
|
247
|
+
binding.name,
|
|
248
|
+
{
|
|
249
|
+
name,
|
|
250
|
+
host: service.durableObjectsHost,
|
|
251
|
+
port: service.durableObjectsPort,
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
})
|
|
255
|
+
.filter(([_, details]) => !!details)
|
|
195
256
|
),
|
|
196
257
|
...(localPersistencePath
|
|
197
258
|
? {
|
|
@@ -212,6 +273,7 @@ function useLocalWorker({
|
|
|
212
273
|
r2Persist: true,
|
|
213
274
|
}),
|
|
214
275
|
|
|
276
|
+
liveReload,
|
|
215
277
|
sitePath: assetPaths?.assetDirectory
|
|
216
278
|
? path.join(assetPaths.baseDirectory, assetPaths.assetDirectory)
|
|
217
279
|
: undefined,
|
|
@@ -230,6 +292,7 @@ function useLocalWorker({
|
|
|
230
292
|
crons,
|
|
231
293
|
upstream,
|
|
232
294
|
disableLogs: logLevel === "none",
|
|
295
|
+
logOptions: logPrefix ? { prefix: logPrefix } : undefined,
|
|
233
296
|
};
|
|
234
297
|
|
|
235
298
|
// The path to the Miniflare CLI assumes that this file is being run from
|
|
@@ -248,19 +311,43 @@ function useLocalWorker({
|
|
|
248
311
|
// "--log=VERBOSE", // uncomment this to Miniflare to log "everything"!
|
|
249
312
|
];
|
|
250
313
|
if (inspect) {
|
|
251
|
-
nodeOptions.push("--inspect"); // start Miniflare listening for a debugger to attach
|
|
314
|
+
nodeOptions.push("--inspect=" + `${ip}:${inspectorPort}`); // start Miniflare listening for a debugger to attach
|
|
252
315
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
316
|
+
|
|
317
|
+
const forkOptions = [miniflareOptions];
|
|
318
|
+
|
|
319
|
+
if (enablePagesAssetsServiceBinding) {
|
|
320
|
+
forkOptions.push(JSON.stringify(enablePagesAssetsServiceBinding));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const child = (local.current = fork(miniflareCLIPath, forkOptions, {
|
|
324
|
+
cwd: path.dirname(scriptPath),
|
|
325
|
+
execArgv: nodeOptions,
|
|
326
|
+
stdio: "pipe",
|
|
327
|
+
}));
|
|
328
|
+
|
|
329
|
+
child.on("message", async (messageString) => {
|
|
330
|
+
const message = JSON.parse(messageString as string);
|
|
331
|
+
if (message.ready) {
|
|
332
|
+
// Let's register our presence in the dev registry
|
|
333
|
+
if (workerName) {
|
|
334
|
+
await registerWorker(workerName, {
|
|
335
|
+
protocol: localProtocol,
|
|
336
|
+
mode: "local",
|
|
337
|
+
port,
|
|
338
|
+
host: ip,
|
|
339
|
+
durableObjects: internalDurableObjects.map((binding) => ({
|
|
340
|
+
name: binding.name,
|
|
341
|
+
className: binding.class_name,
|
|
342
|
+
})),
|
|
343
|
+
...(message.durableObjectsPort
|
|
344
|
+
? {
|
|
345
|
+
durableObjectsHost: ip,
|
|
346
|
+
durableObjectsPort: message.durableObjectsPort,
|
|
347
|
+
}
|
|
348
|
+
: {}),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
264
351
|
onReady?.();
|
|
265
352
|
}
|
|
266
353
|
});
|
|
@@ -331,17 +418,20 @@ function useLocalWorker({
|
|
|
331
418
|
workerName,
|
|
332
419
|
format,
|
|
333
420
|
port,
|
|
421
|
+
inspectorPort,
|
|
334
422
|
ip,
|
|
335
423
|
bindings.durable_objects?.bindings,
|
|
336
424
|
bindings.kv_namespaces,
|
|
337
425
|
bindings.r2_buckets,
|
|
338
426
|
bindings.vars,
|
|
339
427
|
bindings.services,
|
|
428
|
+
workerDefinitions,
|
|
340
429
|
compatibilityDate,
|
|
341
430
|
compatibilityFlags,
|
|
431
|
+
usageModel,
|
|
342
432
|
localPersistencePath,
|
|
433
|
+
liveReload,
|
|
343
434
|
assetPaths,
|
|
344
|
-
isWorkersSite,
|
|
345
435
|
rules,
|
|
346
436
|
bindings.wasm_modules,
|
|
347
437
|
bindings.text_blobs,
|
|
@@ -351,7 +441,9 @@ function useLocalWorker({
|
|
|
351
441
|
localUpstream,
|
|
352
442
|
inspect,
|
|
353
443
|
logLevel,
|
|
444
|
+
logPrefix,
|
|
354
445
|
onReady,
|
|
446
|
+
enablePagesAssetsServiceBinding,
|
|
355
447
|
]);
|
|
356
448
|
return { inspectorUrl };
|
|
357
449
|
}
|
package/src/dev/remote.tsx
CHANGED
|
@@ -55,6 +55,8 @@ export function Remote(props: {
|
|
|
55
55
|
host: string | undefined;
|
|
56
56
|
routes: Route[] | undefined;
|
|
57
57
|
onReady?: (() => void) | undefined;
|
|
58
|
+
sourceMapPath: string | undefined;
|
|
59
|
+
sendMetrics: boolean | undefined;
|
|
58
60
|
}) {
|
|
59
61
|
const [accountId, setAccountId] = useState(props.accountId);
|
|
60
62
|
const accountChoicesRef = useRef<Promise<ChooseAccountItem[]>>();
|
|
@@ -69,7 +71,6 @@ export function Remote(props: {
|
|
|
69
71
|
bindings: props.bindings,
|
|
70
72
|
assetPaths: props.assetPaths,
|
|
71
73
|
isWorkersSite: props.isWorkersSite,
|
|
72
|
-
port: props.port,
|
|
73
74
|
compatibilityDate: props.compatibilityDate,
|
|
74
75
|
compatibilityFlags: props.compatibilityFlags,
|
|
75
76
|
usageModel: props.usageModel,
|
|
@@ -79,6 +80,7 @@ export function Remote(props: {
|
|
|
79
80
|
host: props.host,
|
|
80
81
|
routes: props.routes,
|
|
81
82
|
onReady: props.onReady,
|
|
83
|
+
sendMetrics: props.sendMetrics,
|
|
82
84
|
});
|
|
83
85
|
|
|
84
86
|
usePreviewServer({
|
|
@@ -98,6 +100,7 @@ export function Remote(props: {
|
|
|
98
100
|
: undefined,
|
|
99
101
|
port: props.inspectorPort,
|
|
100
102
|
logToTerminal: true,
|
|
103
|
+
sourceMapPath: props.sourceMapPath,
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
const errorHandler = useErrorHandler();
|
|
@@ -153,7 +156,6 @@ export function useWorker(props: {
|
|
|
153
156
|
bindings: CfWorkerInit["bindings"];
|
|
154
157
|
assetPaths: AssetPaths | undefined;
|
|
155
158
|
isWorkersSite: boolean;
|
|
156
|
-
port: number;
|
|
157
159
|
compatibilityDate: string | undefined;
|
|
158
160
|
compatibilityFlags: string[] | undefined;
|
|
159
161
|
usageModel: "bundled" | "unbound" | undefined;
|
|
@@ -163,6 +165,7 @@ export function useWorker(props: {
|
|
|
163
165
|
host: string | undefined;
|
|
164
166
|
routes: Route[] | undefined;
|
|
165
167
|
onReady: (() => void) | undefined;
|
|
168
|
+
sendMetrics: boolean | undefined;
|
|
166
169
|
}): CfPreviewToken | undefined {
|
|
167
170
|
const {
|
|
168
171
|
name,
|
|
@@ -175,7 +178,6 @@ export function useWorker(props: {
|
|
|
175
178
|
compatibilityDate,
|
|
176
179
|
compatibilityFlags,
|
|
177
180
|
usageModel,
|
|
178
|
-
port,
|
|
179
181
|
onReady,
|
|
180
182
|
} = props;
|
|
181
183
|
const [session, setSession] = useState<CfPreviewSession | undefined>();
|
|
@@ -205,6 +207,7 @@ export function useWorker(props: {
|
|
|
205
207
|
zone: props.zone,
|
|
206
208
|
host: props.host,
|
|
207
209
|
routes: props.routes,
|
|
210
|
+
sendMetrics: props.sendMetrics,
|
|
208
211
|
};
|
|
209
212
|
|
|
210
213
|
setSession(
|
|
@@ -233,6 +236,7 @@ export function useWorker(props: {
|
|
|
233
236
|
props.legacyEnv,
|
|
234
237
|
props.routes,
|
|
235
238
|
props.zone,
|
|
239
|
+
props.sendMetrics,
|
|
236
240
|
]);
|
|
237
241
|
|
|
238
242
|
// This effect uses the session to upload the worker and create a preview
|
|
@@ -323,17 +327,40 @@ export function useWorker(props: {
|
|
|
323
327
|
zone: props.zone,
|
|
324
328
|
host: props.host,
|
|
325
329
|
routes: props.routes,
|
|
330
|
+
sendMetrics: props.sendMetrics,
|
|
326
331
|
};
|
|
327
332
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
abortController.signal
|
|
335
|
-
)
|
|
333
|
+
const workerPreviewToken = await createWorkerPreview(
|
|
334
|
+
init,
|
|
335
|
+
workerAccount,
|
|
336
|
+
workerCtx,
|
|
337
|
+
session,
|
|
338
|
+
abortController.signal
|
|
336
339
|
);
|
|
340
|
+
|
|
341
|
+
setToken(workerPreviewToken);
|
|
342
|
+
|
|
343
|
+
// TODO: Once we get service bindings working in the
|
|
344
|
+
// edge preview server, we can define remote dev service bindings
|
|
345
|
+
// and you can uncomment this code.
|
|
346
|
+
// https://github.com/cloudflare/wrangler2/issues/1182
|
|
347
|
+
|
|
348
|
+
/*
|
|
349
|
+
if (name) {
|
|
350
|
+
await registerWorker(name, {
|
|
351
|
+
mode: "remote",
|
|
352
|
+
// upstream protocol is always https (https://github.com/cloudflare/wrangler2/issues/583)
|
|
353
|
+
protocol: "https",
|
|
354
|
+
port: undefined,
|
|
355
|
+
host: workerPreviewToken.host,
|
|
356
|
+
headers: {
|
|
357
|
+
"cf-workers-preview-token": workerPreviewToken.value,
|
|
358
|
+
host: workerPreviewToken.host,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
*/
|
|
363
|
+
|
|
337
364
|
onReady?.();
|
|
338
365
|
}
|
|
339
366
|
start().catch((err) => {
|
|
@@ -366,7 +393,6 @@ export function useWorker(props: {
|
|
|
366
393
|
bundle,
|
|
367
394
|
format,
|
|
368
395
|
accountId,
|
|
369
|
-
port,
|
|
370
396
|
assetPaths,
|
|
371
397
|
props.isWorkersSite,
|
|
372
398
|
compatibilityDate,
|
|
@@ -381,6 +407,7 @@ export function useWorker(props: {
|
|
|
381
407
|
props.routes,
|
|
382
408
|
session,
|
|
383
409
|
onReady,
|
|
410
|
+
props.sendMetrics,
|
|
384
411
|
]);
|
|
385
412
|
return token;
|
|
386
413
|
}
|
package/src/dev/use-esbuild.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { useState, useEffect } from "react";
|
|
|
5
5
|
import { bundleWorker } from "../bundle";
|
|
6
6
|
import { logger } from "../logger";
|
|
7
7
|
import type { Config } from "../config";
|
|
8
|
+
import type { WorkerRegistry } from "../dev-registry";
|
|
8
9
|
import type { Entry } from "../entry";
|
|
9
10
|
import type { CfModule } from "../worker";
|
|
10
11
|
import type { WatchMode } from "esbuild";
|
|
@@ -15,6 +16,7 @@ export type EsbuildBundle = {
|
|
|
15
16
|
entry: Entry;
|
|
16
17
|
type: "esm" | "commonjs";
|
|
17
18
|
modules: CfModule[];
|
|
19
|
+
sourceMapPath: string | undefined;
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export function useEsbuild({
|
|
@@ -23,24 +25,34 @@ export function useEsbuild({
|
|
|
23
25
|
jsxFactory,
|
|
24
26
|
jsxFragment,
|
|
25
27
|
rules,
|
|
28
|
+
assets,
|
|
26
29
|
serveAssetsFromWorker,
|
|
27
30
|
tsconfig,
|
|
28
31
|
minify,
|
|
29
32
|
nodeCompat,
|
|
30
33
|
define,
|
|
31
34
|
noBundle,
|
|
35
|
+
workerDefinitions,
|
|
36
|
+
services,
|
|
37
|
+
durableObjects,
|
|
38
|
+
firstPartyWorkerDevFacade,
|
|
32
39
|
}: {
|
|
33
40
|
entry: Entry;
|
|
34
41
|
destination: string | undefined;
|
|
35
42
|
jsxFactory: string | undefined;
|
|
36
43
|
jsxFragment: string | undefined;
|
|
37
44
|
rules: Config["rules"];
|
|
45
|
+
assets: Config["assets"];
|
|
38
46
|
define: Config["define"];
|
|
47
|
+
services: Config["services"];
|
|
39
48
|
serveAssetsFromWorker: boolean;
|
|
40
49
|
tsconfig: string | undefined;
|
|
41
50
|
minify: boolean | undefined;
|
|
42
51
|
nodeCompat: boolean | undefined;
|
|
43
52
|
noBundle: boolean;
|
|
53
|
+
workerDefinitions: WorkerRegistry;
|
|
54
|
+
durableObjects: Config["durable_objects"];
|
|
55
|
+
firstPartyWorkerDevFacade: boolean | undefined;
|
|
44
56
|
}): EsbuildBundle | undefined {
|
|
45
57
|
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
46
58
|
const { exit } = useApp();
|
|
@@ -76,12 +88,14 @@ export function useEsbuild({
|
|
|
76
88
|
bundleType,
|
|
77
89
|
modules,
|
|
78
90
|
stop,
|
|
91
|
+
sourceMapPath,
|
|
79
92
|
}: Awaited<ReturnType<typeof bundleWorker>> = noBundle
|
|
80
93
|
? {
|
|
81
94
|
modules: [],
|
|
82
95
|
resolvedEntryPointPath: entry.file,
|
|
83
96
|
bundleType: entry.format === "modules" ? "esm" : "commonjs",
|
|
84
97
|
stop: undefined,
|
|
98
|
+
sourceMapPath: undefined,
|
|
85
99
|
}
|
|
86
100
|
: await bundleWorker(entry, destination, {
|
|
87
101
|
serveAssetsFromWorker,
|
|
@@ -94,6 +108,14 @@ export function useEsbuild({
|
|
|
94
108
|
nodeCompat,
|
|
95
109
|
define,
|
|
96
110
|
checkFetch: true,
|
|
111
|
+
assets: assets && {
|
|
112
|
+
...assets,
|
|
113
|
+
// disable the cache in dev
|
|
114
|
+
bypassCache: true,
|
|
115
|
+
},
|
|
116
|
+
workerDefinitions,
|
|
117
|
+
services,
|
|
118
|
+
firstPartyWorkerDevFacade,
|
|
97
119
|
});
|
|
98
120
|
|
|
99
121
|
// Capture the `stop()` method to use as the `useEffect()` destructor.
|
|
@@ -119,6 +141,7 @@ export function useEsbuild({
|
|
|
119
141
|
path: resolvedEntryPointPath,
|
|
120
142
|
type: bundleType,
|
|
121
143
|
modules,
|
|
144
|
+
sourceMapPath,
|
|
122
145
|
});
|
|
123
146
|
}
|
|
124
147
|
|
|
@@ -145,6 +168,11 @@ export function useEsbuild({
|
|
|
145
168
|
minify,
|
|
146
169
|
nodeCompat,
|
|
147
170
|
define,
|
|
171
|
+
assets,
|
|
172
|
+
services,
|
|
173
|
+
durableObjects,
|
|
174
|
+
workerDefinitions,
|
|
175
|
+
firstPartyWorkerDevFacade,
|
|
148
176
|
]);
|
|
149
177
|
return bundle;
|
|
150
178
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DevProps } from "./dev";
|
|
2
|
+
|
|
3
|
+
export function validateDevProps(props: DevProps) {
|
|
4
|
+
if (
|
|
5
|
+
!props.isWorkersSite &&
|
|
6
|
+
props.assetPaths &&
|
|
7
|
+
props.entry.format === "service-worker"
|
|
8
|
+
) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"You cannot use the service-worker format with an `assets` directory yet. For information on how to migrate to the module-worker format, see: https://developers.cloudflare.com/workers/learning/migrating-to-module-workers/"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (props.bindings.wasm_modules && props.entry.format === "modules") {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (props.bindings.text_blobs && props.entry.format === "modules") {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (props.bindings.data_blobs && props.entry.format === "modules") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import http from "http";
|
|
2
|
+
import net from "net";
|
|
3
|
+
import bodyParser from "body-parser";
|
|
4
|
+
import express from "express";
|
|
5
|
+
import { createHttpTerminator } from "http-terminator";
|
|
6
|
+
import { fetch } from "undici";
|
|
7
|
+
import { logger } from "./logger";
|
|
8
|
+
import type { Server } from "http";
|
|
9
|
+
import type { HttpTerminator } from "http-terminator";
|
|
10
|
+
|
|
11
|
+
const DEV_REGISTRY_PORT = "6284";
|
|
12
|
+
const DEV_REGISTRY_HOST = `http://localhost:${DEV_REGISTRY_PORT}`;
|
|
13
|
+
|
|
14
|
+
let server: Server;
|
|
15
|
+
let terminator: HttpTerminator;
|
|
16
|
+
|
|
17
|
+
export type WorkerRegistry = Record<string, WorkerDefinition>;
|
|
18
|
+
|
|
19
|
+
type WorkerDefinition = {
|
|
20
|
+
port: number | undefined;
|
|
21
|
+
protocol: "http" | "https" | undefined;
|
|
22
|
+
host: string | undefined;
|
|
23
|
+
mode: "local" | "remote";
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
durableObjects: { name: string; className: string }[];
|
|
26
|
+
durableObjectsHost?: string;
|
|
27
|
+
durableObjectsPort?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A helper function to check whether our service registry is already running
|
|
32
|
+
*/
|
|
33
|
+
async function isPortAvailable() {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const netServer = net
|
|
36
|
+
.createServer()
|
|
37
|
+
.once("error", (err) => {
|
|
38
|
+
netServer.close();
|
|
39
|
+
if ((err as unknown as { code: string }).code === "EADDRINUSE") {
|
|
40
|
+
resolve(false);
|
|
41
|
+
} else {
|
|
42
|
+
reject(err);
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.once("listening", () => {
|
|
46
|
+
netServer.close();
|
|
47
|
+
resolve(true);
|
|
48
|
+
});
|
|
49
|
+
netServer.listen(DEV_REGISTRY_PORT);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const jsonBodyParser = bodyParser.json();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Start the service registry. It's a simple server
|
|
57
|
+
* that exposes endpoints for registering and unregistering
|
|
58
|
+
* services, as well as getting the state of the registry.
|
|
59
|
+
*/
|
|
60
|
+
export async function startWorkerRegistry() {
|
|
61
|
+
if ((await isPortAvailable()) && !server) {
|
|
62
|
+
const app = express();
|
|
63
|
+
|
|
64
|
+
let workers: WorkerRegistry = {};
|
|
65
|
+
app
|
|
66
|
+
.get("/workers", async (req, res) => {
|
|
67
|
+
res.json(workers);
|
|
68
|
+
})
|
|
69
|
+
.post("/workers/:workerId", jsonBodyParser, async (req, res) => {
|
|
70
|
+
workers[req.params.workerId] = req.body;
|
|
71
|
+
res.json(null);
|
|
72
|
+
})
|
|
73
|
+
.delete(`/workers/:workerId`, async (req, res) => {
|
|
74
|
+
delete workers[req.params.workerId];
|
|
75
|
+
res.json(null);
|
|
76
|
+
})
|
|
77
|
+
.delete("/workers", async (req, res) => {
|
|
78
|
+
workers = {};
|
|
79
|
+
res.json(null);
|
|
80
|
+
});
|
|
81
|
+
server = http.createServer(app);
|
|
82
|
+
terminator = createHttpTerminator({ server });
|
|
83
|
+
server.listen(DEV_REGISTRY_PORT);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Stop the service registry.
|
|
89
|
+
*/
|
|
90
|
+
export async function stopWorkerRegistry() {
|
|
91
|
+
await terminator?.terminate();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Register a worker in the registry.
|
|
96
|
+
*/
|
|
97
|
+
export async function registerWorker(
|
|
98
|
+
name: string,
|
|
99
|
+
definition: WorkerDefinition
|
|
100
|
+
) {
|
|
101
|
+
try {
|
|
102
|
+
return await fetch(`${DEV_REGISTRY_HOST}/workers/${name}`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify(definition),
|
|
108
|
+
});
|
|
109
|
+
} catch (e) {
|
|
110
|
+
if (
|
|
111
|
+
!["ECONNRESET", "ECONNREFUSED"].includes(
|
|
112
|
+
(e as unknown as { cause?: { code?: string } }).cause?.code || "___"
|
|
113
|
+
)
|
|
114
|
+
) {
|
|
115
|
+
logger.error("Failed to register worker in local service registry", e);
|
|
116
|
+
} else {
|
|
117
|
+
logger.debug("Failed to register worker in local service registry", e);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Unregister a worker from the registry.
|
|
124
|
+
*/
|
|
125
|
+
export async function unregisterWorker(name: string) {
|
|
126
|
+
try {
|
|
127
|
+
await fetch(`${DEV_REGISTRY_HOST}/workers/${name}`, {
|
|
128
|
+
method: "DELETE",
|
|
129
|
+
});
|
|
130
|
+
} catch (e) {
|
|
131
|
+
if (
|
|
132
|
+
!["ECONNRESET", "ECONNREFUSED"].includes(
|
|
133
|
+
(e as unknown as { cause?: { code?: string } }).cause?.code || "___"
|
|
134
|
+
)
|
|
135
|
+
) {
|
|
136
|
+
throw e;
|
|
137
|
+
// logger.error("failed to unregister worker", e);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get the state of the service registry.
|
|
144
|
+
*/
|
|
145
|
+
export async function getRegisteredWorkers(): Promise<
|
|
146
|
+
WorkerRegistry | undefined
|
|
147
|
+
> {
|
|
148
|
+
try {
|
|
149
|
+
const response = await fetch(`${DEV_REGISTRY_HOST}/workers`);
|
|
150
|
+
return (await response.json()) as WorkerRegistry;
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (
|
|
153
|
+
!["ECONNRESET", "ECONNREFUSED"].includes(
|
|
154
|
+
(e as unknown as { cause?: { code?: string } }).cause?.code || "___"
|
|
155
|
+
)
|
|
156
|
+
) {
|
|
157
|
+
throw e;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|