wrangler 0.0.0-e2a26a0 → 0.0.0-e2b3306e1
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 +44 -25
- package/bin/wrangler.js +95 -32
- package/config-schema.json +3370 -0
- package/kv-asset-handler.js +1 -0
- package/package.json +146 -92
- package/templates/__tests__/pages-dev-util.test.ts +128 -0
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/checked-fetch.js +30 -0
- package/templates/facade.d.ts +19 -0
- package/templates/gitignore +170 -0
- package/templates/init-tests/test-jest-new-worker.js +23 -0
- package/templates/init-tests/test-vitest-new-worker.js +24 -0
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/common.ts +67 -0
- package/templates/middleware/loader-modules.ts +134 -0
- package/templates/middleware/loader-sw.ts +229 -0
- package/templates/middleware/middleware-ensure-req-body-drained.ts +18 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
- package/templates/middleware/middleware-mock-analytics-engine.d.ts +3 -0
- package/templates/middleware/middleware-mock-analytics-engine.ts +30 -0
- package/templates/middleware/middleware-patch-console-prefix.d.ts +3 -0
- package/templates/middleware/middleware-patch-console-prefix.ts +21 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +29 -0
- package/templates/middleware/middleware-serve-static-assets.d.ts +6 -0
- package/templates/middleware/middleware-serve-static-assets.ts +56 -0
- package/templates/modules-watch-stub.js +4 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.js +4 -4
- package/templates/new-worker.ts +22 -4
- package/templates/no-op-worker.js +10 -0
- package/templates/pages-dev-pipeline.ts +32 -0
- package/templates/pages-dev-util.ts +55 -0
- package/templates/pages-shim.ts +9 -0
- package/templates/pages-template-plugin.ts +190 -0
- package/templates/pages-template-worker.ts +198 -0
- package/templates/startDevWorker/InspectorProxyWorker.ts +664 -0
- package/templates/startDevWorker/ProxyWorker.ts +336 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +22 -0
- package/templates/tsconfig.json +8 -0
- package/wrangler-dist/InspectorProxyWorker.js +464 -0
- package/wrangler-dist/InspectorProxyWorker.js.map +6 -0
- package/wrangler-dist/ProxyWorker.js +241 -0
- package/wrangler-dist/ProxyWorker.js.map +6 -0
- package/wrangler-dist/cli.d.ts +26730 -0
- package/wrangler-dist/cli.js +165374 -118597
- package/import_meta_url.js +0 -3
- package/miniflare-config-stubs/.env.empty +0 -0
- package/miniflare-config-stubs/package.empty.json +0 -1
- package/miniflare-config-stubs/wrangler.empty.toml +0 -0
- package/pages/functions/buildWorker.ts +0 -62
- package/pages/functions/filepath-routing.test.ts +0 -47
- package/pages/functions/filepath-routing.ts +0 -231
- package/pages/functions/identifiers.ts +0 -78
- package/pages/functions/routes.ts +0 -160
- package/pages/functions/template-worker.ts +0 -136
- package/src/__tests__/dev.test.tsx +0 -71
- package/src/__tests__/helpers/clipboardy-mock.js +0 -4
- package/src/__tests__/helpers/cmd-shim.d.ts +0 -11
- package/src/__tests__/helpers/mock-account-id.ts +0 -30
- package/src/__tests__/helpers/mock-bin.ts +0 -33
- package/src/__tests__/helpers/mock-cfetch.ts +0 -207
- package/src/__tests__/helpers/mock-console.ts +0 -56
- package/src/__tests__/helpers/mock-dialogs.ts +0 -65
- package/src/__tests__/helpers/mock-kv.ts +0 -40
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/helpers/run-in-tmp.ts +0 -29
- package/src/__tests__/helpers/run-wrangler.ts +0 -14
- package/src/__tests__/index.test.ts +0 -543
- package/src/__tests__/jest.setup.ts +0 -37
- package/src/__tests__/kv.test.ts +0 -1139
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/package-manager.test.ts +0 -206
- package/src/__tests__/publish.test.ts +0 -1222
- package/src/__tests__/secret.test.ts +0 -210
- package/src/__tests__/whoami.test.tsx +0 -86
- package/src/api/form_data.ts +0 -134
- package/src/api/preview.ts +0 -128
- package/src/api/worker.ts +0 -155
- package/src/cfetch/index.ts +0 -102
- package/src/cfetch/internal.ts +0 -101
- package/src/cli.ts +0 -9
- package/src/config.ts +0 -641
- package/src/dev.tsx +0 -834
- package/src/dialogs.tsx +0 -77
- package/src/index.tsx +0 -2101
- package/src/inspect.ts +0 -596
- package/src/kv.tsx +0 -284
- package/src/module-collection.ts +0 -63
- package/src/package-manager.ts +0 -120
- package/src/pages.tsx +0 -1046
- package/src/paths.ts +0 -26
- package/src/proxy.ts +0 -370
- package/src/publish.ts +0 -410
- package/src/sites.tsx +0 -240
- package/src/tail.tsx +0 -73
- package/src/user.tsx +0 -1019
- package/src/whoami.tsx +0 -69
- package/templates/static-asset-facade.js +0 -47
- 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
- package/wrangler-dist/cli.js.map +0 -7
package/src/publish.ts
DELETED
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { URLSearchParams } from "node:url";
|
|
5
|
-
import * as esbuild from "esbuild";
|
|
6
|
-
import { execaCommand } from "execa";
|
|
7
|
-
import tmp from "tmp-promise";
|
|
8
|
-
import { toFormData } from "./api/form_data";
|
|
9
|
-
import { fetchResult } from "./cfetch";
|
|
10
|
-
import makeModuleCollector from "./module-collection";
|
|
11
|
-
import { syncAssets } from "./sites";
|
|
12
|
-
import type { CfWorkerInit } from "./api/worker";
|
|
13
|
-
import type { Config } from "./config";
|
|
14
|
-
import type { AssetPaths } from "./sites";
|
|
15
|
-
import type { Metafile } from "esbuild";
|
|
16
|
-
|
|
17
|
-
type CfScriptFormat = undefined | "modules" | "service-worker";
|
|
18
|
-
|
|
19
|
-
type Props = {
|
|
20
|
-
config: Config;
|
|
21
|
-
format: CfScriptFormat | undefined;
|
|
22
|
-
script: string | undefined;
|
|
23
|
-
name: string | undefined;
|
|
24
|
-
env: string | undefined;
|
|
25
|
-
compatibilityDate: string | undefined;
|
|
26
|
-
compatibilityFlags: string[] | undefined;
|
|
27
|
-
assetPaths: AssetPaths | undefined;
|
|
28
|
-
triggers: (string | number)[] | undefined;
|
|
29
|
-
routes: (string | number)[] | undefined;
|
|
30
|
-
legacyEnv: boolean | undefined;
|
|
31
|
-
jsxFactory: undefined | string;
|
|
32
|
-
jsxFragment: undefined | string;
|
|
33
|
-
experimentalPublic: boolean;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
function sleep(ms: number) {
|
|
37
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default async function publish(props: Props): Promise<void> {
|
|
41
|
-
if (props.experimentalPublic && props.format === "service-worker") {
|
|
42
|
-
// TODO: check config too
|
|
43
|
-
throw new Error(
|
|
44
|
-
"You cannot publish in the service worker format with a public directory."
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
// TODO: warn if git/hg has uncommitted changes
|
|
48
|
-
const { config } = props;
|
|
49
|
-
const {
|
|
50
|
-
account_id: accountId,
|
|
51
|
-
build,
|
|
52
|
-
// @ts-expect-error hidden
|
|
53
|
-
__path__,
|
|
54
|
-
} = config;
|
|
55
|
-
|
|
56
|
-
const envRootObj =
|
|
57
|
-
props.env && config.env ? config.env[props.env] || {} : config;
|
|
58
|
-
|
|
59
|
-
assert(
|
|
60
|
-
envRootObj.compatibility_date || props.compatibilityDate,
|
|
61
|
-
"A compatibility_date is required when publishing. Add one to your wrangler.toml file, or pass it in your terminal as --compatibility_date. See https://developers.cloudflare.com/workers/platform/compatibility-dates for more information."
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
if (accountId === undefined) {
|
|
65
|
-
throw new Error("No account_id provided.");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const triggers = props.triggers || config.triggers?.crons;
|
|
69
|
-
const routes = props.routes || config.routes;
|
|
70
|
-
|
|
71
|
-
const jsxFactory = props.jsxFactory || config.jsx_factory;
|
|
72
|
-
const jsxFragment = props.jsxFragment || config.jsx_fragment;
|
|
73
|
-
|
|
74
|
-
assert(config.account_id, "missing account id");
|
|
75
|
-
|
|
76
|
-
let scriptName = props.name || config.name;
|
|
77
|
-
assert(
|
|
78
|
-
scriptName,
|
|
79
|
-
'You need to provide a name when publishing a worker. Either pass it as a cli arg with `--name <name>` or in your config file as `name = "<name>"`'
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
if (config.site?.["entry-point"]) {
|
|
83
|
-
console.warn(
|
|
84
|
-
"Deprecation notice: The `site.entry-point` config field is no longer used.\n" +
|
|
85
|
-
"The entry-point should be specified via the command line (e.g. `wrangler publish path/to/script`) or the `build.upload.main` config field.\n" +
|
|
86
|
-
"Please remove the `site.entry-point` field from the `wrangler.toml` file."
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
assert(
|
|
91
|
-
!config.site || config.site.bucket,
|
|
92
|
-
"A [site] definition requires a `bucket` field with a path to the site's public directory."
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
let file: string;
|
|
96
|
-
if (props.script) {
|
|
97
|
-
// If the script name comes from the command line it is relative to the current working directory.
|
|
98
|
-
file = path.resolve(props.script);
|
|
99
|
-
} else {
|
|
100
|
-
// If the script name comes from the config, then it is relative to the wrangler.toml file.
|
|
101
|
-
if (build?.upload?.main === undefined) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
"Missing entry-point: The entry-point should be specified via the command line (e.g. `wrangler publish path/to/script`) or the `build.upload.main` config field."
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
file = path.resolve(path.dirname(__path__), build.upload.main);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (props.legacyEnv) {
|
|
110
|
-
scriptName += props.env ? `-${props.env}` : "";
|
|
111
|
-
}
|
|
112
|
-
const envName = props.env ?? "production";
|
|
113
|
-
|
|
114
|
-
const destination = await tmp.dir({ unsafeCleanup: true });
|
|
115
|
-
if (props.config.build?.command) {
|
|
116
|
-
// TODO: add a deprecation message here?
|
|
117
|
-
console.log("running:", props.config.build.command);
|
|
118
|
-
await execaCommand(props.config.build.command, {
|
|
119
|
-
shell: true,
|
|
120
|
-
stdout: "inherit",
|
|
121
|
-
stderr: "inherit",
|
|
122
|
-
...(props.config.build?.cwd && { cwd: props.config.build.cwd }),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const moduleCollector = makeModuleCollector();
|
|
127
|
-
const result = await esbuild.build({
|
|
128
|
-
...(props.experimentalPublic
|
|
129
|
-
? {
|
|
130
|
-
stdin: {
|
|
131
|
-
contents: (
|
|
132
|
-
await readFile(
|
|
133
|
-
path.join(__dirname, "../templates/static-asset-facade.js"),
|
|
134
|
-
"utf8"
|
|
135
|
-
)
|
|
136
|
-
).replace("__ENTRY_POINT__", file),
|
|
137
|
-
sourcefile: "static-asset-facade.js",
|
|
138
|
-
resolveDir: path.dirname(file),
|
|
139
|
-
},
|
|
140
|
-
nodePaths: [path.join(__dirname, "../vendor")],
|
|
141
|
-
}
|
|
142
|
-
: { entryPoints: [file] }),
|
|
143
|
-
bundle: true,
|
|
144
|
-
outdir: destination.path,
|
|
145
|
-
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
146
|
-
format: "esm",
|
|
147
|
-
sourcemap: true,
|
|
148
|
-
metafile: true,
|
|
149
|
-
conditions: ["worker", "browser"],
|
|
150
|
-
loader: {
|
|
151
|
-
".js": "jsx",
|
|
152
|
-
".html": "text",
|
|
153
|
-
".pem": "text",
|
|
154
|
-
".txt": "text",
|
|
155
|
-
},
|
|
156
|
-
plugins: [moduleCollector.plugin],
|
|
157
|
-
...(jsxFactory && { jsxFactory }),
|
|
158
|
-
...(jsxFragment && { jsxFragment }),
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// result.metafile is defined because of the `metafile: true` option above.
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
163
|
-
const metafile = result.metafile!;
|
|
164
|
-
const entryPoints = Object.entries(metafile.outputs).filter(
|
|
165
|
-
([_path, output]) => output.entryPoint !== undefined
|
|
166
|
-
);
|
|
167
|
-
assert(
|
|
168
|
-
entryPoints.length > 0,
|
|
169
|
-
`Cannot find entry-point "${file}" in generated bundle.` +
|
|
170
|
-
listEntryPoints(entryPoints)
|
|
171
|
-
);
|
|
172
|
-
assert(
|
|
173
|
-
entryPoints.length < 2,
|
|
174
|
-
"More than one entry-point found for generated bundle." +
|
|
175
|
-
listEntryPoints(entryPoints)
|
|
176
|
-
);
|
|
177
|
-
const entryPointExports = entryPoints[0][1].exports;
|
|
178
|
-
const resolvedEntryPointPath = entryPoints[0][0];
|
|
179
|
-
const { format } = props;
|
|
180
|
-
const bundle = {
|
|
181
|
-
type: entryPointExports.length > 0 ? "esm" : "commonjs",
|
|
182
|
-
exports: entryPointExports,
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// TODO: instead of bundling the facade with the worker, we should just bundle the worker and expose it as a module.
|
|
186
|
-
// That way we'll be able to accurately tell if this is a service worker or not.
|
|
187
|
-
|
|
188
|
-
if (format === "modules" && bundle.type === "commonjs") {
|
|
189
|
-
console.error("⎔ Cannot use modules with a commonjs bundle.");
|
|
190
|
-
// TODO: a much better error message here, with what to do next
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
if (format === "service-worker" && bundle.type !== "esm") {
|
|
194
|
-
console.error("⎔ Cannot use service-worker with a esm bundle.");
|
|
195
|
-
// TODO: a much better error message here, with what to do next
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
let content = await readFile(resolvedEntryPointPath, { encoding: "utf-8" });
|
|
200
|
-
await destination.cleanup();
|
|
201
|
-
|
|
202
|
-
// if config.migrations
|
|
203
|
-
// get current migration tag
|
|
204
|
-
let migrations;
|
|
205
|
-
if (config.migrations !== undefined) {
|
|
206
|
-
const scripts = await fetchResult<{ id: string; migration_tag: string }[]>(
|
|
207
|
-
`/accounts/${accountId}/workers/scripts`
|
|
208
|
-
);
|
|
209
|
-
const script = scripts.find(({ id }) => id === scriptName);
|
|
210
|
-
if (script?.migration_tag) {
|
|
211
|
-
// was already published once
|
|
212
|
-
const foundIndex = config.migrations.findIndex(
|
|
213
|
-
(migration) => migration.tag === script.migration_tag
|
|
214
|
-
);
|
|
215
|
-
if (foundIndex === -1) {
|
|
216
|
-
console.warn(
|
|
217
|
-
`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...`
|
|
218
|
-
);
|
|
219
|
-
migrations = {
|
|
220
|
-
old_tag: script.migration_tag,
|
|
221
|
-
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
222
|
-
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
223
|
-
};
|
|
224
|
-
} else {
|
|
225
|
-
migrations = {
|
|
226
|
-
old_tag: script.migration_tag,
|
|
227
|
-
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
228
|
-
steps: config.migrations
|
|
229
|
-
.slice(foundIndex + 1)
|
|
230
|
-
.map(({ tag: _tag, ...rest }) => rest),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
migrations = {
|
|
235
|
-
new_tag: config.migrations[config.migrations.length - 1].tag,
|
|
236
|
-
steps: config.migrations.map(({ tag: _tag, ...rest }) => rest),
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const assets = await syncAssets(
|
|
242
|
-
accountId,
|
|
243
|
-
scriptName,
|
|
244
|
-
props.assetPaths,
|
|
245
|
-
false,
|
|
246
|
-
props.env
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
const bindings: CfWorkerInit["bindings"] = {
|
|
250
|
-
kv_namespaces: (envRootObj.kv_namespaces || []).concat(
|
|
251
|
-
assets.namespace
|
|
252
|
-
? { binding: "__STATIC_CONTENT", id: assets.namespace }
|
|
253
|
-
: []
|
|
254
|
-
),
|
|
255
|
-
vars: envRootObj.vars,
|
|
256
|
-
durable_objects: envRootObj.durable_objects,
|
|
257
|
-
services: envRootObj.experimental_services,
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const workerType = bundle.type === "esm" ? "esm" : "commonjs";
|
|
261
|
-
if (workerType !== "esm" && assets.manifest) {
|
|
262
|
-
content = `const __STATIC_CONTENT_MANIFEST = ${JSON.stringify(
|
|
263
|
-
assets.manifest
|
|
264
|
-
)};\n${content}`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const worker: CfWorkerInit = {
|
|
268
|
-
name: scriptName,
|
|
269
|
-
main: {
|
|
270
|
-
name: path.basename(resolvedEntryPointPath),
|
|
271
|
-
content: content,
|
|
272
|
-
type: workerType,
|
|
273
|
-
},
|
|
274
|
-
bindings,
|
|
275
|
-
...(migrations && { migrations }),
|
|
276
|
-
modules: moduleCollector.modules.concat(
|
|
277
|
-
assets.manifest && workerType === "esm"
|
|
278
|
-
? {
|
|
279
|
-
name: "__STATIC_CONTENT_MANIFEST",
|
|
280
|
-
content: JSON.stringify(assets.manifest),
|
|
281
|
-
type: "text",
|
|
282
|
-
}
|
|
283
|
-
: []
|
|
284
|
-
),
|
|
285
|
-
compatibility_date: config.compatibility_date,
|
|
286
|
-
compatibility_flags: config.compatibility_flags,
|
|
287
|
-
usage_model: config.usage_model,
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const start = Date.now();
|
|
291
|
-
function formatTime(duration: number) {
|
|
292
|
-
return `(${(duration / 1000).toFixed(2)} sec)`;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const notProd = !props.legacyEnv && props.env;
|
|
296
|
-
const workerName = notProd ? `${scriptName} (${envName})` : scriptName;
|
|
297
|
-
const workerUrl = notProd
|
|
298
|
-
? `/accounts/${accountId}/workers/services/${scriptName}/environments/${envName}`
|
|
299
|
-
: `/accounts/${accountId}/workers/scripts/${scriptName}`;
|
|
300
|
-
|
|
301
|
-
// Upload the script so it has time to propagate.
|
|
302
|
-
const { available_on_subdomain } = await fetchResult(
|
|
303
|
-
workerUrl,
|
|
304
|
-
{
|
|
305
|
-
method: "PUT",
|
|
306
|
-
body: toFormData(worker),
|
|
307
|
-
},
|
|
308
|
-
new URLSearchParams({ available_on_subdomains: "true" })
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
const uploadMs = Date.now() - start;
|
|
312
|
-
console.log("Uploaded", workerName, formatTime(uploadMs));
|
|
313
|
-
const deployments: Promise<string[]>[] = [];
|
|
314
|
-
|
|
315
|
-
const userSubdomain = (
|
|
316
|
-
await fetchResult<{ subdomain: string }>(
|
|
317
|
-
`/accounts/${accountId}/workers/subdomain`
|
|
318
|
-
)
|
|
319
|
-
).subdomain;
|
|
320
|
-
|
|
321
|
-
const scriptURL =
|
|
322
|
-
props.legacyEnv || !props.env
|
|
323
|
-
? `${scriptName}.${userSubdomain}.workers.dev`
|
|
324
|
-
: `${envName}.${scriptName}.${userSubdomain}.workers.dev`;
|
|
325
|
-
|
|
326
|
-
// Enable the `workers.dev` subdomain.
|
|
327
|
-
// TODO: Make this configurable.
|
|
328
|
-
if (!available_on_subdomain) {
|
|
329
|
-
deployments.push(
|
|
330
|
-
fetchResult(`${workerUrl}/subdomain`, {
|
|
331
|
-
method: "POST",
|
|
332
|
-
body: JSON.stringify({ enabled: true }),
|
|
333
|
-
headers: {
|
|
334
|
-
"Content-Type": "application/json",
|
|
335
|
-
},
|
|
336
|
-
})
|
|
337
|
-
.then(() => [scriptURL])
|
|
338
|
-
// Add a delay when the subdomain is first created.
|
|
339
|
-
// This is to prevent an issue where a negative cache-hit
|
|
340
|
-
// causes the subdomain to be unavailable for 30 seconds.
|
|
341
|
-
// This is a temporary measure until we fix this on the edge.
|
|
342
|
-
.then(async (url) => {
|
|
343
|
-
await sleep(3000);
|
|
344
|
-
return url;
|
|
345
|
-
})
|
|
346
|
-
);
|
|
347
|
-
} else {
|
|
348
|
-
deployments.push(Promise.resolve([scriptURL]));
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Update routing table for the script.
|
|
352
|
-
if (routes && routes.length) {
|
|
353
|
-
deployments.push(
|
|
354
|
-
fetchResult(`${workerUrl}/routes`, {
|
|
355
|
-
// TODO: PATCH will not delete previous routes on this script,
|
|
356
|
-
// whereas PUT will. We need to decide on the default behaviour
|
|
357
|
-
// and how to configure it.
|
|
358
|
-
method: "PUT",
|
|
359
|
-
body: JSON.stringify(routes.map((pattern) => ({ pattern }))),
|
|
360
|
-
headers: {
|
|
361
|
-
"Content-Type": "application/json",
|
|
362
|
-
},
|
|
363
|
-
}).then(() => {
|
|
364
|
-
if (routes.length > 10) {
|
|
365
|
-
return routes
|
|
366
|
-
.slice(0, 9)
|
|
367
|
-
.map(String)
|
|
368
|
-
.concat([`...and ${routes.length - 10} more routes`]);
|
|
369
|
-
}
|
|
370
|
-
return routes.map(String);
|
|
371
|
-
})
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Configure any schedules for the script.
|
|
376
|
-
// TODO: rename this to `schedules`?
|
|
377
|
-
if (triggers && triggers.length) {
|
|
378
|
-
deployments.push(
|
|
379
|
-
fetchResult(`${workerUrl}/schedules`, {
|
|
380
|
-
// TODO: Unlike routes, this endpoint does not support PATCH.
|
|
381
|
-
// So technically, this will override any previous schedules.
|
|
382
|
-
// We should change the endpoint to support PATCH.
|
|
383
|
-
method: "PUT",
|
|
384
|
-
body: JSON.stringify(triggers.map((cron) => ({ cron }))),
|
|
385
|
-
headers: {
|
|
386
|
-
"Content-Type": "application/json",
|
|
387
|
-
},
|
|
388
|
-
}).then(() => triggers.map(String))
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (!deployments.length) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const targets = await Promise.all(deployments);
|
|
397
|
-
const deployMs = Date.now() - start - uploadMs;
|
|
398
|
-
console.log("Deployed", workerName, formatTime(deployMs));
|
|
399
|
-
for (const target of targets.flat()) {
|
|
400
|
-
console.log(" ", target);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function listEntryPoints(
|
|
405
|
-
outputs: [string, ValueOf<Metafile["outputs"]>][]
|
|
406
|
-
): string {
|
|
407
|
-
return outputs.map(([_input, output]) => output.entryPoint).join("\n");
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
type ValueOf<T> = T[keyof T];
|
package/src/sites.tsx
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import ignore from "ignore";
|
|
4
|
-
import { XXHash64 } from "xxhash-addon";
|
|
5
|
-
import {
|
|
6
|
-
createNamespace,
|
|
7
|
-
listNamespaceKeys,
|
|
8
|
-
listNamespaces,
|
|
9
|
-
putBulkKeyValue,
|
|
10
|
-
} from "./kv";
|
|
11
|
-
import type { Config } from "./config";
|
|
12
|
-
|
|
13
|
-
/** Paths to always ignore. */
|
|
14
|
-
const ALWAYS_IGNORE = ["node_modules"];
|
|
15
|
-
const HIDDEN_FILES_TO_INCLUDE = [
|
|
16
|
-
".well-known", // See https://datatracker.ietf.org/doc/html/rfc8615
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
async function* getFilesInFolder(dirPath: string): AsyncIterable<string> {
|
|
20
|
-
const files = await readdir(dirPath, { withFileTypes: true });
|
|
21
|
-
for (const file of files) {
|
|
22
|
-
// Skip files that we never want to process.
|
|
23
|
-
if (ALWAYS_IGNORE.some((p) => file.name === p)) {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
// Skip hidden files (starting with .) except for some special ones
|
|
27
|
-
if (
|
|
28
|
-
file.name.startsWith(".") &&
|
|
29
|
-
!HIDDEN_FILES_TO_INCLUDE.some((p) => file.name === p)
|
|
30
|
-
) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
// TODO: follow symlinks??
|
|
34
|
-
if (file.isDirectory()) {
|
|
35
|
-
yield* await getFilesInFolder(path.join(dirPath, file.name));
|
|
36
|
-
} else {
|
|
37
|
-
yield path.join(dirPath, file.name);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a hash key for the given content using the xxhash algorithm.
|
|
44
|
-
*
|
|
45
|
-
* Note we only return the first 10 characters, since we will also include the file name in the asset manifest key
|
|
46
|
-
* the most important thing here is to detect changes of a single file to invalidate the cache and
|
|
47
|
-
* it's impossible to serve two different files with the same name
|
|
48
|
-
*/
|
|
49
|
-
function hashFileContent(content: string): string {
|
|
50
|
-
const hasher = new XXHash64();
|
|
51
|
-
hasher.update(Buffer.from(content));
|
|
52
|
-
const hash = hasher.digest();
|
|
53
|
-
return hash.toString("hex").substring(0, 10);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Create a hashed asset key for the given asset.
|
|
58
|
-
*
|
|
59
|
-
* The key will change if the file path or content of the asset changes.
|
|
60
|
-
* The algorithm used here matches that of Wrangler 1.
|
|
61
|
-
*/
|
|
62
|
-
function hashAsset(filePath: string, content: string): string {
|
|
63
|
-
const extName = path.extname(filePath) || "";
|
|
64
|
-
const baseName = path.basename(filePath, extName);
|
|
65
|
-
const directory = path.dirname(filePath);
|
|
66
|
-
const hash = hashFileContent(content);
|
|
67
|
-
return urlSafe(path.join(directory, `${baseName}.${hash}${extName}`));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function createKVNamespaceIfNotAlreadyExisting(
|
|
71
|
-
title: string,
|
|
72
|
-
accountId: string
|
|
73
|
-
) {
|
|
74
|
-
// check if it already exists
|
|
75
|
-
// TODO: this is super inefficient, should be made better
|
|
76
|
-
const namespaces = await listNamespaces(accountId);
|
|
77
|
-
const found = namespaces.find((x) => x.title === title);
|
|
78
|
-
if (found) {
|
|
79
|
-
return { created: false, id: found.id };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// else we make the namespace
|
|
83
|
-
const id = await createNamespace(accountId, title);
|
|
84
|
-
console.log(`🌀 Created namespace for Workers Site "${title}"`);
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
created: true,
|
|
88
|
-
id,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Upload the assets found within the `dirPath` directory to the sites assets KV namespace for
|
|
94
|
-
* the worker given by `scriptName`.
|
|
95
|
-
*
|
|
96
|
-
* @param accountId the account to upload to.
|
|
97
|
-
* @param scriptName the name of the worker whose assets we are uploading.
|
|
98
|
-
* @param siteAssets an objects describing what assets to upload, or undefined if there are no assets to upload.
|
|
99
|
-
* @param preview if true then upload to a "preview" KV namespace.
|
|
100
|
-
* @param _env (not implemented).
|
|
101
|
-
* @returns a promise for an object mapping the relative paths of the assets to the key of that
|
|
102
|
-
* asset in the KV namespace.
|
|
103
|
-
*/
|
|
104
|
-
export async function syncAssets(
|
|
105
|
-
accountId: string,
|
|
106
|
-
scriptName: string,
|
|
107
|
-
siteAssets: AssetPaths | undefined,
|
|
108
|
-
preview: boolean,
|
|
109
|
-
env: string | undefined
|
|
110
|
-
): Promise<{
|
|
111
|
-
manifest: { [filePath: string]: string } | undefined;
|
|
112
|
-
namespace: string | undefined;
|
|
113
|
-
}> {
|
|
114
|
-
if (siteAssets === undefined) {
|
|
115
|
-
return { manifest: undefined, namespace: undefined };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const title = `__${scriptName}${env ? `-${env}` : ""}-workers_sites_assets${
|
|
119
|
-
preview ? "_preview" : ""
|
|
120
|
-
}`;
|
|
121
|
-
const { id: namespace } = await createKVNamespaceIfNotAlreadyExisting(
|
|
122
|
-
title,
|
|
123
|
-
accountId
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// let's get all the keys in this namespace
|
|
127
|
-
const result = await listNamespaceKeys(accountId, namespace);
|
|
128
|
-
const keys = new Set(result.map((x) => x.name));
|
|
129
|
-
|
|
130
|
-
const manifest = {};
|
|
131
|
-
const upload: {
|
|
132
|
-
key: string;
|
|
133
|
-
value: string;
|
|
134
|
-
base64: boolean;
|
|
135
|
-
}[] = [];
|
|
136
|
-
|
|
137
|
-
const include = createPatternMatcher(siteAssets.includePatterns, false);
|
|
138
|
-
const exclude = createPatternMatcher(siteAssets.excludePatterns, true);
|
|
139
|
-
// TODO: this can be more efficient by parallelising
|
|
140
|
-
for await (const file of getFilesInFolder(siteAssets.baseDirectory)) {
|
|
141
|
-
if (!include(file)) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (exclude(file)) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
await validateAssetSize(file);
|
|
149
|
-
console.log(`reading ${file}...`);
|
|
150
|
-
const content = await readFile(file, "base64");
|
|
151
|
-
|
|
152
|
-
const assetKey = hashAsset(file, content);
|
|
153
|
-
validateAssetKey(assetKey);
|
|
154
|
-
|
|
155
|
-
// now put each of the files into kv
|
|
156
|
-
if (!keys.has(assetKey)) {
|
|
157
|
-
console.log(`uploading as ${assetKey}...`);
|
|
158
|
-
upload.push({
|
|
159
|
-
key: assetKey,
|
|
160
|
-
value: content,
|
|
161
|
-
base64: true,
|
|
162
|
-
});
|
|
163
|
-
} else {
|
|
164
|
-
console.log(`skipping - already uploaded`);
|
|
165
|
-
}
|
|
166
|
-
manifest[path.relative(siteAssets.baseDirectory, file)] = assetKey;
|
|
167
|
-
}
|
|
168
|
-
await putBulkKeyValue(accountId, namespace, JSON.stringify(upload));
|
|
169
|
-
return { manifest, namespace };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function createPatternMatcher(
|
|
173
|
-
patterns: string[],
|
|
174
|
-
exclude: boolean
|
|
175
|
-
): (filePath: string) => boolean {
|
|
176
|
-
if (patterns.length === 0) {
|
|
177
|
-
return (_filePath) => !exclude;
|
|
178
|
-
} else {
|
|
179
|
-
const ignorer = ignore().add(patterns);
|
|
180
|
-
return (filePath) => ignorer.test(filePath).ignored;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function validateAssetSize(filePath: string) {
|
|
185
|
-
const { size } = await stat(filePath);
|
|
186
|
-
if (size > 25 * 1024 * 1024) {
|
|
187
|
-
throw new Error(
|
|
188
|
-
`File ${filePath} is too big, it should be under 25 MiB. See https://developers.cloudflare.com/workers/platform/limits#kv-limits`
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function validateAssetKey(assetKey: string) {
|
|
194
|
-
if (assetKey.length > 512) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`The asset path key "${assetKey}" exceeds the maximum key size limit of 512. See https://developers.cloudflare.com/workers/platform/limits#kv-limits",`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Convert a filePath to be safe to use as a relative URL.
|
|
203
|
-
*
|
|
204
|
-
* Primarily this involves converting Windows backslashes to forward slashes.
|
|
205
|
-
*/
|
|
206
|
-
function urlSafe(filePath: string): string {
|
|
207
|
-
return filePath.replace(/\\/g, "/");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Information about the assets that should be uploaded
|
|
212
|
-
*/
|
|
213
|
-
export interface AssetPaths {
|
|
214
|
-
baseDirectory: string;
|
|
215
|
-
includePatterns: string[];
|
|
216
|
-
excludePatterns: string[];
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get an object that describes what assets to upload, if any.
|
|
221
|
-
*
|
|
222
|
-
* Uses the args (passed from the command line) if available,
|
|
223
|
-
* falling back to those defined in the config.
|
|
224
|
-
*
|
|
225
|
-
* // TODO: Support for environments
|
|
226
|
-
*/
|
|
227
|
-
export function getAssetPaths(
|
|
228
|
-
config: Config,
|
|
229
|
-
baseDirectory = config.site?.bucket,
|
|
230
|
-
includePatterns = config.site?.include ?? [],
|
|
231
|
-
excludePatterns = config.site?.exclude ?? []
|
|
232
|
-
): undefined | AssetPaths {
|
|
233
|
-
return baseDirectory
|
|
234
|
-
? {
|
|
235
|
-
baseDirectory,
|
|
236
|
-
includePatterns,
|
|
237
|
-
excludePatterns,
|
|
238
|
-
}
|
|
239
|
-
: undefined;
|
|
240
|
-
}
|