wrangler 0.0.13 → 0.0.17
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/bin/wrangler.js +2 -2
- package/package.json +20 -11
- package/pages/functions/buildWorker.ts +1 -1
- package/pages/functions/filepath-routing.test.ts +112 -28
- package/pages/functions/filepath-routing.ts +44 -51
- package/pages/functions/routes.ts +11 -18
- package/pages/functions/template-worker.ts +3 -9
- package/src/__tests__/dev.test.tsx +42 -5
- package/src/__tests__/guess-worker-format.test.ts +66 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +30 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +43 -9
- package/src/__tests__/helpers/mock-console.ts +62 -0
- package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
- package/src/__tests__/helpers/mock-kv.ts +40 -0
- package/src/__tests__/helpers/mock-user.ts +27 -0
- package/src/__tests__/helpers/mock-web-socket.ts +37 -0
- package/src/__tests__/{run-in-tmp.ts → helpers/run-in-tmp.ts} +1 -1
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +20 -0
- package/src/__tests__/index.test.ts +418 -71
- package/src/__tests__/jest.setup.ts +30 -2
- package/src/__tests__/kv.test.ts +147 -252
- package/src/__tests__/logout.test.ts +50 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/publish.test.ts +1136 -291
- package/src/__tests__/r2.test.ts +206 -0
- package/src/__tests__/secret.test.ts +210 -0
- package/src/__tests__/sentry.test.ts +146 -0
- package/src/__tests__/tail.test.ts +246 -0
- package/src/__tests__/whoami.test.tsx +6 -47
- package/src/api/form_data.ts +75 -25
- package/src/api/preview.ts +2 -2
- package/src/api/worker.ts +34 -15
- package/src/bundle.ts +127 -0
- package/src/cfetch/index.ts +7 -15
- package/src/cfetch/internal.ts +41 -6
- package/src/cli.ts +10 -0
- package/src/config.ts +125 -95
- package/src/dev.tsx +300 -193
- package/src/dialogs.tsx +2 -2
- package/src/guess-worker-format.ts +68 -0
- package/src/index.tsx +578 -192
- package/src/inspect.ts +29 -10
- package/src/kv.tsx +23 -17
- package/src/module-collection.ts +32 -12
- package/src/open-in-browser.ts +13 -0
- package/src/package-manager.ts +120 -0
- package/src/pages.tsx +28 -23
- package/src/paths.ts +26 -0
- package/src/proxy.ts +88 -14
- package/src/publish.ts +260 -297
- package/src/r2.ts +50 -0
- package/src/reporting.ts +115 -0
- package/src/sites.tsx +28 -27
- package/src/tail.tsx +178 -9
- package/src/user.tsx +58 -44
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
- package/wrangler-dist/cli.js +124315 -104677
- package/wrangler-dist/cli.js.map +3 -3
- package/src/__tests__/mock-console.ts +0 -34
- package/src/__tests__/run-wrangler.ts +0 -8
package/src/publish.ts
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import type { Metafile } from "esbuild";
|
|
6
|
-
import { execa } from "execa";
|
|
4
|
+
import { URLSearchParams } from "node:url";
|
|
5
|
+
import { execaCommand } from "execa";
|
|
7
6
|
import tmp from "tmp-promise";
|
|
8
|
-
import type { CfWorkerInit } from "./api/worker";
|
|
9
7
|
import { toFormData } from "./api/form_data";
|
|
8
|
+
import { bundleWorker } from "./bundle";
|
|
10
9
|
import { fetchResult } from "./cfetch";
|
|
10
|
+
import guessWorkerFormat from "./guess-worker-format";
|
|
11
|
+
import { syncAssets } from "./sites";
|
|
12
|
+
import type { CfScriptFormat, CfWorkerInit } from "./api/worker";
|
|
11
13
|
import type { Config } from "./config";
|
|
12
|
-
import makeModuleCollector from "./module-collection";
|
|
13
14
|
import type { AssetPaths } from "./sites";
|
|
14
|
-
import { syncAssets } from "./sites";
|
|
15
|
-
|
|
16
|
-
type CfScriptFormat = undefined | "modules" | "service-worker";
|
|
17
15
|
|
|
18
16
|
type Props = {
|
|
19
17
|
config: Config;
|
|
20
18
|
format: CfScriptFormat | undefined;
|
|
21
|
-
|
|
19
|
+
entry: { file: string; directory: string };
|
|
22
20
|
name: string | undefined;
|
|
23
21
|
env: string | undefined;
|
|
24
22
|
compatibilityDate: string | undefined;
|
|
@@ -37,20 +35,10 @@ function sleep(ms: number) {
|
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
export default async function publish(props: Props): Promise<void> {
|
|
40
|
-
if (props.experimentalPublic && props.format === "service-worker") {
|
|
41
|
-
// TODO: check config too
|
|
42
|
-
throw new Error(
|
|
43
|
-
"You cannot publish in the service worker format with a public directory."
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
38
|
// TODO: warn if git/hg has uncommitted changes
|
|
47
39
|
const { config } = props;
|
|
48
|
-
const {
|
|
49
|
-
|
|
50
|
-
build,
|
|
51
|
-
// @ts-expect-error hidden
|
|
52
|
-
__path__,
|
|
53
|
-
} = config;
|
|
40
|
+
const { account_id: accountId, workers_dev: deployToWorkersDev = true } =
|
|
41
|
+
config;
|
|
54
42
|
|
|
55
43
|
const envRootObj =
|
|
56
44
|
props.env && config.env ? config.env[props.env] || {} : config;
|
|
@@ -91,309 +79,284 @@ export default async function publish(props: Props): Promise<void> {
|
|
|
91
79
|
"A [site] definition requires a `bucket` field with a path to the site's public directory."
|
|
92
80
|
);
|
|
93
81
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
const destination = await tmp.dir({ unsafeCleanup: true });
|
|
83
|
+
try {
|
|
84
|
+
if (props.legacyEnv) {
|
|
85
|
+
scriptName += props.env ? `-${props.env}` : "";
|
|
86
|
+
}
|
|
87
|
+
const envName = props.env ?? "production";
|
|
88
|
+
|
|
89
|
+
if (props.config.build?.command) {
|
|
90
|
+
// TODO: add a deprecation message here?
|
|
91
|
+
console.log("running:", props.config.build.command);
|
|
92
|
+
await execaCommand(props.config.build.command, {
|
|
93
|
+
shell: true,
|
|
94
|
+
stdout: "inherit",
|
|
95
|
+
stderr: "inherit",
|
|
96
|
+
timeout: 1000 * 30,
|
|
97
|
+
...(props.config.build?.cwd && { cwd: props.config.build.cwd }),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
let fileExists = false;
|
|
101
|
+
try {
|
|
102
|
+
// Use require.resolve to use node's resolution algorithm,
|
|
103
|
+
// this lets us use paths without explicit .js extension
|
|
104
|
+
// TODO: we should probably remove this, because it doesn't
|
|
105
|
+
// take into consideration other extensions like .tsx, .ts, .jsx, etc
|
|
106
|
+
fileExists = existsSync(require.resolve(props.entry.file));
|
|
107
|
+
} catch (e) {
|
|
108
|
+
// fail silently, usually means require.resolve threw MODULE_NOT_FOUND
|
|
109
|
+
}
|
|
110
|
+
if (fileExists === false) {
|
|
111
|
+
throw new Error(`Could not resolve "${props.entry.file}".`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const format = await guessWorkerFormat(props.entry, props.format);
|
|
116
|
+
|
|
117
|
+
if (props.experimentalPublic && format === "service-worker") {
|
|
118
|
+
// TODO: check config too
|
|
101
119
|
throw new Error(
|
|
102
|
-
"
|
|
120
|
+
"You cannot publish in the service worker format with a public directory."
|
|
103
121
|
);
|
|
104
122
|
}
|
|
105
|
-
file = path.resolve(path.dirname(__path__), build.upload.main);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (props.legacyEnv) {
|
|
109
|
-
scriptName += props.env ? `-${props.env}` : "";
|
|
110
|
-
}
|
|
111
|
-
const envName = props.env ?? "production";
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await execa(buildCommandPieces[0], buildCommandPieces.slice(1), {
|
|
119
|
-
stdout: "inherit",
|
|
120
|
-
stderr: "inherit",
|
|
121
|
-
...(props.config.build?.cwd && { cwd: props.config.build.cwd }),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
+
if ("wasm_modules" in config && format === "modules") {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"utf8"
|
|
134
|
-
)
|
|
135
|
-
).replace("__ENTRY_POINT__", file),
|
|
136
|
-
sourcefile: "static-asset-facade.js",
|
|
137
|
-
resolveDir: path.dirname(file),
|
|
138
|
-
},
|
|
139
|
-
nodePaths: [path.join(__dirname, "../vendor")],
|
|
140
|
-
}
|
|
141
|
-
: { entryPoints: [file] }),
|
|
142
|
-
bundle: true,
|
|
143
|
-
outdir: destination.path,
|
|
144
|
-
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
145
|
-
format: "esm",
|
|
146
|
-
sourcemap: true,
|
|
147
|
-
metafile: true,
|
|
148
|
-
conditions: ["worker", "browser"],
|
|
149
|
-
loader: {
|
|
150
|
-
".js": "jsx",
|
|
151
|
-
},
|
|
152
|
-
plugins: [moduleCollector.plugin],
|
|
153
|
-
...(jsxFactory && { jsxFactory }),
|
|
154
|
-
...(jsxFragment && { jsxFragment }),
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// result.metafile is defined because of the `metafile: true` option above.
|
|
158
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
159
|
-
const metafile = result.metafile!;
|
|
160
|
-
const entryPoints = Object.entries(metafile.outputs).filter(
|
|
161
|
-
([_path, output]) => output.entryPoint !== undefined
|
|
162
|
-
);
|
|
163
|
-
assert(
|
|
164
|
-
entryPoints.length > 0,
|
|
165
|
-
`Cannot find entry-point "${file}" in generated bundle.` +
|
|
166
|
-
listEntryPoints(entryPoints)
|
|
167
|
-
);
|
|
168
|
-
assert(
|
|
169
|
-
entryPoints.length < 2,
|
|
170
|
-
"More than one entry-point found for generated bundle." +
|
|
171
|
-
listEntryPoints(entryPoints)
|
|
172
|
-
);
|
|
173
|
-
const entryPointExports = entryPoints[0][1].exports;
|
|
174
|
-
const resolvedEntryPointPath = entryPoints[0][0];
|
|
175
|
-
const { format } = props;
|
|
176
|
-
const bundle = {
|
|
177
|
-
type: entryPointExports.length > 0 ? "esm" : "commonjs",
|
|
178
|
-
exports: entryPointExports,
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// TODO: instead of bundling the facade with the worker, we should just bundle the worker and expose it as a module.
|
|
182
|
-
// That way we'll be able to accurately tell if this is a service worker or not.
|
|
183
|
-
|
|
184
|
-
if (format === "modules" && bundle.type === "commonjs") {
|
|
185
|
-
console.error("⎔ Cannot use modules with a commonjs bundle.");
|
|
186
|
-
// TODO: a much better error message here, with what to do next
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (format === "service-worker" && bundle.type !== "esm") {
|
|
190
|
-
console.error("⎔ Cannot use service-worker with a esm bundle.");
|
|
191
|
-
// TODO: a much better error message here, with what to do next
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
130
|
+
const { modules, resolvedEntryPointPath, bundleType } = await bundleWorker(
|
|
131
|
+
props.entry,
|
|
132
|
+
props.experimentalPublic,
|
|
133
|
+
destination.path,
|
|
134
|
+
jsxFactory,
|
|
135
|
+
jsxFragment,
|
|
136
|
+
format
|
|
137
|
+
);
|
|
194
138
|
|
|
195
|
-
|
|
196
|
-
|
|
139
|
+
let content = readFileSync(resolvedEntryPointPath, {
|
|
140
|
+
encoding: "utf-8",
|
|
141
|
+
});
|
|
197
142
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
211
|
-
if (foundIndex === -1) {
|
|
212
|
-
console.warn(
|
|
213
|
-
`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...`
|
|
143
|
+
// if config.migrations
|
|
144
|
+
// get current migration tag
|
|
145
|
+
let migrations;
|
|
146
|
+
if (config.migrations !== undefined) {
|
|
147
|
+
const scripts = await fetchResult<
|
|
148
|
+
{ id: string; migration_tag: string }[]
|
|
149
|
+
>(`/accounts/${accountId}/workers/scripts`);
|
|
150
|
+
const script = scripts.find(({ id }) => id === scriptName);
|
|
151
|
+
if (script?.migration_tag) {
|
|
152
|
+
// was already published once
|
|
153
|
+
const foundIndex = config.migrations.findIndex(
|
|
154
|
+
(migration) => migration.tag === script.migration_tag
|
|
214
155
|
);
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
156
|
+
if (foundIndex === -1) {
|
|
157
|
+
console.warn(
|
|
158
|
+
`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...`
|
|
159
|
+
);
|
|
160
|
+
migrations = {
|
|
161
|
+
old_tag: script.migration_tag,
|
|
162
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
163
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
migrations = {
|
|
167
|
+
old_tag: script.migration_tag,
|
|
168
|
+
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
169
|
+
steps: config.migrations
|
|
170
|
+
.slice(foundIndex + 1)
|
|
171
|
+
.map(({ tag: _tag, ...rest }) => rest),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
220
174
|
} else {
|
|
221
175
|
migrations = {
|
|
222
|
-
old_tag: script.migration_tag,
|
|
223
176
|
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
224
|
-
steps: config.migrations
|
|
225
|
-
.slice(foundIndex + 1)
|
|
226
|
-
.map(({ tag: _tag, ...rest }) => rest),
|
|
177
|
+
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
227
178
|
};
|
|
228
179
|
}
|
|
229
|
-
} else {
|
|
230
|
-
migrations = {
|
|
231
|
-
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
232
|
-
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
233
|
-
};
|
|
234
180
|
}
|
|
235
|
-
}
|
|
236
181
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
182
|
+
const assets = await syncAssets(
|
|
183
|
+
accountId,
|
|
184
|
+
scriptName,
|
|
185
|
+
props.assetPaths,
|
|
186
|
+
false,
|
|
187
|
+
props.env
|
|
188
|
+
);
|
|
243
189
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
: []
|
|
272
|
-
),
|
|
273
|
-
compatibility_date: config.compatibility_date,
|
|
274
|
-
compatibility_flags: config.compatibility_flags,
|
|
275
|
-
usage_model: config.usage_model,
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const start = Date.now();
|
|
279
|
-
function formatTime(duration: number) {
|
|
280
|
-
return `(${(duration / 1000).toFixed(2)} sec)`;
|
|
281
|
-
}
|
|
190
|
+
const bindings: CfWorkerInit["bindings"] = {
|
|
191
|
+
kv_namespaces: (envRootObj.kv_namespaces || []).concat(
|
|
192
|
+
assets.namespace
|
|
193
|
+
? { binding: "__STATIC_CONTENT", id: assets.namespace }
|
|
194
|
+
: []
|
|
195
|
+
),
|
|
196
|
+
vars: envRootObj.vars,
|
|
197
|
+
wasm_modules: config.wasm_modules,
|
|
198
|
+
durable_objects: envRootObj.durable_objects,
|
|
199
|
+
r2_buckets: envRootObj.r2_buckets,
|
|
200
|
+
unsafe: envRootObj.unsafe?.bindings,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
if (assets.manifest) {
|
|
204
|
+
if (bundleType === "esm") {
|
|
205
|
+
modules.push({
|
|
206
|
+
name: "__STATIC_CONTENT_MANIFEST",
|
|
207
|
+
content: JSON.stringify(assets.manifest),
|
|
208
|
+
type: "text",
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
content = `const __STATIC_CONTENT_MANIFEST = ${JSON.stringify(
|
|
212
|
+
assets.manifest
|
|
213
|
+
)};\n${content}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
282
216
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
217
|
+
const worker: CfWorkerInit = {
|
|
218
|
+
name: scriptName,
|
|
219
|
+
main: {
|
|
220
|
+
name: path.basename(resolvedEntryPointPath),
|
|
221
|
+
content: content,
|
|
222
|
+
type: bundleType,
|
|
223
|
+
},
|
|
224
|
+
bindings,
|
|
225
|
+
migrations,
|
|
226
|
+
modules,
|
|
227
|
+
compatibility_date: config.compatibility_date,
|
|
228
|
+
compatibility_flags: config.compatibility_flags,
|
|
229
|
+
usage_model: config.usage_model,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const start = Date.now();
|
|
233
|
+
const notProd = !props.legacyEnv && props.env;
|
|
234
|
+
const workerName = notProd ? `${scriptName} (${envName})` : scriptName;
|
|
235
|
+
const workerUrl = notProd
|
|
236
|
+
? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
|
|
237
|
+
: `/accounts/${accountId}/workers/scripts/${scriptName}`;
|
|
238
|
+
|
|
239
|
+
// Upload the script so it has time to propagate.
|
|
240
|
+
const { available_on_subdomain } = await fetchResult(
|
|
241
|
+
workerUrl,
|
|
242
|
+
{
|
|
243
|
+
method: "PUT",
|
|
244
|
+
body: toFormData(worker),
|
|
245
|
+
},
|
|
246
|
+
new URLSearchParams({ available_on_subdomain: "true" })
|
|
247
|
+
);
|
|
299
248
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
249
|
+
const uploadMs = Date.now() - start;
|
|
250
|
+
console.log("Uploaded", workerName, formatTime(uploadMs));
|
|
251
|
+
const deployments: Promise<string[]>[] = [];
|
|
252
|
+
|
|
253
|
+
const userSubdomain = (
|
|
254
|
+
await fetchResult<{ subdomain: string }>(
|
|
255
|
+
`/accounts/${accountId}/workers/subdomain`
|
|
256
|
+
)
|
|
257
|
+
).subdomain;
|
|
258
|
+
|
|
259
|
+
if (deployToWorkersDev) {
|
|
260
|
+
// Deploy to a subdomain of `workers.dev`
|
|
261
|
+
const scriptURL =
|
|
262
|
+
props.legacyEnv || !props.env
|
|
263
|
+
? `${scriptName}.${userSubdomain}.workers.dev`
|
|
264
|
+
: `${envName}.${scriptName}.${userSubdomain}.workers.dev`;
|
|
265
|
+
if (!available_on_subdomain) {
|
|
266
|
+
// Enable the `workers.dev` subdomain.
|
|
267
|
+
deployments.push(
|
|
268
|
+
fetchResult(`${workerUrl}/subdomain`, {
|
|
269
|
+
method: "POST",
|
|
270
|
+
body: JSON.stringify({ enabled: true }),
|
|
271
|
+
headers: {
|
|
272
|
+
"Content-Type": "application/json",
|
|
273
|
+
},
|
|
274
|
+
})
|
|
275
|
+
.then(() => [scriptURL])
|
|
276
|
+
// Add a delay when the subdomain is first created.
|
|
277
|
+
// This is to prevent an issue where a negative cache-hit
|
|
278
|
+
// causes the subdomain to be unavailable for 30 seconds.
|
|
279
|
+
// This is a temporary measure until we fix this on the edge.
|
|
280
|
+
.then(async (url) => {
|
|
281
|
+
await sleep(3000);
|
|
282
|
+
return url;
|
|
283
|
+
})
|
|
284
|
+
);
|
|
285
|
+
} else {
|
|
286
|
+
deployments.push(Promise.resolve([scriptURL]));
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
// Disable the workers.dev deployment
|
|
319
290
|
fetchResult(`${workerUrl}/subdomain`, {
|
|
320
291
|
method: "POST",
|
|
321
|
-
body: JSON.stringify({ enabled:
|
|
292
|
+
body: JSON.stringify({ enabled: false }),
|
|
322
293
|
headers: {
|
|
323
294
|
"Content-Type": "application/json",
|
|
324
295
|
},
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
// Add a delay when the subdomain is first created.
|
|
328
|
-
// This is to prevent an issue where a negative cache-hit
|
|
329
|
-
// causes the subdomain to be unavailable for 30 seconds.
|
|
330
|
-
// This is a temporary measure until we fix this on the edge.
|
|
331
|
-
.then(async (url) => {
|
|
332
|
-
await sleep(3000);
|
|
333
|
-
return url;
|
|
334
|
-
})
|
|
335
|
-
);
|
|
336
|
-
} else {
|
|
337
|
-
deployments.push(Promise.resolve([scriptURL]));
|
|
338
|
-
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
339
298
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
299
|
+
// Update routing table for the script.
|
|
300
|
+
if (routes && routes.length) {
|
|
301
|
+
deployments.push(
|
|
302
|
+
fetchResult(`${workerUrl}/routes`, {
|
|
303
|
+
// TODO: PATCH will not delete previous routes on this script,
|
|
304
|
+
// whereas PUT will. We need to decide on the default behaviour
|
|
305
|
+
// and how to configure it.
|
|
306
|
+
method: "PUT",
|
|
307
|
+
body: JSON.stringify(routes.map((pattern) => ({ pattern }))),
|
|
308
|
+
headers: {
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
},
|
|
311
|
+
}).then(() => {
|
|
312
|
+
if (routes.length > 10) {
|
|
313
|
+
return routes
|
|
314
|
+
.slice(0, 9)
|
|
315
|
+
.map(String)
|
|
316
|
+
.concat([`...and ${routes.length - 10} more routes`]);
|
|
317
|
+
}
|
|
318
|
+
return routes.map(String);
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
}
|
|
363
322
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
323
|
+
// Configure any schedules for the script.
|
|
324
|
+
// TODO: rename this to `schedules`?
|
|
325
|
+
if (triggers && triggers.length) {
|
|
326
|
+
deployments.push(
|
|
327
|
+
fetchResult(`${workerUrl}/schedules`, {
|
|
328
|
+
// TODO: Unlike routes, this endpoint does not support PATCH.
|
|
329
|
+
// So technically, this will override any previous schedules.
|
|
330
|
+
// We should change the endpoint to support PATCH.
|
|
331
|
+
method: "PUT",
|
|
332
|
+
body: JSON.stringify(triggers.map((cron) => ({ cron }))),
|
|
333
|
+
headers: {
|
|
334
|
+
"Content-Type": "application/json",
|
|
335
|
+
},
|
|
336
|
+
}).then(() => triggers.map(String))
|
|
337
|
+
);
|
|
338
|
+
}
|
|
380
339
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
340
|
+
const targets = await Promise.all(deployments);
|
|
341
|
+
const deployMs = Date.now() - start - uploadMs;
|
|
384
342
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
343
|
+
if (deployments.length > 0) {
|
|
344
|
+
console.log("Deployed", workerName, formatTime(deployMs));
|
|
345
|
+
for (const target of targets.flat()) {
|
|
346
|
+
console.log(" ", target);
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
console.log(
|
|
350
|
+
"No deployment targets for",
|
|
351
|
+
workerName,
|
|
352
|
+
formatTime(deployMs)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
} finally {
|
|
356
|
+
await destination.cleanup();
|
|
390
357
|
}
|
|
391
358
|
}
|
|
392
359
|
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
): string {
|
|
396
|
-
return outputs.map(([_input, output]) => output.entryPoint).join("\n");
|
|
360
|
+
function formatTime(duration: number) {
|
|
361
|
+
return `(${(duration / 1000).toFixed(2)} sec)`;
|
|
397
362
|
}
|
|
398
|
-
|
|
399
|
-
type ValueOf<T> = T[keyof T];
|
package/src/r2.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { fetchResult } from "./cfetch";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Information about a bucket, returned from `listR2Buckets()`.
|
|
5
|
+
*/
|
|
6
|
+
export interface R2BucketInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
creation_date: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fetch a list of all the buckets under the given `accountId`.
|
|
13
|
+
*/
|
|
14
|
+
export async function listR2Buckets(
|
|
15
|
+
accountId: string
|
|
16
|
+
): Promise<R2BucketInfo[]> {
|
|
17
|
+
const results = await fetchResult<{
|
|
18
|
+
buckets: R2BucketInfo[];
|
|
19
|
+
}>(`/accounts/${accountId}/r2/buckets`);
|
|
20
|
+
return results.buckets;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a bucket with the given `bucketName` within the account given by `accountId`.
|
|
25
|
+
*
|
|
26
|
+
* A 400 is returned if the account already owns a bucket with this name.
|
|
27
|
+
* A bucket must be explicitly deleted to be replaced.
|
|
28
|
+
*/
|
|
29
|
+
export async function createR2Bucket(
|
|
30
|
+
accountId: string,
|
|
31
|
+
bucketName: string
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
return await fetchResult<void>(
|
|
34
|
+
`/accounts/${accountId}/r2/buckets/${bucketName}`,
|
|
35
|
+
{ method: "PUT" }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Delete a bucket with the given name
|
|
41
|
+
*/
|
|
42
|
+
export async function deleteR2Bucket(
|
|
43
|
+
accountId: string,
|
|
44
|
+
bucketName: string
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
return await fetchResult<void>(
|
|
47
|
+
`/accounts/${accountId}/r2/buckets/${bucketName}`,
|
|
48
|
+
{ method: "DELETE" }
|
|
49
|
+
);
|
|
50
|
+
}
|