wrangler 0.0.0-e2a26a0 → 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 +27 -14
- package/bin/wrangler.js +49 -5
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +51 -27
- package/pages/functions/buildPlugin.ts +105 -0
- package/pages/functions/buildWorker.ts +93 -4
- package/pages/functions/filepath-routing.test.ts +223 -36
- package/pages/functions/filepath-routing.ts +78 -120
- package/pages/functions/routes.ts +13 -17
- package/pages/functions/template-plugin.ts +147 -0
- package/pages/functions/template-worker.ts +24 -17
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +3408 -0
- package/src/__tests__/dev.test.tsx +977 -65
- package/src/__tests__/guess-worker-format.test.ts +85 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +26 -8
- package/src/__tests__/helpers/mock-bin.ts +4 -1
- package/src/__tests__/helpers/mock-cfetch.ts +10 -6
- package/src/__tests__/helpers/mock-console.ts +39 -8
- package/src/__tests__/helpers/mock-dialogs.ts +67 -2
- 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/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/run-wrangler.ts +5 -3
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/https-options.test.ts +130 -0
- package/src/__tests__/index.test.ts +148 -435
- package/src/__tests__/init.test.ts +1637 -0
- package/src/__tests__/jest.setup.ts +45 -5
- package/src/__tests__/kv.test.ts +590 -248
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/package-manager.test.ts +156 -9
- package/src/__tests__/pages.test.ts +754 -0
- package/src/__tests__/parse.test.ts +330 -0
- package/src/__tests__/publish.test.ts +5471 -939
- package/src/__tests__/r2.test.ts +233 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +362 -91
- package/src/__tests__/tail.test.ts +690 -0
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +60 -7
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +197 -0
- package/src/cfetch/index.ts +27 -8
- package/src/cfetch/internal.ts +73 -32
- package/src/cli.ts +6 -2
- package/src/config/README.md +107 -0
- package/src/config/config.ts +234 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +420 -0
- package/src/config/index.ts +225 -0
- package/src/config/validation-helpers.ts +584 -0
- package/src/config/validation.ts +1539 -0
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +185 -0
- package/src/create-worker-upload-form.ts +254 -0
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +412 -0
- package/src/dev/local.tsx +322 -0
- package/src/dev/remote.tsx +237 -0
- package/src/dev/use-esbuild.ts +118 -0
- package/src/dialogs.tsx +64 -6
- package/src/durable.ts +102 -0
- package/src/entry.ts +322 -0
- package/src/environment-variables.ts +35 -0
- package/src/errors.ts +11 -0
- package/src/https-options.ts +123 -0
- package/src/index.tsx +1747 -1002
- package/src/inspect.ts +80 -14
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv.tsx → kv.ts} +192 -68
- 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 +226 -31
- package/src/open-in-browser.ts +19 -0
- package/src/package-manager.ts +79 -25
- package/src/pages.tsx +1131 -84
- package/src/parse.ts +202 -0
- package/src/proxy.ts +170 -62
- package/src/publish.ts +612 -306
- package/src/r2.ts +50 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +143 -57
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +67 -0
- package/src/update-check.ts +19 -0
- package/src/user.tsx +341 -146
- package/src/whoami.tsx +5 -3
- package/src/{api/worker.ts → worker.ts} +65 -39
- package/src/zones.ts +73 -0
- package/templates/gitignore +171 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.js +1 -1
- package/templates/new-worker.ts +17 -2
- package/templates/static-asset-facade.js +1 -5
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +72295 -71006
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/api/form_data.ts +0 -134
- package/src/api/preview.ts +0 -128
- package/src/config.ts +0 -641
- package/src/dev.tsx +0 -834
- package/src/tail.tsx +0 -73
- 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/inspect.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import { URL } from "node:url";
|
|
4
5
|
|
|
6
|
+
import open from "open";
|
|
5
7
|
import { useEffect, useRef, useState } from "react";
|
|
6
8
|
import WebSocket, { WebSocketServer } from "ws";
|
|
7
9
|
import { version } from "../package.json";
|
|
10
|
+
import { logger } from "./logger";
|
|
11
|
+
import { waitForPortToBeAvailable } from "./proxy";
|
|
8
12
|
import type Protocol from "devtools-protocol";
|
|
9
13
|
import type { IncomingMessage, Server, ServerResponse } from "node:http";
|
|
10
14
|
import type { MessageEvent } from "ws";
|
|
@@ -41,7 +45,7 @@ interface InspectorProps {
|
|
|
41
45
|
/**
|
|
42
46
|
* The websocket URL exposed by Workers that the inspector should connect to.
|
|
43
47
|
*/
|
|
44
|
-
inspectorUrl:
|
|
48
|
+
inspectorUrl: string | undefined;
|
|
45
49
|
/**
|
|
46
50
|
* Whether console statements and exceptions should be logged to the terminal.
|
|
47
51
|
* (We don't log them in local mode because they're already getting
|
|
@@ -133,7 +137,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
133
137
|
wsServer.on("connection", (ws: WebSocket) => {
|
|
134
138
|
if (wsServer.clients.size > 1) {
|
|
135
139
|
/** We only want to have one active Devtools instance at a time. */
|
|
136
|
-
|
|
140
|
+
logger.error(
|
|
137
141
|
"Tried to open a new devtools window when a previous one was already open."
|
|
138
142
|
);
|
|
139
143
|
ws.close(1013, "Too many clients; only one can be connected at a time");
|
|
@@ -153,12 +157,26 @@ export default function useInspector(props: InspectorProps) {
|
|
|
153
157
|
* of the component lifecycle. Convenient.
|
|
154
158
|
*/
|
|
155
159
|
useEffect(() => {
|
|
156
|
-
|
|
160
|
+
const abortController = new AbortController();
|
|
161
|
+
async function startInspectorProxy() {
|
|
162
|
+
await waitForPortToBeAvailable(props.port, {
|
|
163
|
+
retryPeriod: 200,
|
|
164
|
+
timeout: 2000,
|
|
165
|
+
abortSignal: abortController.signal,
|
|
166
|
+
});
|
|
167
|
+
server.listen(props.port);
|
|
168
|
+
}
|
|
169
|
+
startInspectorProxy().catch((err) => {
|
|
170
|
+
if ((err as { code: string }).code !== "ABORT_ERR") {
|
|
171
|
+
logger.error("Failed to start inspector:", err);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
157
174
|
return () => {
|
|
158
175
|
server.close();
|
|
159
176
|
// Also disconnect any open websockets/devtools connections
|
|
160
177
|
wsServer.clients.forEach((ws) => ws.close());
|
|
161
178
|
wsServer.close();
|
|
179
|
+
abortController.abort();
|
|
162
180
|
};
|
|
163
181
|
}, [props.port, server, wsServer]);
|
|
164
182
|
|
|
@@ -239,9 +257,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
239
257
|
const evt = JSON.parse(event.data);
|
|
240
258
|
if (evt.method === "Runtime.exceptionThrown") {
|
|
241
259
|
const params = evt.params as Protocol.Runtime.ExceptionThrownEvent;
|
|
242
|
-
|
|
243
|
-
"🚨", // cheesy, but it works
|
|
244
|
-
// maybe we could use color here too.
|
|
260
|
+
logger.error(
|
|
245
261
|
params.exceptionDetails.text,
|
|
246
262
|
params.exceptionDetails.exception?.description ?? ""
|
|
247
263
|
);
|
|
@@ -253,7 +269,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
253
269
|
}
|
|
254
270
|
} else {
|
|
255
271
|
// We should never get here, but who know is 2022...
|
|
256
|
-
|
|
272
|
+
logger.error("Unrecognised devtools event:", event);
|
|
257
273
|
}
|
|
258
274
|
});
|
|
259
275
|
}
|
|
@@ -272,7 +288,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
272
288
|
});
|
|
273
289
|
|
|
274
290
|
ws.on("unexpected-response", () => {
|
|
275
|
-
|
|
291
|
+
logger.log("Waiting for connection...");
|
|
276
292
|
/**
|
|
277
293
|
* This usually means the worker is not "ready" yet
|
|
278
294
|
* so we'll just retry the connection process
|
|
@@ -367,7 +383,10 @@ export default function useInspector(props: InspectorProps) {
|
|
|
367
383
|
);
|
|
368
384
|
remoteWebSocket.send(event.data);
|
|
369
385
|
} catch (e) {
|
|
370
|
-
if (
|
|
386
|
+
if (
|
|
387
|
+
(e as Error).message !==
|
|
388
|
+
"WebSocket is not open: readyState 0 (CONNECTING)"
|
|
389
|
+
) {
|
|
371
390
|
/**
|
|
372
391
|
* ^ this just means we haven't opened a websocket yet
|
|
373
392
|
* usually happens until there's at least one request
|
|
@@ -375,7 +394,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
375
394
|
* happens on the first request. Maybe we should buffer
|
|
376
395
|
* these messages too?
|
|
377
396
|
*/
|
|
378
|
-
|
|
397
|
+
logger.error(e);
|
|
379
398
|
}
|
|
380
399
|
}
|
|
381
400
|
}
|
|
@@ -587,10 +606,57 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
|
|
|
587
606
|
const method = mapConsoleAPIMessageTypeToConsoleMethod[evt.type];
|
|
588
607
|
|
|
589
608
|
if (method in console) {
|
|
590
|
-
|
|
591
|
-
|
|
609
|
+
switch (method) {
|
|
610
|
+
case "dir":
|
|
611
|
+
console.dir(args);
|
|
612
|
+
break;
|
|
613
|
+
case "table":
|
|
614
|
+
console.table(args);
|
|
615
|
+
break;
|
|
616
|
+
default:
|
|
617
|
+
// eslint-disable-next-line prefer-spread
|
|
618
|
+
console[method].apply(console, args);
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
592
621
|
} else {
|
|
593
|
-
|
|
594
|
-
|
|
622
|
+
logger.warn(`Unsupported console method: ${method}`);
|
|
623
|
+
logger.warn("console event:", evt);
|
|
595
624
|
}
|
|
596
625
|
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Opens the chrome debugger
|
|
629
|
+
*/
|
|
630
|
+
export const openInspector = async (inspectorPort: number) => {
|
|
631
|
+
const url = `https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`;
|
|
632
|
+
const errorMessage =
|
|
633
|
+
"Failed to open inspector.\nInspector depends on having a Chromium-based browser installed, maybe you need to install one?";
|
|
634
|
+
|
|
635
|
+
// see: https://github.com/sindresorhus/open/issues/177#issue-610016699
|
|
636
|
+
let braveBrowser: string;
|
|
637
|
+
switch (os.platform()) {
|
|
638
|
+
case "darwin":
|
|
639
|
+
case "win32":
|
|
640
|
+
braveBrowser = "Brave";
|
|
641
|
+
break;
|
|
642
|
+
default:
|
|
643
|
+
braveBrowser = "brave";
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const childProcess = await open(url, {
|
|
647
|
+
app: [
|
|
648
|
+
{
|
|
649
|
+
name: open.apps.chrome,
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: braveBrowser,
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
name: open.apps.edge,
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
});
|
|
659
|
+
childProcess.on("error", () => {
|
|
660
|
+
logger.warn(errorMessage);
|
|
661
|
+
});
|
|
662
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO Remove once https://github.com/microsoft/TypeScript/pull/47254 lands
|
|
3
|
+
*/
|
|
4
|
+
declare namespace Intl {
|
|
5
|
+
interface DateTimeFormatOptions {
|
|
6
|
+
formatMatcher?: "basic" | "best fit" | "best fit" | undefined;
|
|
7
|
+
dateStyle?: "full" | "long" | "medium" | "short" | undefined;
|
|
8
|
+
timeStyle?: "full" | "long" | "medium" | "short" | undefined;
|
|
9
|
+
dayPeriod?: "narrow" | "short" | "long" | undefined;
|
|
10
|
+
fractionalSecondDigits?: 0 | 1 | 2 | 3 | undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ResolvedDateTimeFormatOptions {
|
|
14
|
+
formatMatcher?: "basic" | "best fit" | "best fit";
|
|
15
|
+
dateStyle?: "full" | "long" | "medium" | "short";
|
|
16
|
+
timeStyle?: "full" | "long" | "medium" | "short";
|
|
17
|
+
hourCycle?: "h11" | "h12" | "h23" | "h24";
|
|
18
|
+
dayPeriod?: "narrow" | "short" | "long";
|
|
19
|
+
fractionalSecondDigits?: 0 | 1 | 2 | 3;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface NumberFormat {
|
|
23
|
+
formatRange(startDate: number | bigint, endDate: number | bigint): string;
|
|
24
|
+
formatRangeToParts(
|
|
25
|
+
startDate: number | bigint,
|
|
26
|
+
endDate: number | bigint
|
|
27
|
+
): NumberFormatPart[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The locale matching algorithm to use.
|
|
32
|
+
*
|
|
33
|
+
* [MDN](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_negotiation).
|
|
34
|
+
*/
|
|
35
|
+
type ListFormatLocaleMatcher = "lookup" | "best fit";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The format of output message.
|
|
39
|
+
*
|
|
40
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters).
|
|
41
|
+
*/
|
|
42
|
+
type ListFormatType = "conjunction" | "disjunction" | "unit";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The length of the formatted message.
|
|
46
|
+
*
|
|
47
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters).
|
|
48
|
+
*/
|
|
49
|
+
type ListFormatStyle = "long" | "short" | "narrow";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An object with some or all properties of the `Intl.ListFormat` constructor `options` parameter.
|
|
53
|
+
*
|
|
54
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters).
|
|
55
|
+
*/
|
|
56
|
+
interface ListFormatOptions {
|
|
57
|
+
/** The locale matching algorithm to use. For information about this option, see [Intl page](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_negotiation). */
|
|
58
|
+
localeMatcher?: ListFormatLocaleMatcher;
|
|
59
|
+
/** The format of output message. */
|
|
60
|
+
type?: ListFormatType;
|
|
61
|
+
/** The length of the internationalized message. */
|
|
62
|
+
style?: ListFormatStyle;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface ListFormat {
|
|
66
|
+
/**
|
|
67
|
+
* Returns a string with a language-specific representation of the list.
|
|
68
|
+
*
|
|
69
|
+
* @param list - An iterable object, such as an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).
|
|
70
|
+
*
|
|
71
|
+
* @throws `TypeError` if `list` includes something other than the possible values.
|
|
72
|
+
*
|
|
73
|
+
* @returns {string} A language-specific formatted string representing the elements of the list.
|
|
74
|
+
*
|
|
75
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/format).
|
|
76
|
+
*/
|
|
77
|
+
format(list: Iterable<string>): string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns an Array of objects representing the different components that can be used to format a list of values in a locale-aware fashion.
|
|
81
|
+
*
|
|
82
|
+
* @param list - An iterable object, such as an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), to be formatted according to a locale.
|
|
83
|
+
*
|
|
84
|
+
* @throws `TypeError` if `list` includes something other than the possible values.
|
|
85
|
+
*
|
|
86
|
+
* @returns {{ type: "element" | "literal", value: string; }[]} An Array of components which contains the formatted parts from the list.
|
|
87
|
+
*
|
|
88
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/formatToParts).
|
|
89
|
+
*/
|
|
90
|
+
formatToParts(
|
|
91
|
+
list: Iterable<string>
|
|
92
|
+
): { type: "element" | "literal"; value: string }[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const ListFormat: {
|
|
96
|
+
prototype: ListFormat;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Creates [Intl.ListFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) objects that
|
|
100
|
+
* enable language-sensitive list formatting.
|
|
101
|
+
*
|
|
102
|
+
* @param locales - A string with a [BCP 47 language tag](http://tools.ietf.org/html/rfc5646), or an array of such strings.
|
|
103
|
+
* For the general form and interpretation of the `locales` argument,
|
|
104
|
+
* see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation).
|
|
105
|
+
*
|
|
106
|
+
* @param options - An [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#parameters)
|
|
107
|
+
* with some or all options of `ListFormatOptions`.
|
|
108
|
+
*
|
|
109
|
+
* @returns [Intl.ListFormatOptions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) object.
|
|
110
|
+
*
|
|
111
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat).
|
|
112
|
+
*/
|
|
113
|
+
new (
|
|
114
|
+
locales?: BCP47LanguageTag | BCP47LanguageTag[],
|
|
115
|
+
options?: ListFormatOptions
|
|
116
|
+
): ListFormat;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns an array containing those of the provided locales that are
|
|
120
|
+
* supported in list formatting without having to fall back to the runtime's default locale.
|
|
121
|
+
*
|
|
122
|
+
* @param locales - A string with a [BCP 47 language tag](http://tools.ietf.org/html/rfc5646), or an array of such strings.
|
|
123
|
+
* For the general form and interpretation of the `locales` argument,
|
|
124
|
+
* see the [`Intl` page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation).
|
|
125
|
+
*
|
|
126
|
+
* @param options - An [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/supportedLocalesOf#parameters).
|
|
127
|
+
* with some or all possible options.
|
|
128
|
+
*
|
|
129
|
+
* @returns An array of strings representing a subset of the given locale tags that are supported in list
|
|
130
|
+
* formatting without having to fall back to the runtime's default locale.
|
|
131
|
+
*
|
|
132
|
+
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/supportedLocalesOf).
|
|
133
|
+
*/
|
|
134
|
+
supportedLocalesOf(
|
|
135
|
+
locales: BCP47LanguageTag | BCP47LanguageTag[],
|
|
136
|
+
options?: Pick<ListFormatOptions, "localeMatcher">
|
|
137
|
+
): BCP47LanguageTag[];
|
|
138
|
+
};
|
|
139
|
+
}
|
package/src/{kv.tsx → kv.ts}
RENAMED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { URLSearchParams } from "node:url";
|
|
2
2
|
import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
|
|
3
|
+
import { logger } from "./logger";
|
|
3
4
|
import type { Config } from "./config";
|
|
4
5
|
|
|
6
|
+
/** The largest number of kv items we can pass to the API in a single request. */
|
|
7
|
+
const API_MAX = 10000;
|
|
8
|
+
// The const below are halved from the API's true capacity to help avoid
|
|
9
|
+
// hammering it with large requests.
|
|
10
|
+
const BATCH_KEY_MAX = API_MAX / 2;
|
|
11
|
+
|
|
5
12
|
type KvArgs = {
|
|
6
13
|
binding?: string;
|
|
7
14
|
"namespace-id"?: string;
|
|
8
|
-
env?: string;
|
|
9
15
|
preview?: boolean;
|
|
10
|
-
config?: Config;
|
|
11
16
|
};
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -15,7 +20,7 @@ type KvArgs = {
|
|
|
15
20
|
*
|
|
16
21
|
* @returns the generated id of the created namespace.
|
|
17
22
|
*/
|
|
18
|
-
export async function
|
|
23
|
+
export async function createKVNamespace(
|
|
19
24
|
accountId: string,
|
|
20
25
|
title: string
|
|
21
26
|
): Promise<string> {
|
|
@@ -47,7 +52,7 @@ export interface KVNamespaceInfo {
|
|
|
47
52
|
/**
|
|
48
53
|
* Fetch a list of all the namespaces under the given `accountId`.
|
|
49
54
|
*/
|
|
50
|
-
export async function
|
|
55
|
+
export async function listKVNamespaces(
|
|
51
56
|
accountId: string
|
|
52
57
|
): Promise<KVNamespaceInfo[]> {
|
|
53
58
|
const pageSize = 100;
|
|
@@ -79,7 +84,7 @@ export interface NamespaceKeyInfo {
|
|
|
79
84
|
metadata?: { [key: string]: unknown };
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
export async function
|
|
87
|
+
export async function listKVNamespaceKeys(
|
|
83
88
|
accountId: string,
|
|
84
89
|
namespaceId: string,
|
|
85
90
|
prefix = ""
|
|
@@ -91,75 +96,218 @@ export async function listNamespaceKeys(
|
|
|
91
96
|
);
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
export async function
|
|
99
|
+
export async function deleteKVNamespace(
|
|
100
|
+
accountId: string,
|
|
101
|
+
namespaceId: string
|
|
102
|
+
) {
|
|
103
|
+
return await fetchResult<{ id: string }>(
|
|
104
|
+
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}`,
|
|
105
|
+
{ method: "DELETE" }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Information about a key-value pair, including its "metadata" fields.
|
|
111
|
+
*/
|
|
112
|
+
export interface KeyValue {
|
|
113
|
+
key: string;
|
|
114
|
+
value: string;
|
|
115
|
+
expiration?: number;
|
|
116
|
+
expiration_ttl?: number;
|
|
117
|
+
metadata?: object;
|
|
118
|
+
base64?: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const KeyValueKeys = new Set([
|
|
122
|
+
"key",
|
|
123
|
+
"value",
|
|
124
|
+
"expiration",
|
|
125
|
+
"expiration_ttl",
|
|
126
|
+
"metadata",
|
|
127
|
+
"base64",
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The object has the specified property.
|
|
132
|
+
*/
|
|
133
|
+
function hasProperty<T extends object>(
|
|
134
|
+
obj: object,
|
|
135
|
+
property: keyof T
|
|
136
|
+
): obj is T {
|
|
137
|
+
return property in obj;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The object has a required property of the specified type.
|
|
142
|
+
*/
|
|
143
|
+
function hasTypedProperty<T extends object>(
|
|
144
|
+
obj: object,
|
|
145
|
+
property: keyof T,
|
|
146
|
+
type: string
|
|
147
|
+
): obj is T {
|
|
148
|
+
return hasProperty(obj, property) && typeof obj[property] === type;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The object an optional property, of the specified type.
|
|
153
|
+
*/
|
|
154
|
+
function hasOptionalTypedProperty<T extends object>(
|
|
155
|
+
obj: object,
|
|
156
|
+
property: keyof T,
|
|
157
|
+
type: string
|
|
158
|
+
): obj is Omit<T, typeof property> | T {
|
|
159
|
+
return !hasProperty(obj, property) || typeof obj[property] === type;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Is the given object a valid `KeyValue` type?
|
|
164
|
+
*/
|
|
165
|
+
export function isKVKeyValue(keyValue: unknown): keyValue is KeyValue {
|
|
166
|
+
if (
|
|
167
|
+
keyValue === null ||
|
|
168
|
+
typeof keyValue !== "object" ||
|
|
169
|
+
!hasTypedProperty(keyValue, "key", "string") ||
|
|
170
|
+
!hasTypedProperty(keyValue, "value", "string") ||
|
|
171
|
+
!hasOptionalTypedProperty(keyValue, "expiration", "number") ||
|
|
172
|
+
!hasOptionalTypedProperty(keyValue, "expiration_ttl", "number") ||
|
|
173
|
+
!hasOptionalTypedProperty(keyValue, "base64", "boolean") ||
|
|
174
|
+
!hasOptionalTypedProperty(keyValue, "metadata", "object")
|
|
175
|
+
) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get all the properties on the `keyValue` that are not expected.
|
|
183
|
+
*/
|
|
184
|
+
export function unexpectedKVKeyValueProps(keyValue: KeyValue): string[] {
|
|
185
|
+
const props = Object.keys(keyValue);
|
|
186
|
+
return props.filter((prop) => !KeyValueKeys.has(prop));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function putKVKeyValue(
|
|
95
190
|
accountId: string,
|
|
96
191
|
namespaceId: string,
|
|
97
|
-
|
|
98
|
-
value: string,
|
|
99
|
-
args?: { expiration?: number; expiration_ttl?: number }
|
|
192
|
+
keyValue: KeyValue
|
|
100
193
|
) {
|
|
101
194
|
let searchParams: URLSearchParams | undefined;
|
|
102
|
-
if (
|
|
195
|
+
if (keyValue.expiration || keyValue.expiration_ttl) {
|
|
103
196
|
searchParams = new URLSearchParams();
|
|
104
|
-
if (
|
|
105
|
-
searchParams.set("expiration", `${
|
|
197
|
+
if (keyValue.expiration) {
|
|
198
|
+
searchParams.set("expiration", `${keyValue.expiration}`);
|
|
106
199
|
}
|
|
107
|
-
if (
|
|
108
|
-
searchParams.set("expiration_ttl", `${
|
|
200
|
+
if (keyValue.expiration_ttl) {
|
|
201
|
+
searchParams.set("expiration_ttl", `${keyValue.expiration_ttl}`);
|
|
109
202
|
}
|
|
110
203
|
}
|
|
111
204
|
return await fetchResult(
|
|
112
|
-
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${
|
|
113
|
-
|
|
205
|
+
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
|
|
206
|
+
keyValue.key
|
|
207
|
+
)}`,
|
|
208
|
+
{ method: "PUT", body: keyValue.value },
|
|
114
209
|
searchParams
|
|
115
210
|
);
|
|
116
211
|
}
|
|
117
212
|
|
|
118
|
-
export async function
|
|
213
|
+
export async function getKVKeyValue(
|
|
119
214
|
accountId: string,
|
|
120
215
|
namespaceId: string,
|
|
121
216
|
key: string
|
|
122
217
|
): Promise<string> {
|
|
123
|
-
return await fetchKVGetValue(accountId, namespaceId, key);
|
|
218
|
+
return await fetchKVGetValue(accountId, namespaceId, encodeURIComponent(key));
|
|
124
219
|
}
|
|
125
220
|
|
|
126
|
-
export async function
|
|
221
|
+
export async function deleteKVKeyValue(
|
|
127
222
|
accountId: string,
|
|
128
223
|
namespaceId: string,
|
|
129
|
-
|
|
224
|
+
key: string
|
|
130
225
|
) {
|
|
131
226
|
return await fetchResult(
|
|
132
|
-
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
headers: { "Content-Type": "application/json" },
|
|
137
|
-
}
|
|
227
|
+
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
|
|
228
|
+
key
|
|
229
|
+
)}`,
|
|
230
|
+
{ method: "DELETE" }
|
|
138
231
|
);
|
|
139
232
|
}
|
|
140
233
|
|
|
141
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Formatter for converting e.g. 5328 --> 5,328
|
|
236
|
+
*/
|
|
237
|
+
const formatNumber = new Intl.NumberFormat("en-US", {
|
|
238
|
+
notation: "standard",
|
|
239
|
+
}).format;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Helper function for bulk requests, logs ongoing output to console.
|
|
243
|
+
*/
|
|
244
|
+
function logBulkProgress(
|
|
245
|
+
operation: "put" | "delete",
|
|
246
|
+
index: number,
|
|
247
|
+
total: number
|
|
248
|
+
) {
|
|
249
|
+
logger.log(
|
|
250
|
+
`${operation === "put" ? "Uploaded" : "Deleted"} ${Math.floor(
|
|
251
|
+
(100 * index) / total
|
|
252
|
+
)}% (${formatNumber(index)} out of ${formatNumber(total)})`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function putKVBulkKeyValue(
|
|
142
257
|
accountId: string,
|
|
143
258
|
namespaceId: string,
|
|
144
|
-
|
|
259
|
+
keyValues: KeyValue[],
|
|
260
|
+
quiet = false
|
|
145
261
|
) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
method: "DELETE",
|
|
150
|
-
body: keyStr,
|
|
151
|
-
headers: { "Content-Type": "application/json" },
|
|
262
|
+
for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
|
|
263
|
+
if (!quiet && keyValues.length > BATCH_KEY_MAX) {
|
|
264
|
+
logBulkProgress("put", index, keyValues.length);
|
|
152
265
|
}
|
|
153
|
-
|
|
266
|
+
|
|
267
|
+
await fetchResult(
|
|
268
|
+
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
|
|
269
|
+
{
|
|
270
|
+
method: "PUT",
|
|
271
|
+
body: JSON.stringify(keyValues.slice(index, index + BATCH_KEY_MAX)),
|
|
272
|
+
headers: { "Content-Type": "application/json" },
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!quiet && keyValues.length > BATCH_KEY_MAX) {
|
|
278
|
+
logBulkProgress("put", keyValues.length, keyValues.length);
|
|
279
|
+
}
|
|
154
280
|
}
|
|
155
281
|
|
|
156
|
-
export function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
282
|
+
export async function deleteKVBulkKeyValue(
|
|
283
|
+
accountId: string,
|
|
284
|
+
namespaceId: string,
|
|
285
|
+
keys: string[],
|
|
286
|
+
quiet = false
|
|
287
|
+
) {
|
|
288
|
+
for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
|
|
289
|
+
if (!quiet && keys.length > BATCH_KEY_MAX) {
|
|
290
|
+
logBulkProgress("delete", index, keys.length);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await fetchResult(
|
|
294
|
+
`/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
|
|
295
|
+
{
|
|
296
|
+
method: "DELETE",
|
|
297
|
+
body: JSON.stringify(keys.slice(index, index + BATCH_KEY_MAX)),
|
|
298
|
+
headers: { "Content-Type": "application/json" },
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (!quiet && keys.length > BATCH_KEY_MAX) {
|
|
303
|
+
logBulkProgress("delete", keys.length, keys.length);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function getKVNamespaceId(
|
|
308
|
+
{ preview, binding, "namespace-id": namespaceId }: KvArgs,
|
|
309
|
+
config: Config
|
|
310
|
+
): string {
|
|
163
311
|
// nice
|
|
164
312
|
if (namespaceId) {
|
|
165
313
|
return namespaceId;
|
|
@@ -167,7 +315,7 @@ export function getNamespaceId({
|
|
|
167
315
|
|
|
168
316
|
// begin pre-flight checks
|
|
169
317
|
|
|
170
|
-
// `--binding` is only valid if there's a wrangler configuration
|
|
318
|
+
// `--binding` is only valid if there's a wrangler configuration.
|
|
171
319
|
if (binding && !config) {
|
|
172
320
|
throw new Error("--binding specified, but no config file was found.");
|
|
173
321
|
}
|
|
@@ -180,30 +328,6 @@ export function getNamespaceId({
|
|
|
180
328
|
);
|
|
181
329
|
}
|
|
182
330
|
|
|
183
|
-
// they want to use an environment, actually
|
|
184
|
-
if (env) {
|
|
185
|
-
if (!config.env || !config.env[env]) {
|
|
186
|
-
throw new Error(
|
|
187
|
-
`Failed to find environment "${env}" in configuration file!`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// TODO: either a bespoke arg type for this function to avoid `undefined`s or an `EnvOrConfig` type
|
|
192
|
-
return getNamespaceId({
|
|
193
|
-
binding,
|
|
194
|
-
"namespace-id": namespaceId,
|
|
195
|
-
env: undefined,
|
|
196
|
-
preview,
|
|
197
|
-
config: {
|
|
198
|
-
env: undefined,
|
|
199
|
-
build: undefined,
|
|
200
|
-
name: undefined,
|
|
201
|
-
account_id: undefined,
|
|
202
|
-
...config.env[env],
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
331
|
// there's no KV namespaces
|
|
208
332
|
if (!config.kv_namespaces || config.kv_namespaces.length === 0) {
|
|
209
333
|
throw new Error(
|
|
@@ -275,7 +399,7 @@ export function getNamespaceId({
|
|
|
275
399
|
/**
|
|
276
400
|
* KV namespace binding names must be valid JS identifiers.
|
|
277
401
|
*/
|
|
278
|
-
export function
|
|
402
|
+
export function isValidKVNamespaceBinding(
|
|
279
403
|
binding: string | undefined
|
|
280
404
|
): binding is string {
|
|
281
405
|
return (
|