wrangler 2.0.23 → 2.0.26
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 +235 -47
- package/package.json +11 -6
- package/src/__tests__/configuration.test.ts +89 -17
- package/src/__tests__/dev.test.tsx +29 -4
- package/src/__tests__/generate.test.ts +93 -0
- package/src/__tests__/helpers/mock-cfetch.ts +87 -2
- package/src/__tests__/index.test.ts +10 -27
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +34 -1
- package/src/__tests__/kv.test.ts +2 -2
- package/src/__tests__/metrics.test.ts +5 -0
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/publish.test.ts +497 -254
- package/src/__tests__/r2.test.ts +173 -71
- package/src/__tests__/tail.test.ts +112 -42
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/whoami.test.tsx +60 -1
- package/src/api/dev.ts +7 -0
- package/src/bundle.ts +279 -44
- package/src/cfetch/internal.ts +73 -2
- package/src/config/config.ts +8 -3
- package/src/config/environment.ts +40 -8
- package/src/config/index.ts +13 -0
- package/src/config/validation.ts +102 -8
- package/src/create-worker-upload-form.ts +25 -0
- package/src/dev/dev.tsx +121 -28
- package/src/dev/local.tsx +88 -14
- package/src/dev/remote.tsx +39 -8
- 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 +107 -80
- package/src/generate.ts +112 -14
- package/src/index.tsx +212 -4
- package/src/init.ts +111 -38
- 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 +27 -16
- package/src/miniflare-cli/index.ts +124 -2
- package/src/module-collection.ts +3 -3
- package/src/pages/build.tsx +75 -41
- package/src/pages/constants.ts +5 -0
- package/src/pages/deployments.tsx +10 -10
- package/src/pages/dev.tsx +177 -52
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +122 -0
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +76 -23
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +38 -21
- package/src/publish.ts +126 -112
- package/src/r2.ts +81 -0
- package/src/tail/filters.ts +3 -1
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +43 -3
- 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-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
- package/templates/service-bindings-module-facade.js +54 -0
- package/templates/service-bindings-sw-facade.js +42 -0
- package/wrangler-dist/cli.d.ts +7 -0
- package/wrangler-dist/cli.js +40851 -15332
package/src/dev/local.tsx
CHANGED
|
@@ -4,11 +4,13 @@ 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";
|
|
12
14
|
import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
|
|
13
15
|
import type { AssetPaths } from "../sites";
|
|
14
16
|
import type { CfWorkerInit, CfScriptFormat } from "../worker";
|
|
@@ -21,9 +23,10 @@ interface LocalProps {
|
|
|
21
23
|
format: CfScriptFormat | undefined;
|
|
22
24
|
compatibilityDate: string;
|
|
23
25
|
compatibilityFlags: string[] | undefined;
|
|
26
|
+
usageModel: "bundled" | "unbound" | undefined;
|
|
24
27
|
bindings: CfWorkerInit["bindings"];
|
|
28
|
+
workerDefinitions: WorkerRegistry;
|
|
25
29
|
assetPaths: AssetPaths | undefined;
|
|
26
|
-
isWorkersSite: boolean;
|
|
27
30
|
port: number;
|
|
28
31
|
ip: string;
|
|
29
32
|
rules: Config["rules"];
|
|
@@ -56,9 +59,10 @@ function useLocalWorker({
|
|
|
56
59
|
format,
|
|
57
60
|
compatibilityDate,
|
|
58
61
|
compatibilityFlags,
|
|
62
|
+
usageModel,
|
|
59
63
|
bindings,
|
|
64
|
+
workerDefinitions,
|
|
60
65
|
assetPaths,
|
|
61
|
-
isWorkersSite,
|
|
62
66
|
port,
|
|
63
67
|
inspectorPort,
|
|
64
68
|
rules,
|
|
@@ -88,6 +92,27 @@ function useLocalWorker({
|
|
|
88
92
|
// so that it's persisted in the temp dir across a dev session
|
|
89
93
|
// even when we change source and reload
|
|
90
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
|
+
|
|
91
116
|
useEffect(() => {
|
|
92
117
|
const abortController = new AbortController();
|
|
93
118
|
async function startLocalWorker() {
|
|
@@ -100,12 +125,6 @@ function useLocalWorker({
|
|
|
100
125
|
abortSignal: abortController.signal,
|
|
101
126
|
});
|
|
102
127
|
|
|
103
|
-
if (bindings.services && bindings.services.length > 0) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
"⎔ Service bindings are not yet supported in local mode."
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
128
|
// In local mode, we want to copy all referenced modules into
|
|
110
129
|
// the output bundle directory before starting up
|
|
111
130
|
for (const module of bundle.modules) {
|
|
@@ -178,6 +197,15 @@ function useLocalWorker({
|
|
|
178
197
|
? `${localProtocol}://${localUpstream}`
|
|
179
198
|
: undefined;
|
|
180
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.
|
|
181
209
|
const options: MiniflareOptions = {
|
|
182
210
|
name: workerName,
|
|
183
211
|
port,
|
|
@@ -194,12 +222,37 @@ function useLocalWorker({
|
|
|
194
222
|
})),
|
|
195
223
|
compatibilityDate,
|
|
196
224
|
compatibilityFlags,
|
|
225
|
+
usageModel,
|
|
197
226
|
kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
|
|
198
227
|
r2Buckets: bindings.r2_buckets?.map((r2) => r2.binding),
|
|
199
228
|
durableObjects: Object.fromEntries(
|
|
200
|
-
(
|
|
201
|
-
|
|
202
|
-
|
|
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)
|
|
203
256
|
),
|
|
204
257
|
...(localPersistencePath
|
|
205
258
|
? {
|
|
@@ -273,8 +326,28 @@ function useLocalWorker({
|
|
|
273
326
|
stdio: "pipe",
|
|
274
327
|
}));
|
|
275
328
|
|
|
276
|
-
child.on("message", (
|
|
277
|
-
|
|
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
|
+
}
|
|
278
351
|
onReady?.();
|
|
279
352
|
}
|
|
280
353
|
});
|
|
@@ -352,12 +425,13 @@ function useLocalWorker({
|
|
|
352
425
|
bindings.r2_buckets,
|
|
353
426
|
bindings.vars,
|
|
354
427
|
bindings.services,
|
|
428
|
+
workerDefinitions,
|
|
355
429
|
compatibilityDate,
|
|
356
430
|
compatibilityFlags,
|
|
431
|
+
usageModel,
|
|
357
432
|
localPersistencePath,
|
|
358
433
|
liveReload,
|
|
359
434
|
assetPaths,
|
|
360
|
-
isWorkersSite,
|
|
361
435
|
rules,
|
|
362
436
|
bindings.wasm_modules,
|
|
363
437
|
bindings.text_blobs,
|
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[]>>();
|
|
@@ -78,6 +80,7 @@ export function Remote(props: {
|
|
|
78
80
|
host: props.host,
|
|
79
81
|
routes: props.routes,
|
|
80
82
|
onReady: props.onReady,
|
|
83
|
+
sendMetrics: props.sendMetrics,
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
usePreviewServer({
|
|
@@ -97,6 +100,7 @@ export function Remote(props: {
|
|
|
97
100
|
: undefined,
|
|
98
101
|
port: props.inspectorPort,
|
|
99
102
|
logToTerminal: true,
|
|
103
|
+
sourceMapPath: props.sourceMapPath,
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
const errorHandler = useErrorHandler();
|
|
@@ -161,6 +165,7 @@ export function useWorker(props: {
|
|
|
161
165
|
host: string | undefined;
|
|
162
166
|
routes: Route[] | undefined;
|
|
163
167
|
onReady: (() => void) | undefined;
|
|
168
|
+
sendMetrics: boolean | undefined;
|
|
164
169
|
}): CfPreviewToken | undefined {
|
|
165
170
|
const {
|
|
166
171
|
name,
|
|
@@ -202,6 +207,7 @@ export function useWorker(props: {
|
|
|
202
207
|
zone: props.zone,
|
|
203
208
|
host: props.host,
|
|
204
209
|
routes: props.routes,
|
|
210
|
+
sendMetrics: props.sendMetrics,
|
|
205
211
|
};
|
|
206
212
|
|
|
207
213
|
setSession(
|
|
@@ -230,6 +236,7 @@ export function useWorker(props: {
|
|
|
230
236
|
props.legacyEnv,
|
|
231
237
|
props.routes,
|
|
232
238
|
props.zone,
|
|
239
|
+
props.sendMetrics,
|
|
233
240
|
]);
|
|
234
241
|
|
|
235
242
|
// This effect uses the session to upload the worker and create a preview
|
|
@@ -320,17 +327,40 @@ export function useWorker(props: {
|
|
|
320
327
|
zone: props.zone,
|
|
321
328
|
host: props.host,
|
|
322
329
|
routes: props.routes,
|
|
330
|
+
sendMetrics: props.sendMetrics,
|
|
323
331
|
};
|
|
324
332
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
abortController.signal
|
|
332
|
-
)
|
|
333
|
+
const workerPreviewToken = await createWorkerPreview(
|
|
334
|
+
init,
|
|
335
|
+
workerAccount,
|
|
336
|
+
workerCtx,
|
|
337
|
+
session,
|
|
338
|
+
abortController.signal
|
|
333
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
|
+
|
|
334
364
|
onReady?.();
|
|
335
365
|
}
|
|
336
366
|
start().catch((err) => {
|
|
@@ -377,6 +407,7 @@ export function useWorker(props: {
|
|
|
377
407
|
props.routes,
|
|
378
408
|
session,
|
|
379
409
|
onReady,
|
|
410
|
+
props.sendMetrics,
|
|
380
411
|
]);
|
|
381
412
|
return token;
|
|
382
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
|
+
}
|