wrangler 0.0.0-e95bea2 → 0.0.0-e95e5a0
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 +25 -14
- package/bin/wrangler.js +13 -1
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +33 -25
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +80 -4
- package/pages/functions/filepath-routing.test.ts +136 -25
- package/pages/functions/filepath-routing.ts +14 -3
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +146 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +1179 -271
- package/src/__tests__/dev.test.tsx +382 -25
- package/src/__tests__/guess-worker-format.test.ts +28 -1
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +5 -1
- package/src/__tests__/helpers/mock-console.ts +20 -3
- package/src/__tests__/helpers/mock-dialogs.ts +26 -1
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +5 -2
- package/src/__tests__/index.test.ts +27 -769
- package/src/__tests__/init.test.ts +1516 -0
- package/src/__tests__/jest.setup.ts +4 -4
- package/src/__tests__/kv.test.ts +172 -126
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/pages.test.ts +311 -26
- package/src/__tests__/publish.test.ts +2946 -524
- package/src/__tests__/r2.test.ts +26 -10
- package/src/__tests__/route.test.ts +4 -4
- package/src/__tests__/secret.test.ts +55 -34
- package/src/__tests__/tail.test.ts +146 -20
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +26 -7
- package/src/bundle.ts +30 -3
- package/src/cfetch/internal.ts +9 -7
- package/src/cli.ts +5 -11
- package/src/config/README.md +7 -7
- package/src/config/config.ts +33 -78
- package/src/config/environment.ts +93 -4
- package/src/config/index.ts +15 -4
- package/src/config/validation-helpers.ts +202 -15
- package/src/config/validation.ts +530 -133
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +22 -13
- package/src/create-worker-upload-form.ts +32 -9
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +136 -75
- package/src/dev/local.tsx +196 -125
- package/src/dev/remote.tsx +25 -13
- package/src/dev/use-esbuild.ts +23 -5
- package/src/dialogs.tsx +16 -6
- package/src/entry.ts +119 -11
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +3 -2
- package/src/index.tsx +739 -400
- package/src/inspect.ts +17 -11
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv/index.tsx → kv.ts} +8 -28
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +63 -58
- package/src/open-in-browser.ts +5 -4
- package/src/package-manager.ts +9 -8
- package/src/pages.tsx +883 -62
- package/src/parse.ts +1 -1
- package/src/proxy.ts +32 -21
- package/src/publish.ts +276 -161
- package/src/sites.tsx +88 -33
- package/src/tail/index.ts +2 -2
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +231 -103
- package/src/whoami.tsx +2 -1
- package/src/worker.ts +15 -6
- package/templates/gitignore +171 -0
- package/templates/static-asset-facade.js +1 -1
- package/wrangler-dist/cli.js +22087 -49340
- package/src/__tests__/dev2.test.tsx +0 -152
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/reporting.ts +0 -131
- package/wrangler-dist/cli.js.map +0 -7
package/src/dev/local.tsx
CHANGED
|
@@ -5,36 +5,34 @@ import path from "node:path";
|
|
|
5
5
|
import { useState, useEffect, useRef } from "react";
|
|
6
6
|
import onExit from "signal-exit";
|
|
7
7
|
import useInspector from "../inspect";
|
|
8
|
+
import { logger } from "../logger";
|
|
8
9
|
import { DEFAULT_MODULE_RULES } from "../module-collection";
|
|
9
10
|
import { waitForPortToBeAvailable } from "../proxy";
|
|
10
11
|
import type { Config } from "../config";
|
|
11
12
|
import type { AssetPaths } from "../sites";
|
|
12
13
|
import type { CfWorkerInit, CfScriptFormat } from "../worker";
|
|
13
14
|
import type { EsbuildBundle } from "./use-esbuild";
|
|
15
|
+
import type { MiniflareOptions } from "miniflare";
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
name:
|
|
17
|
+
interface LocalProps {
|
|
18
|
+
name: string | undefined;
|
|
17
19
|
bundle: EsbuildBundle | undefined;
|
|
18
20
|
format: CfScriptFormat | undefined;
|
|
21
|
+
compatibilityDate: string;
|
|
22
|
+
compatibilityFlags: string[] | undefined;
|
|
19
23
|
bindings: CfWorkerInit["bindings"];
|
|
20
|
-
assetPaths:
|
|
21
|
-
public:
|
|
24
|
+
assetPaths: AssetPaths | undefined;
|
|
25
|
+
public: string | undefined;
|
|
22
26
|
port: number;
|
|
27
|
+
ip: string;
|
|
23
28
|
rules: Config["rules"];
|
|
24
29
|
inspectorPort: number;
|
|
25
30
|
enableLocalPersistence: boolean;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
bindings: props.bindings,
|
|
32
|
-
assetPaths: props.assetPaths,
|
|
33
|
-
public: props.public,
|
|
34
|
-
port: props.port,
|
|
35
|
-
rules: props.rules,
|
|
36
|
-
enableLocalPersistence: props.enableLocalPersistence,
|
|
37
|
-
});
|
|
31
|
+
crons: Config["triggers"]["crons"];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function Local(props: LocalProps) {
|
|
35
|
+
const { inspectorUrl } = useLocalWorker(props);
|
|
38
36
|
useInspector({
|
|
39
37
|
inspectorUrl,
|
|
40
38
|
port: props.inspectorPort,
|
|
@@ -43,30 +41,48 @@ export function Local(props: {
|
|
|
43
41
|
return null;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
function useLocalWorker(
|
|
47
|
-
name:
|
|
48
|
-
bundle
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
44
|
+
function useLocalWorker({
|
|
45
|
+
name: workerName,
|
|
46
|
+
bundle,
|
|
47
|
+
format,
|
|
48
|
+
compatibilityDate,
|
|
49
|
+
compatibilityFlags,
|
|
50
|
+
bindings,
|
|
51
|
+
assetPaths,
|
|
52
|
+
public: publicDirectory,
|
|
53
|
+
port,
|
|
54
|
+
rules,
|
|
55
|
+
enableLocalPersistence,
|
|
56
|
+
ip,
|
|
57
|
+
crons,
|
|
58
|
+
}: LocalProps) {
|
|
57
59
|
// TODO: pass vars via command line
|
|
58
|
-
const { bundle, format, bindings, port, assetPaths } = props;
|
|
59
60
|
const local = useRef<ReturnType<typeof spawn>>();
|
|
60
61
|
const removeSignalExitListener = useRef<() => void>();
|
|
61
62
|
const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
|
|
63
|
+
// if we're using local persistence for data, we should use the cwd
|
|
64
|
+
// as an explicit path, or else it'll use the temp dir
|
|
65
|
+
// which disappears when dev ends
|
|
66
|
+
const localPersistencePath = enableLocalPersistence
|
|
67
|
+
? // Maybe we could make the path configurable as well?
|
|
68
|
+
path.join(process.cwd(), "wrangler-local-state")
|
|
69
|
+
: // We otherwise choose null, but choose true later
|
|
70
|
+
// so that it's persisted in the temp dir across a dev session
|
|
71
|
+
// even when we change source and reload
|
|
72
|
+
null;
|
|
62
73
|
useEffect(() => {
|
|
74
|
+
const abortController = new AbortController();
|
|
63
75
|
async function startLocalWorker() {
|
|
64
76
|
if (!bundle || !format) return;
|
|
65
77
|
|
|
66
78
|
// port for the worker
|
|
67
|
-
await waitForPortToBeAvailable(port, {
|
|
79
|
+
await waitForPortToBeAvailable(port, {
|
|
80
|
+
retryPeriod: 200,
|
|
81
|
+
timeout: 2000,
|
|
82
|
+
abortSignal: abortController.signal,
|
|
83
|
+
});
|
|
68
84
|
|
|
69
|
-
if (
|
|
85
|
+
if (publicDirectory) {
|
|
70
86
|
throw new Error(
|
|
71
87
|
'⎔ A "public" folder is not yet supported in local mode.'
|
|
72
88
|
);
|
|
@@ -81,109 +97,156 @@ function useLocalWorker(props: {
|
|
|
81
97
|
);
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
const scriptPath = realpathSync(bundle.path);
|
|
101
|
+
|
|
102
|
+
// the wasm_modules/text_blobs/data_blobs bindings are
|
|
103
|
+
// relative to process.cwd(), but the actual worker bundle
|
|
104
|
+
// is in the temp output directory; so we rewrite the paths to be absolute,
|
|
105
|
+
// letting miniflare resolve them correctly
|
|
106
|
+
|
|
107
|
+
// wasm
|
|
108
|
+
const wasmBindings: Record<string, string> = {};
|
|
109
|
+
for (const [name, filePath] of Object.entries(
|
|
110
|
+
bindings.wasm_modules || {}
|
|
111
|
+
)) {
|
|
112
|
+
wasmBindings[name] = path.join(process.cwd(), filePath);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// text
|
|
116
|
+
const textBlobBindings: Record<string, string> = {};
|
|
117
|
+
for (const [name, filePath] of Object.entries(
|
|
118
|
+
bindings.text_blobs || {}
|
|
119
|
+
)) {
|
|
120
|
+
textBlobBindings[name] = path.join(process.cwd(), filePath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// data
|
|
124
|
+
const dataBlobBindings: Record<string, string> = {};
|
|
125
|
+
for (const [name, filePath] of Object.entries(
|
|
126
|
+
bindings.data_blobs || {}
|
|
127
|
+
)) {
|
|
128
|
+
dataBlobBindings[name] = path.join(process.cwd(), filePath);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (format === "service-worker") {
|
|
132
|
+
for (const { type, name } of bundle.modules) {
|
|
133
|
+
if (type === "compiled-wasm") {
|
|
134
|
+
// In service-worker format, .wasm modules are referenced by global identifiers,
|
|
135
|
+
// so we convert it here.
|
|
136
|
+
// This identifier has to be a valid JS identifier, so we replace all non alphanumeric
|
|
137
|
+
// characters with an underscore.
|
|
138
|
+
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
139
|
+
wasmBindings[identifier] = name;
|
|
140
|
+
} else if (type === "text") {
|
|
141
|
+
// In service-worker format, text modules are referenced by global identifiers,
|
|
142
|
+
// so we convert it here.
|
|
143
|
+
// This identifier has to be a valid JS identifier, so we replace all non alphanumeric
|
|
144
|
+
// characters with an underscore.
|
|
145
|
+
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
146
|
+
textBlobBindings[identifier] = name;
|
|
147
|
+
} else if (type === "buffer") {
|
|
148
|
+
// In service-worker format, data blobs are referenced by global identifiers,
|
|
149
|
+
// so we convert it here.
|
|
150
|
+
// This identifier has to be a valid JS identifier, so we replace all non alphanumeric
|
|
151
|
+
// characters with an underscore.
|
|
152
|
+
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
153
|
+
dataBlobBindings[identifier] = name;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const options: MiniflareOptions = {
|
|
159
|
+
name: workerName,
|
|
160
|
+
port,
|
|
161
|
+
scriptPath,
|
|
162
|
+
host: ip,
|
|
163
|
+
modules: format === "modules",
|
|
164
|
+
modulesRules: (rules || [])
|
|
165
|
+
.concat(DEFAULT_MODULE_RULES)
|
|
166
|
+
.map(({ type, globs: include, fallthrough }) => ({
|
|
167
|
+
type,
|
|
168
|
+
include,
|
|
169
|
+
fallthrough,
|
|
170
|
+
})),
|
|
171
|
+
compatibilityDate,
|
|
172
|
+
compatibilityFlags,
|
|
173
|
+
kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
|
|
174
|
+
durableObjects: Object.fromEntries(
|
|
175
|
+
(bindings.durable_objects?.bindings ?? []).map<[string, string]>(
|
|
176
|
+
(value) => [value.name, value.class_name]
|
|
177
|
+
)
|
|
178
|
+
),
|
|
179
|
+
...(localPersistencePath
|
|
180
|
+
? {
|
|
181
|
+
kvPersist: path.join(localPersistencePath, "kv"),
|
|
182
|
+
durableObjectsPersist: path.join(localPersistencePath, "do"),
|
|
183
|
+
cachePersist: path.join(localPersistencePath, "cache"),
|
|
184
|
+
}
|
|
185
|
+
: {
|
|
186
|
+
// We mark these as true, so that they'll
|
|
187
|
+
// persist in the temp directory.
|
|
188
|
+
// This means they'll persist across a dev session,
|
|
189
|
+
// even if we change source and reload,
|
|
190
|
+
// and be deleted when the dev session ends
|
|
191
|
+
durableObjectsPersist: true,
|
|
192
|
+
cachePersist: true,
|
|
193
|
+
kvPersist: true,
|
|
194
|
+
}),
|
|
195
|
+
|
|
196
|
+
sitePath: assetPaths?.assetDirectory
|
|
197
|
+
? path.join(assetPaths.baseDirectory, assetPaths.assetDirectory)
|
|
198
|
+
: undefined,
|
|
199
|
+
siteInclude: assetPaths?.includePatterns.length
|
|
200
|
+
? assetPaths?.includePatterns
|
|
201
|
+
: undefined,
|
|
202
|
+
siteExclude: assetPaths?.excludePatterns.length
|
|
203
|
+
? assetPaths.excludePatterns
|
|
204
|
+
: undefined,
|
|
205
|
+
bindings: bindings.vars,
|
|
206
|
+
wasmBindings,
|
|
207
|
+
textBlobBindings,
|
|
208
|
+
dataBlobBindings,
|
|
209
|
+
sourceMap: true,
|
|
210
|
+
logUnhandledRejections: true,
|
|
211
|
+
crons,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// The path to the Miniflare CLI assumes that this file is being run from
|
|
215
|
+
// `wrangler-dist` and that the CLI is found in `miniflare-dist`.
|
|
216
|
+
// If either of those paths change this line needs updating.
|
|
217
|
+
const miniflareCLIPath = path.resolve(
|
|
218
|
+
__dirname,
|
|
219
|
+
"../miniflare-dist/index.mjs"
|
|
220
|
+
);
|
|
221
|
+
const optionsArg = JSON.stringify(options, null);
|
|
222
|
+
|
|
223
|
+
logger.log("⎔ Starting a local server...");
|
|
86
224
|
local.current = spawn(
|
|
87
225
|
"node",
|
|
88
226
|
[
|
|
89
|
-
"--experimental-vm-modules",
|
|
90
|
-
"--
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
"--
|
|
95
|
-
path.join(__dirname, "../miniflare-config-stubs/wrangler.empty.toml"),
|
|
96
|
-
"--env",
|
|
97
|
-
path.join(__dirname, "../miniflare-config-stubs/.env.empty"),
|
|
98
|
-
"--package",
|
|
99
|
-
path.join(__dirname, "../miniflare-config-stubs/package.empty.json"),
|
|
100
|
-
"--port",
|
|
101
|
-
port.toString(),
|
|
102
|
-
...(assetPaths
|
|
103
|
-
? [
|
|
104
|
-
"--site",
|
|
105
|
-
path.join(process.cwd(), assetPaths.baseDirectory),
|
|
106
|
-
...assetPaths.includePatterns.map((pattern) => [
|
|
107
|
-
"--site-include",
|
|
108
|
-
pattern,
|
|
109
|
-
]),
|
|
110
|
-
...assetPaths.excludePatterns.map((pattern) => [
|
|
111
|
-
"--site-exclude",
|
|
112
|
-
pattern,
|
|
113
|
-
]),
|
|
114
|
-
].flatMap((x) => x)
|
|
115
|
-
: []),
|
|
116
|
-
...(props.enableLocalPersistence
|
|
117
|
-
? ["--kv-persist", "--cache-persist", "--do-persist"]
|
|
118
|
-
: []),
|
|
119
|
-
...Object.entries(bindings.vars || {}).flatMap(([key, value]) => {
|
|
120
|
-
return ["--binding", `${key}=${value}`];
|
|
121
|
-
}),
|
|
122
|
-
...(bindings.kv_namespaces || []).flatMap(({ binding }) => {
|
|
123
|
-
return ["--kv", binding];
|
|
124
|
-
}),
|
|
125
|
-
...(bindings.durable_objects?.bindings || []).flatMap(
|
|
126
|
-
({ name, class_name }) => {
|
|
127
|
-
return ["--do", `${name}=${class_name}`];
|
|
128
|
-
}
|
|
129
|
-
),
|
|
130
|
-
...Object.entries(bindings.wasm_modules || {}).flatMap(
|
|
131
|
-
([name, filePath]) => {
|
|
132
|
-
return [
|
|
133
|
-
"--wasm",
|
|
134
|
-
`${name}=${path.join(process.cwd(), filePath)}`,
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
),
|
|
138
|
-
...bundle.modules.reduce<string[]>((cmd, { name, type }) => {
|
|
139
|
-
if (format === "service-worker") {
|
|
140
|
-
if (type === "compiled-wasm") {
|
|
141
|
-
// In service-worker format, .wasm modules are referenced
|
|
142
|
-
// by global identifiers, so we convert it here.
|
|
143
|
-
// This identifier has to be a valid JS identifier,
|
|
144
|
-
// so we replace all non alphanumeric characters
|
|
145
|
-
// with an underscore.
|
|
146
|
-
const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
147
|
-
return cmd.concat([`--wasm`, `${identifier}=${name}`]);
|
|
148
|
-
} else {
|
|
149
|
-
// TODO: we should actually support this
|
|
150
|
-
throw new Error(
|
|
151
|
-
`⎔ Unsupported module type ${type} for file ${name} in service-worker format`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return cmd;
|
|
156
|
-
}, []),
|
|
157
|
-
"--modules",
|
|
158
|
-
String(format === "modules"),
|
|
159
|
-
...(props.rules || [])
|
|
160
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
161
|
-
.concat(DEFAULT_MODULE_RULES!)
|
|
162
|
-
.flatMap((rule) =>
|
|
163
|
-
rule.globs.flatMap((glob) => [
|
|
164
|
-
"--modules-rule",
|
|
165
|
-
`${rule.type}=${glob}`,
|
|
166
|
-
])
|
|
167
|
-
),
|
|
227
|
+
"--experimental-vm-modules", // ensures that Miniflare can run ESM Workers
|
|
228
|
+
"--no-warnings", // hide annoying Node warnings
|
|
229
|
+
"--inspect", // start Miniflare listening for a debugger to attach
|
|
230
|
+
miniflareCLIPath,
|
|
231
|
+
optionsArg,
|
|
232
|
+
// "--log=VERBOSE", // uncomment this to Miniflare to log "everything"!
|
|
168
233
|
],
|
|
169
234
|
{
|
|
170
|
-
cwd: path.dirname(
|
|
235
|
+
cwd: path.dirname(scriptPath),
|
|
171
236
|
}
|
|
172
237
|
);
|
|
173
|
-
console.log(`⬣ Listening at http://localhost:${port}`);
|
|
174
238
|
|
|
175
239
|
local.current.on("close", (code) => {
|
|
176
240
|
if (code) {
|
|
177
|
-
|
|
241
|
+
logger.log(`Miniflare process exited with code ${code}`);
|
|
178
242
|
}
|
|
179
243
|
});
|
|
180
244
|
|
|
181
245
|
local.current.stdout?.on("data", (data: Buffer) => {
|
|
182
|
-
|
|
246
|
+
process.stdout.write(data);
|
|
183
247
|
});
|
|
184
248
|
|
|
185
249
|
local.current.stderr?.on("data", (data: Buffer) => {
|
|
186
|
-
console.error(`${data.toString()}`);
|
|
187
250
|
const matches =
|
|
188
251
|
/Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
|
|
189
252
|
data.toString()
|
|
@@ -195,29 +258,30 @@ function useLocalWorker(props: {
|
|
|
195
258
|
|
|
196
259
|
local.current.on("exit", (code) => {
|
|
197
260
|
if (code) {
|
|
198
|
-
|
|
261
|
+
logger.error(`Miniflare process exited with code ${code}`);
|
|
199
262
|
}
|
|
200
263
|
});
|
|
201
264
|
|
|
202
265
|
local.current.on("error", (error: Error) => {
|
|
203
|
-
|
|
204
|
-
|
|
266
|
+
logger.error(`Miniflare process failed to spawn`);
|
|
267
|
+
logger.error(error);
|
|
205
268
|
});
|
|
206
269
|
|
|
207
270
|
removeSignalExitListener.current = onExit((_code, _signal) => {
|
|
208
|
-
|
|
271
|
+
logger.log("⎔ Shutting down local server.");
|
|
209
272
|
local.current?.kill();
|
|
210
273
|
local.current = undefined;
|
|
211
274
|
});
|
|
212
275
|
}
|
|
213
276
|
|
|
214
277
|
startLocalWorker().catch((err) => {
|
|
215
|
-
|
|
278
|
+
logger.error("local worker:", err);
|
|
216
279
|
});
|
|
217
280
|
|
|
218
281
|
return () => {
|
|
282
|
+
abortController.abort();
|
|
219
283
|
if (local.current) {
|
|
220
|
-
|
|
284
|
+
logger.log("⎔ Shutting down local server.");
|
|
221
285
|
local.current?.kill();
|
|
222
286
|
local.current = undefined;
|
|
223
287
|
removeSignalExitListener.current && removeSignalExitListener.current();
|
|
@@ -226,16 +290,23 @@ function useLocalWorker(props: {
|
|
|
226
290
|
};
|
|
227
291
|
}, [
|
|
228
292
|
bundle,
|
|
293
|
+
workerName,
|
|
229
294
|
format,
|
|
230
295
|
port,
|
|
296
|
+
ip,
|
|
231
297
|
bindings.durable_objects?.bindings,
|
|
232
298
|
bindings.kv_namespaces,
|
|
233
299
|
bindings.vars,
|
|
234
|
-
|
|
300
|
+
compatibilityDate,
|
|
301
|
+
compatibilityFlags,
|
|
302
|
+
localPersistencePath,
|
|
235
303
|
assetPaths,
|
|
236
|
-
|
|
237
|
-
|
|
304
|
+
publicDirectory,
|
|
305
|
+
rules,
|
|
238
306
|
bindings.wasm_modules,
|
|
307
|
+
bindings.text_blobs,
|
|
308
|
+
bindings.data_blobs,
|
|
309
|
+
crons,
|
|
239
310
|
]);
|
|
240
311
|
return { inspectorUrl };
|
|
241
312
|
}
|
package/src/dev/remote.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { useState, useEffect, useRef } from "react";
|
|
5
5
|
import { createWorkerPreview } from "../create-worker-preview";
|
|
6
6
|
import useInspector from "../inspect";
|
|
7
|
+
import { logger } from "../logger";
|
|
7
8
|
import { usePreviewServer } from "../proxy";
|
|
8
9
|
import { syncAssets } from "../sites";
|
|
9
10
|
import type { CfPreviewToken } from "../create-worker-preview";
|
|
@@ -15,17 +16,18 @@ export function Remote(props: {
|
|
|
15
16
|
name: string | undefined;
|
|
16
17
|
bundle: EsbuildBundle | undefined;
|
|
17
18
|
format: CfScriptFormat | undefined;
|
|
18
|
-
public:
|
|
19
|
-
assetPaths:
|
|
19
|
+
public: string | undefined;
|
|
20
|
+
assetPaths: AssetPaths | undefined;
|
|
20
21
|
port: number;
|
|
22
|
+
ip: string;
|
|
21
23
|
localProtocol: "https" | "http";
|
|
22
24
|
inspectorPort: number;
|
|
23
|
-
accountId:
|
|
24
|
-
apiToken:
|
|
25
|
+
accountId: string | undefined;
|
|
26
|
+
apiToken: string | undefined;
|
|
25
27
|
bindings: CfWorkerInit["bindings"];
|
|
26
|
-
compatibilityDate: string
|
|
27
|
-
compatibilityFlags:
|
|
28
|
-
usageModel:
|
|
28
|
+
compatibilityDate: string;
|
|
29
|
+
compatibilityFlags: string[] | undefined;
|
|
30
|
+
usageModel: "bundled" | "unbound" | undefined;
|
|
29
31
|
env: string | undefined;
|
|
30
32
|
legacyEnv: boolean | undefined;
|
|
31
33
|
zone: { id: string; host: string } | undefined;
|
|
@@ -55,6 +57,7 @@ export function Remote(props: {
|
|
|
55
57
|
publicRoot: props.public,
|
|
56
58
|
localProtocol: props.localProtocol,
|
|
57
59
|
localPort: props.port,
|
|
60
|
+
ip: props.ip,
|
|
58
61
|
});
|
|
59
62
|
|
|
60
63
|
useInspector({
|
|
@@ -73,11 +76,11 @@ export function useWorker(props: {
|
|
|
73
76
|
accountId: string;
|
|
74
77
|
apiToken: string;
|
|
75
78
|
bindings: CfWorkerInit["bindings"];
|
|
76
|
-
assetPaths:
|
|
79
|
+
assetPaths: AssetPaths | undefined;
|
|
77
80
|
port: number;
|
|
78
81
|
compatibilityDate: string | undefined;
|
|
79
82
|
compatibilityFlags: string[] | undefined;
|
|
80
|
-
usageModel:
|
|
83
|
+
usageModel: "bundled" | "unbound" | undefined;
|
|
81
84
|
env: string | undefined;
|
|
82
85
|
legacyEnv: boolean | undefined;
|
|
83
86
|
zone: { id: string; host: string } | undefined;
|
|
@@ -104,6 +107,7 @@ export function useWorker(props: {
|
|
|
104
107
|
const startedRef = useRef(false);
|
|
105
108
|
|
|
106
109
|
useEffect(() => {
|
|
110
|
+
const abortController = new AbortController();
|
|
107
111
|
async function start() {
|
|
108
112
|
setToken(undefined); // reset token in case we're re-running
|
|
109
113
|
|
|
@@ -112,7 +116,7 @@ export function useWorker(props: {
|
|
|
112
116
|
if (!startedRef.current) {
|
|
113
117
|
startedRef.current = true;
|
|
114
118
|
} else {
|
|
115
|
-
|
|
119
|
+
logger.log("⎔ Detected changes, restarted server.");
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
const assets = await syncAssets(
|
|
@@ -123,7 +127,8 @@ export function useWorker(props: {
|
|
|
123
127
|
// concept of service environments for kv namespaces yet).
|
|
124
128
|
name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
|
|
125
129
|
assetPaths,
|
|
126
|
-
true
|
|
130
|
+
true,
|
|
131
|
+
false
|
|
127
132
|
); // TODO: cancellable?
|
|
128
133
|
|
|
129
134
|
const content = await readFile(bundle.path, "utf-8");
|
|
@@ -171,15 +176,22 @@ export function useWorker(props: {
|
|
|
171
176
|
accountId,
|
|
172
177
|
apiToken,
|
|
173
178
|
},
|
|
174
|
-
{ env: props.env, legacyEnv: props.legacyEnv, zone: props.zone }
|
|
179
|
+
{ env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
|
|
180
|
+
abortController.signal
|
|
175
181
|
)
|
|
176
182
|
);
|
|
177
183
|
}
|
|
178
184
|
start().catch((err) => {
|
|
179
185
|
// we want to log the error, but not end the process
|
|
180
186
|
// since it could recover after the developer fixes whatever's wrong
|
|
181
|
-
|
|
187
|
+
if ((err as { code: string }).code !== "ABORT_ERR") {
|
|
188
|
+
logger.error("Error on remote worker:", err);
|
|
189
|
+
}
|
|
182
190
|
});
|
|
191
|
+
|
|
192
|
+
return () => {
|
|
193
|
+
abortController.abort();
|
|
194
|
+
};
|
|
183
195
|
}, [
|
|
184
196
|
name,
|
|
185
197
|
bundle,
|
package/src/dev/use-esbuild.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
+
import { useApp } from "ink";
|
|
2
3
|
import { useState, useEffect } from "react";
|
|
3
4
|
import { bundleWorker } from "../bundle";
|
|
5
|
+
import { logger } from "../logger";
|
|
4
6
|
import type { Config } from "../config";
|
|
5
7
|
import type { Entry } from "../entry";
|
|
6
8
|
import type { CfModule } from "../worker";
|
|
@@ -23,22 +25,29 @@ export function useEsbuild({
|
|
|
23
25
|
jsxFragment,
|
|
24
26
|
rules,
|
|
25
27
|
serveAssetsFromWorker,
|
|
28
|
+
tsconfig,
|
|
29
|
+
minify,
|
|
30
|
+
nodeCompat,
|
|
26
31
|
}: {
|
|
27
32
|
entry: Entry;
|
|
28
33
|
destination: string | undefined;
|
|
29
|
-
staticRoot:
|
|
34
|
+
staticRoot: string | undefined;
|
|
30
35
|
jsxFactory: string | undefined;
|
|
31
36
|
jsxFragment: string | undefined;
|
|
32
37
|
rules: Config["rules"];
|
|
33
38
|
serveAssetsFromWorker: boolean;
|
|
39
|
+
tsconfig: string | undefined;
|
|
40
|
+
minify: boolean | undefined;
|
|
41
|
+
nodeCompat: boolean | undefined;
|
|
34
42
|
}): EsbuildBundle | undefined {
|
|
35
43
|
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
44
|
+
const { exit } = useApp();
|
|
36
45
|
useEffect(() => {
|
|
37
46
|
let stopWatching: (() => void) | undefined = undefined;
|
|
38
47
|
|
|
39
48
|
const watchMode: WatchMode = {
|
|
40
49
|
async onRebuild(error) {
|
|
41
|
-
if (error)
|
|
50
|
+
if (error) logger.error("Watch build failed:", error);
|
|
42
51
|
else {
|
|
43
52
|
// nothing really changes here, so let's increment the id
|
|
44
53
|
// to change the return object's identity
|
|
@@ -64,6 +73,9 @@ export function useEsbuild({
|
|
|
64
73
|
jsxFragment,
|
|
65
74
|
rules,
|
|
66
75
|
watch: watchMode,
|
|
76
|
+
tsconfig,
|
|
77
|
+
minify,
|
|
78
|
+
nodeCompat,
|
|
67
79
|
});
|
|
68
80
|
|
|
69
81
|
// Capture the `stop()` method to use as the `useEffect()` destructor.
|
|
@@ -79,9 +91,11 @@ export function useEsbuild({
|
|
|
79
91
|
});
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
build().catch(() => {
|
|
83
|
-
// esbuild
|
|
84
|
-
//
|
|
94
|
+
build().catch((err) => {
|
|
95
|
+
// If esbuild fails on first run, we want to quit the process
|
|
96
|
+
// since we can't recover from here
|
|
97
|
+
// related: https://github.com/evanw/esbuild/issues/1037
|
|
98
|
+
exit(err);
|
|
85
99
|
});
|
|
86
100
|
|
|
87
101
|
return () => {
|
|
@@ -95,6 +109,10 @@ export function useEsbuild({
|
|
|
95
109
|
jsxFragment,
|
|
96
110
|
serveAssetsFromWorker,
|
|
97
111
|
rules,
|
|
112
|
+
tsconfig,
|
|
113
|
+
exit,
|
|
114
|
+
minify,
|
|
115
|
+
nodeCompat,
|
|
98
116
|
]);
|
|
99
117
|
return bundle;
|
|
100
118
|
}
|
package/src/dialogs.tsx
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
1
2
|
import { Box, Text, useInput, render } from "ink";
|
|
2
3
|
import TextInput from "ink-text-input";
|
|
3
4
|
import * as React from "react";
|
|
4
5
|
import { useState } from "react";
|
|
6
|
+
import { logger } from "./logger";
|
|
5
7
|
type ConfirmProps = {
|
|
6
8
|
text: string;
|
|
7
9
|
onConfirm: (answer: boolean) => void;
|
|
8
10
|
};
|
|
9
11
|
function Confirm(props: ConfirmProps) {
|
|
10
|
-
useInput((input: string) => {
|
|
11
|
-
if (input === "y") {
|
|
12
|
+
useInput((input: string, key) => {
|
|
13
|
+
if (input === "y" || key.return === true) {
|
|
12
14
|
props.onConfirm(true);
|
|
13
15
|
} else if (input === "n") {
|
|
14
16
|
props.onConfirm(false);
|
|
15
17
|
} else {
|
|
16
|
-
|
|
18
|
+
logger.warn("Unrecognised input:", input);
|
|
17
19
|
}
|
|
18
20
|
});
|
|
19
21
|
return (
|
|
20
22
|
<Box>
|
|
21
|
-
<Text>
|
|
23
|
+
<Text>
|
|
24
|
+
{props.text} ({chalk.bold("y")}/n)
|
|
25
|
+
</Text>
|
|
22
26
|
</Box>
|
|
23
27
|
);
|
|
24
28
|
}
|
|
@@ -39,12 +43,13 @@ export function confirm(text: string): Promise<boolean> {
|
|
|
39
43
|
|
|
40
44
|
type PromptProps = {
|
|
41
45
|
text: string;
|
|
46
|
+
defaultValue?: string;
|
|
42
47
|
type?: "text" | "password";
|
|
43
48
|
onSubmit: (text: string) => void;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
51
|
function Prompt(props: PromptProps) {
|
|
47
|
-
const [value, setValue] = useState("");
|
|
52
|
+
const [value, setValue] = useState(props.defaultValue || "");
|
|
48
53
|
|
|
49
54
|
return (
|
|
50
55
|
<Box>
|
|
@@ -61,11 +66,16 @@ function Prompt(props: PromptProps) {
|
|
|
61
66
|
);
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
export async function prompt(
|
|
69
|
+
export async function prompt(
|
|
70
|
+
text: string,
|
|
71
|
+
type: "text" | "password" = "text",
|
|
72
|
+
defaultValue?: string
|
|
73
|
+
): Promise<string> {
|
|
65
74
|
return new Promise((resolve) => {
|
|
66
75
|
const { unmount } = render(
|
|
67
76
|
<Prompt
|
|
68
77
|
text={text}
|
|
78
|
+
defaultValue={defaultValue}
|
|
69
79
|
type={type}
|
|
70
80
|
onSubmit={(inputText) => {
|
|
71
81
|
unmount();
|