wrangler 0.0.0-e2f9bb2 → 0.0.0-e3278fa
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/Cloudflare_CA.pem +18 -0
- package/README.md +3 -1
- package/bin/wrangler.js +36 -4
- package/package.json +12 -7
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +14 -1
- package/pages/functions/template-plugin.ts +3 -2
- package/src/__tests__/configuration.test.ts +370 -87
- package/src/__tests__/dev.test.tsx +298 -77
- package/src/__tests__/helpers/mock-dialogs.ts +43 -2
- package/src/__tests__/index.test.ts +79 -72
- package/src/__tests__/init.test.ts +718 -123
- package/src/__tests__/jest.setup.ts +8 -0
- package/src/__tests__/kv.test.ts +173 -146
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +478 -53
- package/src/__tests__/parse.test.ts +8 -0
- package/src/__tests__/publish.test.ts +1094 -215
- package/src/__tests__/r2.test.ts +55 -36
- package/src/__tests__/secret.test.ts +39 -4
- package/src/__tests__/whoami.test.tsx +34 -0
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +34 -6
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +23 -18
- package/src/config/config.ts +2 -2
- package/src/config/environment.ts +40 -14
- package/src/config/index.ts +162 -0
- package/src/config/validation-helpers.ts +10 -1
- package/src/config/validation.ts +180 -65
- package/src/create-worker-preview.ts +32 -22
- package/src/create-worker-upload-form.ts +22 -8
- package/src/dev/dev.tsx +12 -18
- package/src/dev/local.tsx +10 -0
- package/src/dev/remote.tsx +40 -16
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/entry.ts +64 -9
- package/src/index.tsx +548 -317
- package/src/inspect.ts +39 -0
- package/src/kv.ts +114 -25
- package/src/open-in-browser.ts +5 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +434 -207
- package/src/parse.ts +23 -4
- package/src/proxy.ts +59 -35
- package/src/publish.ts +374 -127
- package/src/sites.tsx +61 -28
- package/src/user.tsx +23 -2
- package/src/whoami.tsx +3 -2
- package/src/worker.ts +10 -1
- package/src/zones.ts +73 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/templates/static-asset-facade.js +0 -4
- package/wrangler-dist/cli.js +96965 -83414
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
|
@@ -32,20 +32,24 @@ export interface WorkerMetadata {
|
|
|
32
32
|
compatibility_flags?: string[];
|
|
33
33
|
usage_model?: "bundled" | "unbound";
|
|
34
34
|
migrations?: CfDurableObjectMigrations;
|
|
35
|
+
// If you add any new binding types here, also add it to safeBindings
|
|
36
|
+
// under validateUnsafeBinding in config/validation.ts
|
|
35
37
|
bindings: (
|
|
36
|
-
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
37
38
|
| { type: "plain_text"; name: string; text: string }
|
|
38
39
|
| { type: "json"; name: string; json: unknown }
|
|
39
40
|
| { type: "wasm_module"; name: string; part: string }
|
|
40
41
|
| { type: "text_blob"; name: string; part: string }
|
|
41
42
|
| { type: "data_blob"; name: string; part: string }
|
|
43
|
+
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
42
44
|
| {
|
|
43
45
|
type: "durable_object_namespace";
|
|
44
46
|
name: string;
|
|
45
47
|
class_name: string;
|
|
46
48
|
script_name?: string;
|
|
49
|
+
environment?: string;
|
|
47
50
|
}
|
|
48
51
|
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
52
|
+
| { type: "service"; name: string; service: string; environment?: string }
|
|
49
53
|
)[];
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -67,6 +71,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
67
71
|
|
|
68
72
|
const metadataBindings: WorkerMetadata["bindings"] = [];
|
|
69
73
|
|
|
74
|
+
Object.entries(bindings.vars || {})?.forEach(([key, value]) => {
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
metadataBindings.push({ name: key, type: "plain_text", text: value });
|
|
77
|
+
} else {
|
|
78
|
+
metadataBindings.push({ name: key, type: "json", json: value });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
70
82
|
bindings.kv_namespaces?.forEach(({ id, binding }) => {
|
|
71
83
|
metadataBindings.push({
|
|
72
84
|
name: binding,
|
|
@@ -76,12 +88,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
76
88
|
});
|
|
77
89
|
|
|
78
90
|
bindings.durable_objects?.bindings.forEach(
|
|
79
|
-
({ name, class_name, script_name }) => {
|
|
91
|
+
({ name, class_name, script_name, environment }) => {
|
|
80
92
|
metadataBindings.push({
|
|
81
93
|
name,
|
|
82
94
|
type: "durable_object_namespace",
|
|
83
95
|
class_name: class_name,
|
|
84
96
|
...(script_name && { script_name }),
|
|
97
|
+
...(environment && { environment }),
|
|
85
98
|
});
|
|
86
99
|
}
|
|
87
100
|
);
|
|
@@ -94,12 +107,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
94
107
|
});
|
|
95
108
|
});
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
bindings.services?.forEach(({ binding, service, environment }) => {
|
|
111
|
+
metadataBindings.push({
|
|
112
|
+
name: binding,
|
|
113
|
+
type: "service",
|
|
114
|
+
service,
|
|
115
|
+
...(environment && { environment }),
|
|
116
|
+
});
|
|
103
117
|
});
|
|
104
118
|
|
|
105
119
|
for (const [name, filePath] of Object.entries(bindings.wasm_modules || {})) {
|
package/src/dev/dev.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
import { watch } from "chokidar";
|
|
3
4
|
import clipboardy from "clipboardy";
|
|
4
5
|
import commandExists from "command-exists";
|
|
@@ -9,9 +10,9 @@ import onExit from "signal-exit";
|
|
|
9
10
|
import tmp from "tmp-promise";
|
|
10
11
|
import { fetch } from "undici";
|
|
11
12
|
import { runCustomBuild } from "../entry";
|
|
13
|
+
import { openInspector } from "../inspect";
|
|
12
14
|
import { logger } from "../logger";
|
|
13
15
|
import openInBrowser from "../open-in-browser";
|
|
14
|
-
import { getAPIToken } from "../user";
|
|
15
16
|
import { Local } from "./local";
|
|
16
17
|
import { Remote } from "./remote";
|
|
17
18
|
import { useEsbuild } from "./use-esbuild";
|
|
@@ -52,16 +53,11 @@ export type DevProps = {
|
|
|
52
53
|
};
|
|
53
54
|
env: string | undefined;
|
|
54
55
|
legacyEnv: boolean;
|
|
55
|
-
zone:
|
|
56
|
-
|
|
57
|
-
id: string;
|
|
58
|
-
host: string;
|
|
59
|
-
}
|
|
60
|
-
| undefined;
|
|
56
|
+
zone: string | undefined;
|
|
57
|
+
host: string | undefined;
|
|
61
58
|
};
|
|
62
59
|
|
|
63
60
|
export function DevImplementation(props: DevProps): JSX.Element {
|
|
64
|
-
const apiToken = props.initialMode === "remote" ? getAPIToken() : undefined;
|
|
65
61
|
const directory = useTmpDir();
|
|
66
62
|
|
|
67
63
|
useCustomBuild(props.entry, props.build);
|
|
@@ -106,19 +102,17 @@ export function DevImplementation(props: DevProps): JSX.Element {
|
|
|
106
102
|
// only load the UI if we're running in a supported environment
|
|
107
103
|
const { isRawModeSupported } = useStdin();
|
|
108
104
|
return isRawModeSupported ? (
|
|
109
|
-
<InteractiveDevSession {...props} bundle={bundle}
|
|
105
|
+
<InteractiveDevSession {...props} bundle={bundle} />
|
|
110
106
|
) : (
|
|
111
107
|
<DevSession
|
|
112
108
|
{...props}
|
|
113
109
|
bundle={bundle}
|
|
114
|
-
apiToken={apiToken}
|
|
115
110
|
local={props.initialMode === "local"}
|
|
116
111
|
/>
|
|
117
112
|
);
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
type InteractiveDevSessionProps = DevProps & {
|
|
121
|
-
apiToken: string | undefined;
|
|
122
116
|
bundle: EsbuildBundle | undefined;
|
|
123
117
|
};
|
|
124
118
|
|
|
@@ -164,6 +158,7 @@ function DevSession(props: DevSessionProps) {
|
|
|
164
158
|
bundle={props.bundle}
|
|
165
159
|
format={props.entry.format}
|
|
166
160
|
compatibilityDate={props.compatibilityDate}
|
|
161
|
+
localProtocol={props.localProtocol}
|
|
167
162
|
compatibilityFlags={props.compatibilityFlags}
|
|
168
163
|
bindings={props.bindings}
|
|
169
164
|
assetPaths={props.assetPaths}
|
|
@@ -181,7 +176,6 @@ function DevSession(props: DevSessionProps) {
|
|
|
181
176
|
bundle={props.bundle}
|
|
182
177
|
format={props.entry.format}
|
|
183
178
|
accountId={props.accountId}
|
|
184
|
-
apiToken={props.apiToken}
|
|
185
179
|
bindings={props.bindings}
|
|
186
180
|
assetPaths={props.assetPaths}
|
|
187
181
|
public={props.public}
|
|
@@ -195,6 +189,7 @@ function DevSession(props: DevSessionProps) {
|
|
|
195
189
|
env={props.env}
|
|
196
190
|
legacyEnv={props.legacyEnv}
|
|
197
191
|
zone={props.zone}
|
|
192
|
+
host={props.host}
|
|
198
193
|
/>
|
|
199
194
|
);
|
|
200
195
|
}
|
|
@@ -242,9 +237,11 @@ function useCustomBuild(
|
|
|
242
237
|
persistent: true,
|
|
243
238
|
ignoreInitial: true,
|
|
244
239
|
}).on("all", (_event, filePath) => {
|
|
240
|
+
const relativeFile =
|
|
241
|
+
path.relative(expectedEntry.directory, expectedEntry.file) || ".";
|
|
245
242
|
//TODO: we should buffer requests to the proxy until this completes
|
|
246
243
|
logger.log(`The file ${filePath} changed, restarting build...`);
|
|
247
|
-
runCustomBuild(expectedEntry.file, build).catch((err) => {
|
|
244
|
+
runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
|
|
248
245
|
logger.error("Custom build failed:", err);
|
|
249
246
|
});
|
|
250
247
|
});
|
|
@@ -253,7 +250,7 @@ function useCustomBuild(
|
|
|
253
250
|
return () => {
|
|
254
251
|
watcher?.close();
|
|
255
252
|
};
|
|
256
|
-
}, [build, expectedEntry
|
|
253
|
+
}, [build, expectedEntry]);
|
|
257
254
|
}
|
|
258
255
|
|
|
259
256
|
function sleep(period: number) {
|
|
@@ -375,10 +372,7 @@ function useHotkeys(
|
|
|
375
372
|
}
|
|
376
373
|
// toggle inspector
|
|
377
374
|
case "d": {
|
|
378
|
-
await
|
|
379
|
-
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`,
|
|
380
|
-
{ forceChromium: true }
|
|
381
|
-
);
|
|
375
|
+
await openInspector(inspectorPort);
|
|
382
376
|
break;
|
|
383
377
|
}
|
|
384
378
|
// toggle local
|
package/src/dev/local.tsx
CHANGED
|
@@ -21,6 +21,7 @@ interface LocalProps {
|
|
|
21
21
|
compatibilityDate: string;
|
|
22
22
|
compatibilityFlags: string[] | undefined;
|
|
23
23
|
bindings: CfWorkerInit["bindings"];
|
|
24
|
+
localProtocol: "http" | "https";
|
|
24
25
|
assetPaths: AssetPaths | undefined;
|
|
25
26
|
public: string | undefined;
|
|
26
27
|
port: number;
|
|
@@ -54,6 +55,7 @@ function useLocalWorker({
|
|
|
54
55
|
rules,
|
|
55
56
|
enableLocalPersistence,
|
|
56
57
|
ip,
|
|
58
|
+
localProtocol,
|
|
57
59
|
crons,
|
|
58
60
|
}: LocalProps) {
|
|
59
61
|
// TODO: pass vars via command line
|
|
@@ -87,6 +89,11 @@ function useLocalWorker({
|
|
|
87
89
|
'⎔ A "public" folder is not yet supported in local mode.'
|
|
88
90
|
);
|
|
89
91
|
}
|
|
92
|
+
if (bindings.services && bindings.services.length > 0) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
"⎔ Service bindings are not yet supported in local mode."
|
|
95
|
+
);
|
|
96
|
+
}
|
|
90
97
|
|
|
91
98
|
// In local mode, we want to copy all referenced modules into
|
|
92
99
|
// the output bundle directory before starting up
|
|
@@ -159,6 +166,7 @@ function useLocalWorker({
|
|
|
159
166
|
name: workerName,
|
|
160
167
|
port,
|
|
161
168
|
scriptPath,
|
|
169
|
+
https: localProtocol === "https",
|
|
162
170
|
host: ip,
|
|
163
171
|
modules: format === "modules",
|
|
164
172
|
modulesRules: (rules || [])
|
|
@@ -293,10 +301,12 @@ function useLocalWorker({
|
|
|
293
301
|
workerName,
|
|
294
302
|
format,
|
|
295
303
|
port,
|
|
304
|
+
localProtocol,
|
|
296
305
|
ip,
|
|
297
306
|
bindings.durable_objects?.bindings,
|
|
298
307
|
bindings.kv_namespaces,
|
|
299
308
|
bindings.vars,
|
|
309
|
+
bindings.services,
|
|
300
310
|
compatibilityDate,
|
|
301
311
|
compatibilityFlags,
|
|
302
312
|
localPersistencePath,
|
package/src/dev/remote.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
1
|
import { readFile } from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { useState, useEffect, useRef } from "react";
|
|
@@ -7,6 +6,7 @@ import useInspector from "../inspect";
|
|
|
7
6
|
import { logger } from "../logger";
|
|
8
7
|
import { usePreviewServer } from "../proxy";
|
|
9
8
|
import { syncAssets } from "../sites";
|
|
9
|
+
import { requireApiToken, requireAuth } from "../user";
|
|
10
10
|
import type { CfPreviewToken } from "../create-worker-preview";
|
|
11
11
|
import type { AssetPaths } from "../sites";
|
|
12
12
|
import type { CfModule, CfWorkerInit, CfScriptFormat } from "../worker";
|
|
@@ -23,24 +23,21 @@ export function Remote(props: {
|
|
|
23
23
|
localProtocol: "https" | "http";
|
|
24
24
|
inspectorPort: number;
|
|
25
25
|
accountId: string | undefined;
|
|
26
|
-
apiToken: string | undefined;
|
|
27
26
|
bindings: CfWorkerInit["bindings"];
|
|
28
27
|
compatibilityDate: string;
|
|
29
28
|
compatibilityFlags: string[] | undefined;
|
|
30
29
|
usageModel: "bundled" | "unbound" | undefined;
|
|
31
30
|
env: string | undefined;
|
|
32
31
|
legacyEnv: boolean | undefined;
|
|
33
|
-
zone:
|
|
32
|
+
zone: string | undefined;
|
|
33
|
+
host: string | undefined;
|
|
34
34
|
}) {
|
|
35
|
-
assert(props.accountId, "accountId is required");
|
|
36
|
-
assert(props.apiToken, "apiToken is required");
|
|
37
35
|
const previewToken = useWorker({
|
|
38
36
|
name: props.name,
|
|
39
37
|
bundle: props.bundle,
|
|
40
38
|
format: props.format,
|
|
41
39
|
modules: props.bundle ? props.bundle.modules : [],
|
|
42
40
|
accountId: props.accountId,
|
|
43
|
-
apiToken: props.apiToken,
|
|
44
41
|
bindings: props.bindings,
|
|
45
42
|
assetPaths: props.assetPaths,
|
|
46
43
|
port: props.port,
|
|
@@ -50,6 +47,7 @@ export function Remote(props: {
|
|
|
50
47
|
env: props.env,
|
|
51
48
|
legacyEnv: props.legacyEnv,
|
|
52
49
|
zone: props.zone,
|
|
50
|
+
host: props.host,
|
|
53
51
|
});
|
|
54
52
|
|
|
55
53
|
usePreviewServer({
|
|
@@ -73,8 +71,7 @@ export function useWorker(props: {
|
|
|
73
71
|
bundle: EsbuildBundle | undefined;
|
|
74
72
|
format: CfScriptFormat | undefined;
|
|
75
73
|
modules: CfModule[];
|
|
76
|
-
accountId: string;
|
|
77
|
-
apiToken: string;
|
|
74
|
+
accountId: string | undefined;
|
|
78
75
|
bindings: CfWorkerInit["bindings"];
|
|
79
76
|
assetPaths: AssetPaths | undefined;
|
|
80
77
|
port: number;
|
|
@@ -83,7 +80,8 @@ export function useWorker(props: {
|
|
|
83
80
|
usageModel: "bundled" | "unbound" | undefined;
|
|
84
81
|
env: string | undefined;
|
|
85
82
|
legacyEnv: boolean | undefined;
|
|
86
|
-
zone:
|
|
83
|
+
zone: string | undefined;
|
|
84
|
+
host: string | undefined;
|
|
87
85
|
}): CfPreviewToken | undefined {
|
|
88
86
|
const {
|
|
89
87
|
name,
|
|
@@ -91,7 +89,6 @@ export function useWorker(props: {
|
|
|
91
89
|
format,
|
|
92
90
|
modules,
|
|
93
91
|
accountId,
|
|
94
|
-
apiToken,
|
|
95
92
|
bindings,
|
|
96
93
|
assetPaths,
|
|
97
94
|
compatibilityDate,
|
|
@@ -105,6 +102,9 @@ export function useWorker(props: {
|
|
|
105
102
|
// something's "happened" in our system; We make a ref and
|
|
106
103
|
// mark it once we log our initial message. Refs are vars!
|
|
107
104
|
const startedRef = useRef(false);
|
|
105
|
+
// This ref holds the actual accountId being used, the `accountId` prop could be undefined
|
|
106
|
+
// as it is only what is retrieved from the wrangler.toml config.
|
|
107
|
+
const accountIdRef = useRef(accountId);
|
|
108
108
|
|
|
109
109
|
useEffect(() => {
|
|
110
110
|
const abortController = new AbortController();
|
|
@@ -119,8 +119,13 @@ export function useWorker(props: {
|
|
|
119
119
|
logger.log("⎔ Detected changes, restarted server.");
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Ensure we have an account id, even if it means logging in here.
|
|
123
|
+
accountIdRef.current = await requireAuth({
|
|
124
|
+
account_id: accountIdRef.current,
|
|
125
|
+
});
|
|
126
|
+
|
|
122
127
|
const assets = await syncAssets(
|
|
123
|
-
|
|
128
|
+
accountIdRef.current,
|
|
124
129
|
// When we're using the newer service environments, we wouldn't
|
|
125
130
|
// have added the env name on to the script name. However, we must
|
|
126
131
|
// include it in the kv namespace name regardless (since there's no
|
|
@@ -173,10 +178,15 @@ export function useWorker(props: {
|
|
|
173
178
|
await createWorkerPreview(
|
|
174
179
|
init,
|
|
175
180
|
{
|
|
176
|
-
accountId,
|
|
177
|
-
apiToken,
|
|
181
|
+
accountId: accountIdRef.current,
|
|
182
|
+
apiToken: requireApiToken(),
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
env: props.env,
|
|
186
|
+
legacyEnv: props.legacyEnv,
|
|
187
|
+
zone: props.zone,
|
|
188
|
+
host: props.host,
|
|
178
189
|
},
|
|
179
|
-
{ env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
|
|
180
190
|
abortController.signal
|
|
181
191
|
)
|
|
182
192
|
);
|
|
@@ -185,7 +195,21 @@ export function useWorker(props: {
|
|
|
185
195
|
// we want to log the error, but not end the process
|
|
186
196
|
// since it could recover after the developer fixes whatever's wrong
|
|
187
197
|
if ((err as { code: string }).code !== "ABORT_ERR") {
|
|
188
|
-
|
|
198
|
+
// instead of logging the raw API error to the user,
|
|
199
|
+
// give them friendly instructions
|
|
200
|
+
// for error 10063 (workers.dev subdomain required)
|
|
201
|
+
if (err.code === 10063) {
|
|
202
|
+
const errorMessage =
|
|
203
|
+
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
|
|
204
|
+
const solutionMessage =
|
|
205
|
+
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
|
|
206
|
+
const onboardingLink = `https://dash.cloudflare.com/${accountIdRef.current}/workers/onboarding`;
|
|
207
|
+
logger.error(
|
|
208
|
+
`${errorMessage}\n${solutionMessage}\n${onboardingLink}`
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
logger.error("Error on remote worker:", err);
|
|
212
|
+
}
|
|
189
213
|
}
|
|
190
214
|
});
|
|
191
215
|
|
|
@@ -197,7 +221,6 @@ export function useWorker(props: {
|
|
|
197
221
|
bundle,
|
|
198
222
|
format,
|
|
199
223
|
accountId,
|
|
200
|
-
apiToken,
|
|
201
224
|
port,
|
|
202
225
|
assetPaths,
|
|
203
226
|
compatibilityDate,
|
|
@@ -208,6 +231,7 @@ export function useWorker(props: {
|
|
|
208
231
|
props.env,
|
|
209
232
|
props.legacyEnv,
|
|
210
233
|
props.zone,
|
|
234
|
+
props.host,
|
|
211
235
|
]);
|
|
212
236
|
return token;
|
|
213
237
|
}
|
package/src/dialogs.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { Box, Text, useInput, render } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
3
4
|
import TextInput from "ink-text-input";
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import { useState } from "react";
|
|
@@ -85,3 +86,50 @@ export async function prompt(
|
|
|
85
86
|
);
|
|
86
87
|
});
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
type SelectOption = {
|
|
91
|
+
value: string;
|
|
92
|
+
label: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
type SelectProps = {
|
|
96
|
+
text: string;
|
|
97
|
+
options: SelectOption[];
|
|
98
|
+
initialIndex: number;
|
|
99
|
+
onSelect: (value: string) => void;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function Select(props: SelectProps) {
|
|
103
|
+
return (
|
|
104
|
+
<Box flexDirection="column">
|
|
105
|
+
<Text>{props.text}</Text>
|
|
106
|
+
<SelectInput
|
|
107
|
+
initialIndex={props.initialIndex}
|
|
108
|
+
items={props.options}
|
|
109
|
+
onSelect={async (selected) => {
|
|
110
|
+
props.onSelect(selected.value);
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
</Box>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function select(
|
|
118
|
+
text: string,
|
|
119
|
+
options: SelectOption[],
|
|
120
|
+
initialIndex: number
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
return new Promise((resolve) => {
|
|
123
|
+
const { unmount } = render(
|
|
124
|
+
<Select
|
|
125
|
+
text={text}
|
|
126
|
+
options={options}
|
|
127
|
+
initialIndex={initialIndex}
|
|
128
|
+
onSelect={(option: string) => {
|
|
129
|
+
unmount();
|
|
130
|
+
resolve(option);
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
}
|
package/src/durable.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { fetchResult } from "./cfetch";
|
|
3
|
+
import { logger } from "./logger";
|
|
4
|
+
import type { Config } from "./config";
|
|
5
|
+
import type { CfWorkerInit } from "./worker";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* For a given Worker + migrations config, figure out which migrations
|
|
9
|
+
* to upload based on the current migration tag of the deployed Worker.
|
|
10
|
+
*/
|
|
11
|
+
export async function getMigrationsToUpload(
|
|
12
|
+
scriptName: string,
|
|
13
|
+
props: {
|
|
14
|
+
accountId: string | undefined;
|
|
15
|
+
config: Config;
|
|
16
|
+
legacyEnv: boolean | undefined;
|
|
17
|
+
env: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
): Promise<CfWorkerInit["migrations"]> {
|
|
20
|
+
const { config, accountId } = props;
|
|
21
|
+
|
|
22
|
+
assert(accountId, "Missing accountId");
|
|
23
|
+
// if config.migrations
|
|
24
|
+
let migrations;
|
|
25
|
+
if (config.migrations.length > 0) {
|
|
26
|
+
// get current migration tag
|
|
27
|
+
type ScriptData = { id: string; migration_tag?: string };
|
|
28
|
+
let script: ScriptData | undefined;
|
|
29
|
+
if (!props.legacyEnv) {
|
|
30
|
+
try {
|
|
31
|
+
if (props.env) {
|
|
32
|
+
const scriptData = await fetchResult<{
|
|
33
|
+
script: ScriptData;
|
|
34
|
+
}>(
|
|
35
|
+
`/accounts/${accountId}/workers/services/${scriptName}/environments/${props.env}`
|
|
36
|
+
);
|
|
37
|
+
script = scriptData.script;
|
|
38
|
+
} else {
|
|
39
|
+
const scriptData = await fetchResult<{
|
|
40
|
+
default_environment: {
|
|
41
|
+
script: ScriptData;
|
|
42
|
+
};
|
|
43
|
+
}>(`/accounts/${accountId}/workers/services/${scriptName}`);
|
|
44
|
+
script = scriptData.default_environment.script;
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (
|
|
48
|
+
![
|
|
49
|
+
10090, // corresponds to workers.api.error.service_not_found, so the script wasn't previously published at all
|
|
50
|
+
10092, // workers.api.error.environment_not_found, so the script wasn't published to this environment yet
|
|
51
|
+
].includes((err as { code: number }).code)
|
|
52
|
+
) {
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
// else it's a 404, no script found, and we can proceed
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
const scripts = await fetchResult<ScriptData[]>(
|
|
59
|
+
`/accounts/${accountId}/workers/scripts`
|
|
60
|
+
);
|
|
61
|
+
script = scripts.find(({ id }) => id === scriptName);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (script?.migration_tag) {
|
|
65
|
+
// was already published once
|
|
66
|
+
const scriptMigrationTag = script.migration_tag;
|
|
67
|
+
const foundIndex = config.migrations.findIndex(
|
|
68
|
+
(migration) => migration.tag === scriptMigrationTag
|
|
69
|
+
);
|
|
70
|
+
if (foundIndex === -1) {
|
|
71
|
+
logger.warn(
|
|
72
|
+
`The published script ${scriptName} has a migration tag "${script.migration_tag}, which was not found in wrangler.toml. You may have already deleted it. Applying all available migrations to the script...`
|
|
73
|
+
);
|
|
74
|
+
migrations = {
|
|
75
|
+
old_tag: script.migration_tag,
|
|
76
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
77
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
if (foundIndex !== config.migrations.length - 1) {
|
|
81
|
+
// there are new migrations to send up
|
|
82
|
+
migrations = {
|
|
83
|
+
old_tag: script.migration_tag,
|
|
84
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
85
|
+
steps: config.migrations
|
|
86
|
+
.slice(foundIndex + 1)
|
|
87
|
+
.map(({ tag: _tag, ...rest }) => rest),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// else, we're up to date, no migrations to send
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// first time publishing durable objects to this script,
|
|
94
|
+
// so we send all the migrations
|
|
95
|
+
migrations = {
|
|
96
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
97
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return migrations;
|
|
102
|
+
}
|
package/src/entry.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
5
|
import { execaCommand } from "execa";
|
|
@@ -45,11 +45,16 @@ export async function getEntry(
|
|
|
45
45
|
file = path.resolve(directory, config.main);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
const relativeFile = path.relative(directory, file) || ".";
|
|
49
|
+
await runCustomBuild(file, relativeFile, config.build);
|
|
49
50
|
|
|
50
51
|
if (fileExists(file) === false) {
|
|
51
52
|
throw new Error(
|
|
52
|
-
|
|
53
|
+
getMissingEntryPointMessage(
|
|
54
|
+
`The entry-point file at "${relativeFile}" was not found.`,
|
|
55
|
+
file,
|
|
56
|
+
relativeFile
|
|
57
|
+
)
|
|
53
58
|
);
|
|
54
59
|
}
|
|
55
60
|
const format = await guessWorkerFormat(
|
|
@@ -90,7 +95,8 @@ export async function getEntry(
|
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
export async function runCustomBuild(
|
|
93
|
-
|
|
98
|
+
expectedEntryAbsolute: string,
|
|
99
|
+
expectedEntryRelative: string,
|
|
94
100
|
build: Config["build"]
|
|
95
101
|
) {
|
|
96
102
|
if (build?.command) {
|
|
@@ -105,12 +111,14 @@ export async function runCustomBuild(
|
|
|
105
111
|
...(build.cwd && { cwd: build.cwd }),
|
|
106
112
|
});
|
|
107
113
|
|
|
108
|
-
if (fileExists(
|
|
114
|
+
if (fileExists(expectedEntryAbsolute) === false) {
|
|
109
115
|
throw new Error(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
getMissingEntryPointMessage(
|
|
117
|
+
`The expected output file at "${expectedEntryRelative}" was not found after running custom build: ${build.command}.\n` +
|
|
118
|
+
"The `main` property in wrangler.toml should point to the file generated by the custom build.",
|
|
119
|
+
expectedEntryAbsolute,
|
|
120
|
+
expectedEntryRelative
|
|
121
|
+
)
|
|
114
122
|
);
|
|
115
123
|
}
|
|
116
124
|
}
|
|
@@ -265,3 +273,50 @@ function generateAddScriptNameExamples(
|
|
|
265
273
|
})
|
|
266
274
|
.join("\n");
|
|
267
275
|
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Generate an appropriate message for when the entry-point is missing.
|
|
279
|
+
*
|
|
280
|
+
* To be more helpful to developers, we check whether there is a suitable file
|
|
281
|
+
* nearby to the expected file path.
|
|
282
|
+
*/
|
|
283
|
+
function getMissingEntryPointMessage(
|
|
284
|
+
message: string,
|
|
285
|
+
absoluteEntryPointPath: string,
|
|
286
|
+
relativeEntryPointPath: string
|
|
287
|
+
): string {
|
|
288
|
+
if (
|
|
289
|
+
existsSync(absoluteEntryPointPath) &&
|
|
290
|
+
statSync(absoluteEntryPointPath).isDirectory()
|
|
291
|
+
) {
|
|
292
|
+
// The expected entry-point is a directory, so offer further guidance.
|
|
293
|
+
message += `\nThe provided entry-point path, "${relativeEntryPointPath}", points to a directory, rather than a file.\n`;
|
|
294
|
+
|
|
295
|
+
// Perhaps we can even guess what the correct path should be...
|
|
296
|
+
const possiblePaths: string[] = [];
|
|
297
|
+
for (const basenamePath of [
|
|
298
|
+
"worker",
|
|
299
|
+
"dist/worker",
|
|
300
|
+
"index",
|
|
301
|
+
"dist/index",
|
|
302
|
+
]) {
|
|
303
|
+
for (const extension of [".ts", ".tsx", ".js", ".jsx"]) {
|
|
304
|
+
const filePath = basenamePath + extension;
|
|
305
|
+
if (fileExists(path.resolve(absoluteEntryPointPath, filePath))) {
|
|
306
|
+
possiblePaths.push(path.join(relativeEntryPointPath, filePath));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (possiblePaths.length > 0) {
|
|
312
|
+
message +=
|
|
313
|
+
`\nDid you mean to set the main field to${
|
|
314
|
+
possiblePaths.length > 1 ? " one of" : ""
|
|
315
|
+
}:\n` +
|
|
316
|
+
"```\n" +
|
|
317
|
+
possiblePaths.map((filePath) => `main = "./${filePath}"\n`).join("") +
|
|
318
|
+
"```";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return message;
|
|
322
|
+
}
|