wrangler 0.0.0-e1c615f → 0.0.0-e5d3690
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/package.json +9 -5
- package/pages/functions/buildWorker.ts +5 -0
- package/pages/functions/filepath-routing.ts +15 -8
- package/pages/functions/routes.ts +1 -1
- package/pages/functions/template-worker.ts +18 -19
- package/src/__tests__/dev.test.tsx +5 -4
- package/src/__tests__/index.test.ts +2 -0
- package/src/__tests__/kv.test.ts +173 -72
- package/src/__tests__/mock-cfetch.ts +8 -6
- package/src/__tests__/publish.test.ts +672 -0
- package/src/__tests__/whoami.test.tsx +130 -0
- package/src/api/form_data.ts +1 -1
- package/src/api/worker.ts +6 -6
- package/src/cfetch/index.ts +10 -2
- package/src/cfetch/internal.ts +10 -8
- package/src/config.ts +633 -118
- package/src/dev.tsx +119 -214
- package/src/index.tsx +268 -222
- package/src/inspect.ts +543 -0
- package/src/kv.tsx +18 -9
- package/src/pages.tsx +117 -45
- package/src/proxy.ts +275 -76
- package/src/publish.ts +78 -46
- package/src/sites.tsx +167 -38
- package/src/user.tsx +56 -59
- package/src/whoami.tsx +69 -0
- package/wrangler-dist/cli.js +31405 -30705
- package/wrangler-dist/cli.js.map +3 -3
- package/src/api/inspect.ts +0 -430
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrangler",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-e5d3690",
|
|
4
4
|
"author": "wrangler@cloudflare.com",
|
|
5
5
|
"description": "Command-line interface for all things Cloudflare Workers",
|
|
6
6
|
"bin": {
|
|
@@ -37,9 +37,10 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"esbuild": "0.14.1",
|
|
40
|
-
"miniflare": "2.
|
|
40
|
+
"miniflare": "2.2.0",
|
|
41
41
|
"path-to-regexp": "^6.2.0",
|
|
42
|
-
"semiver": "^1.1.0"
|
|
42
|
+
"semiver": "^1.1.0",
|
|
43
|
+
"xxhash-addon": "^1.4.0"
|
|
43
44
|
},
|
|
44
45
|
"optionalDependencies": {
|
|
45
46
|
"fsevents": "~2.3.2"
|
|
@@ -59,18 +60,20 @@
|
|
|
59
60
|
"chokidar": "^3.5.2",
|
|
60
61
|
"clipboardy": "^3.0.0",
|
|
61
62
|
"command-exists": "^1.2.9",
|
|
63
|
+
"devtools-protocol": "^0.0.955664",
|
|
62
64
|
"execa": "^6.0.0",
|
|
63
65
|
"faye-websocket": "^0.11.4",
|
|
64
66
|
"finalhandler": "^1.1.2",
|
|
65
67
|
"find-up": "^6.2.0",
|
|
66
68
|
"formdata-node": "^4.3.1",
|
|
69
|
+
"ignore": "^5.2.0",
|
|
67
70
|
"ink": "^3.2.0",
|
|
68
71
|
"ink-select-input": "^4.2.1",
|
|
69
72
|
"ink-table": "^3.0.0",
|
|
70
73
|
"ink-testing-library": "^2.1.0",
|
|
71
74
|
"ink-text-input": "^4.0.2",
|
|
72
75
|
"mime": "^3.0.0",
|
|
73
|
-
"node-fetch": "
|
|
76
|
+
"node-fetch": "3.1.1",
|
|
74
77
|
"open": "^8.4.0",
|
|
75
78
|
"react": "^17.0.2",
|
|
76
79
|
"react-error-boundary": "^3.1.4",
|
|
@@ -96,7 +99,8 @@
|
|
|
96
99
|
"bundle": "node -r esbuild-register scripts/bundle.ts",
|
|
97
100
|
"build": "npm run clean && npm run bundle",
|
|
98
101
|
"start": "npm run bundle && NODE_OPTIONS=--enable-source-maps ./bin/wrangler.js",
|
|
99
|
-
"test": "CF_API_TOKEN=some-api-token CF_ACCOUNT_ID=some-account-id jest --silent=false --verbose=true"
|
|
102
|
+
"test": "CF_API_TOKEN=some-api-token CF_ACCOUNT_ID=some-account-id jest --silent=false --verbose=true",
|
|
103
|
+
"test-watch": "npm run test -- --runInBand --testTimeout=50000 --watch"
|
|
100
104
|
},
|
|
101
105
|
"engines": {
|
|
102
106
|
"node": ">=16.7.0"
|
|
@@ -6,6 +6,7 @@ type Options = {
|
|
|
6
6
|
outfile: string;
|
|
7
7
|
minify?: boolean;
|
|
8
8
|
sourcemap?: boolean;
|
|
9
|
+
fallbackService?: string;
|
|
9
10
|
watch?: boolean;
|
|
10
11
|
onEnd?: () => void;
|
|
11
12
|
};
|
|
@@ -15,6 +16,7 @@ export function buildWorker({
|
|
|
15
16
|
outfile = "bundle.js",
|
|
16
17
|
minify = false,
|
|
17
18
|
sourcemap = false,
|
|
19
|
+
fallbackService = "ASSETS",
|
|
18
20
|
watch = false,
|
|
19
21
|
onEnd = () => {},
|
|
20
22
|
}: Options) {
|
|
@@ -31,6 +33,9 @@ export function buildWorker({
|
|
|
31
33
|
sourcemap,
|
|
32
34
|
watch,
|
|
33
35
|
allowOverwrite: true,
|
|
36
|
+
define: {
|
|
37
|
+
__FALLBACK_SERVICE__: JSON.stringify(fallbackService),
|
|
38
|
+
},
|
|
34
39
|
plugins: [
|
|
35
40
|
{
|
|
36
41
|
name: "wrangler notifier and monitor",
|
|
@@ -52,7 +52,7 @@ export async function generateConfigFromFileTree({
|
|
|
52
52
|
const declaration = node.declaration;
|
|
53
53
|
|
|
54
54
|
// `export async function onRequest() {...}`
|
|
55
|
-
if (declaration.type === "FunctionDeclaration") {
|
|
55
|
+
if (declaration.type === "FunctionDeclaration" && declaration.id) {
|
|
56
56
|
exportNames.push(declaration.id.name);
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -155,12 +155,12 @@ export async function generateConfigFromFileTree({
|
|
|
155
155
|
// more specific routes aren't occluded from matching due to
|
|
156
156
|
// less specific routes appearing first in the route list.
|
|
157
157
|
export function compareRoutes(a: string, b: string) {
|
|
158
|
-
function parseRoutePath(routePath: string) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
function parseRoutePath(routePath: string): [string | null, string[]] {
|
|
159
|
+
const parts = routePath.split(" ", 2);
|
|
160
|
+
// split() will guarantee at least one element.
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
162
|
+
const segmentedPath = parts.pop()!;
|
|
163
|
+
const method = parts.pop() ?? null;
|
|
164
164
|
|
|
165
165
|
const segments = segmentedPath.slice(1).split("/").filter(Boolean);
|
|
166
166
|
return [method, segments];
|
|
@@ -204,7 +204,7 @@ async function forEachFile<T>(
|
|
|
204
204
|
const searchPaths = [baseDir];
|
|
205
205
|
const returnValues: T[] = [];
|
|
206
206
|
|
|
207
|
-
while (searchPaths
|
|
207
|
+
while (isNotEmpty(searchPaths)) {
|
|
208
208
|
const cwd = searchPaths.shift();
|
|
209
209
|
const dir = await fs.readdir(cwd, { withFileTypes: true });
|
|
210
210
|
for (const entry of dir) {
|
|
@@ -219,3 +219,10 @@ async function forEachFile<T>(
|
|
|
219
219
|
|
|
220
220
|
return returnValues;
|
|
221
221
|
}
|
|
222
|
+
|
|
223
|
+
interface NonEmptyArray<T> extends Array<T> {
|
|
224
|
+
shift(): T;
|
|
225
|
+
}
|
|
226
|
+
function isNotEmpty<T>(array: T[]): array is NonEmptyArray<T> {
|
|
227
|
+
return array.length > 0;
|
|
228
|
+
}
|
|
@@ -114,7 +114,7 @@ export function parseConfig(config: Config, baseDir: string) {
|
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
for (const [route, props] of Object.entries(config.routes)) {
|
|
117
|
+
for (const [route, props] of Object.entries(config.routes ?? {})) {
|
|
118
118
|
let [_methods, routePath] = route.split(" ");
|
|
119
119
|
if (!routePath) {
|
|
120
120
|
routePath = _methods;
|
|
@@ -29,18 +29,20 @@ type RouteHandler = {
|
|
|
29
29
|
|
|
30
30
|
// inject `routes` via ESBuild
|
|
31
31
|
declare const routes: RouteHandler[];
|
|
32
|
+
// define `__FALLBACK_SERVICE__` via ESBuild
|
|
33
|
+
declare const __FALLBACK_SERVICE__: string;
|
|
32
34
|
|
|
33
35
|
// expect an ASSETS fetcher binding pointing to the asset-server stage
|
|
34
|
-
type
|
|
35
|
-
[name: string]:
|
|
36
|
-
ASSETS: { fetch
|
|
36
|
+
type FetchEnv = {
|
|
37
|
+
[name: string]: { fetch: typeof fetch };
|
|
38
|
+
ASSETS: { fetch: typeof fetch };
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
type WorkerContext = {
|
|
40
42
|
waitUntil: (promise: Promise<unknown>) => void;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
function* executeRequest(request: Request,
|
|
45
|
+
function* executeRequest(request: Request, _env: FetchEnv) {
|
|
44
46
|
const requestPath = new URL(request.url).pathname;
|
|
45
47
|
|
|
46
48
|
// First, iterate through the routes (backwards) and execute "middlewares" on partial route matches
|
|
@@ -85,16 +87,10 @@ function* executeRequest(request: Request, env: Env) {
|
|
|
85
87
|
break;
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
|
-
|
|
89
|
-
// Finally, yield to the asset-server
|
|
90
|
-
return {
|
|
91
|
-
handler: () => env.ASSETS.fetch(request.url, request),
|
|
92
|
-
params: {} as Params,
|
|
93
|
-
};
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
export default {
|
|
97
|
-
async fetch(request: Request, env:
|
|
93
|
+
async fetch(request: Request, env: FetchEnv, workerContext: WorkerContext) {
|
|
98
94
|
const handlerIterator = executeRequest(request, env);
|
|
99
95
|
const data = {}; // arbitrary data the user can set between functions
|
|
100
96
|
const next = async (input?: RequestInfo, init?: RequestInit) => {
|
|
@@ -102,14 +98,11 @@ export default {
|
|
|
102
98
|
request = new Request(input, init);
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
string,
|
|
111
|
-
Record<string, unknown>
|
|
112
|
-
> = {
|
|
101
|
+
const result = handlerIterator.next();
|
|
102
|
+
// Note we can't use `!result.done` because this doesn't narrow to the correct type
|
|
103
|
+
if (result.done == false) {
|
|
104
|
+
const { handler, params } = result.value;
|
|
105
|
+
const context = {
|
|
113
106
|
request: new Request(request.clone()),
|
|
114
107
|
next,
|
|
115
108
|
params,
|
|
@@ -125,6 +118,12 @@ export default {
|
|
|
125
118
|
[101, 204, 205, 304].includes(response.status) ? null : response.body,
|
|
126
119
|
response
|
|
127
120
|
);
|
|
121
|
+
} else if (__FALLBACK_SERVICE__) {
|
|
122
|
+
// There are no more handlers so finish with the fallback service (`env.ASSETS.fetch` in Pages' case)
|
|
123
|
+
return env[__FALLBACK_SERVICE__].fetch(request);
|
|
124
|
+
} else {
|
|
125
|
+
// There was not fallback service so actually make the request to the origin.
|
|
126
|
+
return fetch(request);
|
|
128
127
|
}
|
|
129
128
|
};
|
|
130
129
|
|
|
@@ -15,9 +15,10 @@ describe("Dev component", () => {
|
|
|
15
15
|
accountId: "some-account-id",
|
|
16
16
|
public: "some/public/path",
|
|
17
17
|
});
|
|
18
|
-
expect(lastFrame()).
|
|
18
|
+
expect(lastFrame()?.split("\n").slice(0, 2).join("\n"))
|
|
19
|
+
.toMatchInlineSnapshot(`
|
|
19
20
|
"Something went wrong:
|
|
20
|
-
You cannot use the service worker format with a \`public\` directory."
|
|
21
|
+
Error: You cannot use the service worker format with a \`public\` directory."
|
|
21
22
|
`);
|
|
22
23
|
});
|
|
23
24
|
});
|
|
@@ -38,7 +39,7 @@ function renderDev({
|
|
|
38
39
|
jsxFragment,
|
|
39
40
|
bindings = {},
|
|
40
41
|
public: publicDir,
|
|
41
|
-
|
|
42
|
+
assetPaths,
|
|
42
43
|
compatibilityDate,
|
|
43
44
|
compatibilityFlags,
|
|
44
45
|
usageModel,
|
|
@@ -55,7 +56,7 @@ function renderDev({
|
|
|
55
56
|
jsxFactory={jsxFactory}
|
|
56
57
|
jsxFragment={jsxFragment}
|
|
57
58
|
accountId={accountId}
|
|
58
|
-
|
|
59
|
+
assetPaths={assetPaths}
|
|
59
60
|
public={publicDir}
|
|
60
61
|
compatibilityDate={compatibilityDate}
|
|
61
62
|
compatibilityFlags={compatibilityFlags}
|
|
@@ -17,6 +17,7 @@ describe("wrangler", () => {
|
|
|
17
17
|
|
|
18
18
|
Commands:
|
|
19
19
|
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
20
|
+
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
20
21
|
wrangler dev <filename> 👂 Start a local server for developing your worker
|
|
21
22
|
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
22
23
|
wrangler tail [name] 🦚 Starts a log tailing session for a deployed Worker.
|
|
@@ -49,6 +50,7 @@ describe("wrangler", () => {
|
|
|
49
50
|
|
|
50
51
|
Commands:
|
|
51
52
|
wrangler init [name] 📥 Create a wrangler.toml configuration file
|
|
53
|
+
wrangler whoami 🕵️ Retrieve your user info and test your auth config
|
|
52
54
|
wrangler dev <filename> 👂 Start a local server for developing your worker
|
|
53
55
|
wrangler publish [script] 🆙 Publish your Worker to Cloudflare.
|
|
54
56
|
wrangler tail [name] 🦚 Starts a log tailing session for a deployed Worker.
|
package/src/__tests__/kv.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeFileSync } from "fs";
|
|
2
|
+
import type { KVNamespaceInfo } from "../kv";
|
|
2
3
|
import {
|
|
3
4
|
setMockResponse,
|
|
4
5
|
setMockRawResponse,
|
|
@@ -196,30 +197,30 @@ describe("wrangler", () => {
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
it("should list namespaces", async () => {
|
|
199
|
-
const
|
|
200
|
+
const kvNamespaces: KVNamespaceInfo[] = [
|
|
200
201
|
{ title: "title-1", id: "id-1" },
|
|
201
202
|
{ title: "title-2", id: "id-2" },
|
|
202
203
|
];
|
|
203
|
-
mockListRequest(
|
|
204
|
+
mockListRequest(kvNamespaces);
|
|
204
205
|
const { error, stdout, stderr } = await runWrangler(
|
|
205
206
|
"kv:namespace list"
|
|
206
207
|
);
|
|
207
208
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
208
209
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
209
210
|
const namespaces = JSON.parse(stdout);
|
|
210
|
-
expect(namespaces).toEqual(
|
|
211
|
+
expect(namespaces).toEqual(kvNamespaces);
|
|
211
212
|
});
|
|
212
213
|
|
|
213
214
|
it("should make multiple requests for paginated results", async () => {
|
|
214
215
|
// Create a lot of mock namespaces, so that the fetch requests will be paginated
|
|
215
|
-
const
|
|
216
|
+
const kvNamespaces: KVNamespaceInfo[] = [];
|
|
216
217
|
for (let i = 0; i < 550; i++) {
|
|
217
|
-
|
|
218
|
+
kvNamespaces.push({ title: "title-" + i, id: "id-" + i });
|
|
218
219
|
}
|
|
219
|
-
const requests = mockListRequest(
|
|
220
|
+
const requests = mockListRequest(kvNamespaces);
|
|
220
221
|
const { stdout } = await runWrangler("kv:namespace list");
|
|
221
222
|
const namespaces = JSON.parse(stdout);
|
|
222
|
-
expect(namespaces).toEqual(
|
|
223
|
+
expect(namespaces).toEqual(kvNamespaces);
|
|
223
224
|
expect(requests.count).toEqual(6);
|
|
224
225
|
});
|
|
225
226
|
});
|
|
@@ -335,8 +336,16 @@ describe("wrangler", () => {
|
|
|
335
336
|
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
336
337
|
expect(key).toEqual(expectedKey);
|
|
337
338
|
expect(body).toEqual(expectedValue);
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
if (expiration !== undefined) {
|
|
340
|
+
expect(query.get("expiration")).toEqual(`${expiration}`);
|
|
341
|
+
} else {
|
|
342
|
+
expect(query.has("expiration")).toBe(false);
|
|
343
|
+
}
|
|
344
|
+
if (expirationTtl) {
|
|
345
|
+
expect(query.get("expiration_ttl")).toEqual(`${expirationTtl}`);
|
|
346
|
+
} else {
|
|
347
|
+
expect(query.has("expiration_ttl")).toBe(false);
|
|
348
|
+
}
|
|
340
349
|
return null;
|
|
341
350
|
}
|
|
342
351
|
);
|
|
@@ -636,9 +645,12 @@ describe("wrangler", () => {
|
|
|
636
645
|
`kv:key put key value --binding otherBinding`
|
|
637
646
|
);
|
|
638
647
|
expect(stdout).toMatchInlineSnapshot(`""`);
|
|
639
|
-
expect(stderr).toMatchInlineSnapshot(
|
|
640
|
-
|
|
641
|
-
|
|
648
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
649
|
+
"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
|
|
650
|
+
|
|
651
|
+
[32m%s[0m
|
|
652
|
+
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
653
|
+
`);
|
|
642
654
|
expect(error).toMatchInlineSnapshot(
|
|
643
655
|
`[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
|
|
644
656
|
);
|
|
@@ -655,9 +667,12 @@ describe("wrangler", () => {
|
|
|
655
667
|
"kv:key put my-key my-value --binding someBinding"
|
|
656
668
|
);
|
|
657
669
|
expect(stdout).toMatchInlineSnapshot(`""`);
|
|
658
|
-
expect(stderr).toMatchInlineSnapshot(
|
|
659
|
-
|
|
660
|
-
|
|
670
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
671
|
+
"someBinding has both a namespace ID and a preview ID. Specify \\"--preview\\" or \\"--preview false\\" to avoid writing data to the wrong namespace.
|
|
672
|
+
|
|
673
|
+
[32m%s[0m
|
|
674
|
+
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
675
|
+
`);
|
|
661
676
|
expect(error).toMatchInlineSnapshot(
|
|
662
677
|
`[Error: someBinding has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.]`
|
|
663
678
|
);
|
|
@@ -666,37 +681,6 @@ describe("wrangler", () => {
|
|
|
666
681
|
});
|
|
667
682
|
|
|
668
683
|
describe("list", () => {
|
|
669
|
-
function mockKeyListRequest(
|
|
670
|
-
expectedNamespaceId: string,
|
|
671
|
-
expectedKeys: string[],
|
|
672
|
-
keysPerRequest = 1000
|
|
673
|
-
) {
|
|
674
|
-
const requests = { count: 0 };
|
|
675
|
-
setMockRawResponse(
|
|
676
|
-
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
677
|
-
([_url, accountId, namespaceId], _init, query) => {
|
|
678
|
-
requests.count++;
|
|
679
|
-
expect(accountId).toEqual("some-account-id");
|
|
680
|
-
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
681
|
-
if (expectedKeys.length <= keysPerRequest) {
|
|
682
|
-
return createFetchResult(expectedKeys);
|
|
683
|
-
} else {
|
|
684
|
-
const start = parseInt(query.get("cursor")) || 0;
|
|
685
|
-
const end = start + keysPerRequest;
|
|
686
|
-
const cursor = end < expectedKeys.length ? end : undefined;
|
|
687
|
-
return createFetchResult(
|
|
688
|
-
expectedKeys.slice(start, end),
|
|
689
|
-
true,
|
|
690
|
-
[],
|
|
691
|
-
[],
|
|
692
|
-
{ cursor }
|
|
693
|
-
);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
);
|
|
697
|
-
return requests;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
684
|
it("should list the keys of a namespace specified by namespace-id", async () => {
|
|
701
685
|
const keys = ["key-1", "key-2", "key-3"];
|
|
702
686
|
mockKeyListRequest("some-namespace-id", keys);
|
|
@@ -706,9 +690,23 @@ describe("wrangler", () => {
|
|
|
706
690
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
707
691
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
708
692
|
expect(stdout).toMatchInlineSnapshot(`
|
|
709
|
-
"
|
|
710
|
-
|
|
711
|
-
|
|
693
|
+
"[
|
|
694
|
+
{
|
|
695
|
+
\\"name\\": \\"key-1\\",
|
|
696
|
+
\\"expiration\\": 123456789,
|
|
697
|
+
\\"metadata\\": {}
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
\\"name\\": \\"key-2\\",
|
|
701
|
+
\\"expiration\\": 123456789,
|
|
702
|
+
\\"metadata\\": {}
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
\\"name\\": \\"key-3\\",
|
|
706
|
+
\\"expiration\\": 123456789,
|
|
707
|
+
\\"metadata\\": {}
|
|
708
|
+
}
|
|
709
|
+
]"
|
|
712
710
|
`);
|
|
713
711
|
});
|
|
714
712
|
|
|
@@ -722,9 +720,23 @@ describe("wrangler", () => {
|
|
|
722
720
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
723
721
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
724
722
|
expect(stdout).toMatchInlineSnapshot(`
|
|
725
|
-
"
|
|
726
|
-
|
|
727
|
-
|
|
723
|
+
"[
|
|
724
|
+
{
|
|
725
|
+
\\"name\\": \\"key-1\\",
|
|
726
|
+
\\"expiration\\": 123456789,
|
|
727
|
+
\\"metadata\\": {}
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
\\"name\\": \\"key-2\\",
|
|
731
|
+
\\"expiration\\": 123456789,
|
|
732
|
+
\\"metadata\\": {}
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
\\"name\\": \\"key-3\\",
|
|
736
|
+
\\"expiration\\": 123456789,
|
|
737
|
+
\\"metadata\\": {}
|
|
738
|
+
}
|
|
739
|
+
]"
|
|
728
740
|
`);
|
|
729
741
|
});
|
|
730
742
|
|
|
@@ -738,9 +750,23 @@ describe("wrangler", () => {
|
|
|
738
750
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
739
751
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
740
752
|
expect(stdout).toMatchInlineSnapshot(`
|
|
741
|
-
"
|
|
742
|
-
|
|
743
|
-
|
|
753
|
+
"[
|
|
754
|
+
{
|
|
755
|
+
\\"name\\": \\"key-1\\",
|
|
756
|
+
\\"expiration\\": 123456789,
|
|
757
|
+
\\"metadata\\": {}
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
\\"name\\": \\"key-2\\",
|
|
761
|
+
\\"expiration\\": 123456789,
|
|
762
|
+
\\"metadata\\": {}
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
\\"name\\": \\"key-3\\",
|
|
766
|
+
\\"expiration\\": 123456789,
|
|
767
|
+
\\"metadata\\": {}
|
|
768
|
+
}
|
|
769
|
+
]"
|
|
744
770
|
`);
|
|
745
771
|
});
|
|
746
772
|
|
|
@@ -754,9 +780,23 @@ describe("wrangler", () => {
|
|
|
754
780
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
755
781
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
756
782
|
expect(stdout).toMatchInlineSnapshot(`
|
|
757
|
-
"
|
|
758
|
-
|
|
759
|
-
|
|
783
|
+
"[
|
|
784
|
+
{
|
|
785
|
+
\\"name\\": \\"key-1\\",
|
|
786
|
+
\\"expiration\\": 123456789,
|
|
787
|
+
\\"metadata\\": {}
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
\\"name\\": \\"key-2\\",
|
|
791
|
+
\\"expiration\\": 123456789,
|
|
792
|
+
\\"metadata\\": {}
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
\\"name\\": \\"key-3\\",
|
|
796
|
+
\\"expiration\\": 123456789,
|
|
797
|
+
\\"metadata\\": {}
|
|
798
|
+
}
|
|
799
|
+
]"
|
|
760
800
|
`);
|
|
761
801
|
});
|
|
762
802
|
|
|
@@ -770,15 +810,29 @@ describe("wrangler", () => {
|
|
|
770
810
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
771
811
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
772
812
|
expect(stdout).toMatchInlineSnapshot(`
|
|
773
|
-
"
|
|
774
|
-
|
|
775
|
-
|
|
813
|
+
"[
|
|
814
|
+
{
|
|
815
|
+
\\"name\\": \\"key-1\\",
|
|
816
|
+
\\"expiration\\": 123456789,
|
|
817
|
+
\\"metadata\\": {}
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
\\"name\\": \\"key-2\\",
|
|
821
|
+
\\"expiration\\": 123456789,
|
|
822
|
+
\\"metadata\\": {}
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
\\"name\\": \\"key-3\\",
|
|
826
|
+
\\"expiration\\": 123456789,
|
|
827
|
+
\\"metadata\\": {}
|
|
828
|
+
}
|
|
829
|
+
]"
|
|
776
830
|
`);
|
|
777
831
|
});
|
|
778
832
|
|
|
779
833
|
it("should make multiple requests for paginated results", async () => {
|
|
780
834
|
// Create a lot of mock keys, so that the fetch requests will be paginated
|
|
781
|
-
const keys = [];
|
|
835
|
+
const keys: string[] = [];
|
|
782
836
|
for (let i = 0; i < 550; i++) {
|
|
783
837
|
keys.push("key-" + i);
|
|
784
838
|
}
|
|
@@ -789,7 +843,7 @@ describe("wrangler", () => {
|
|
|
789
843
|
);
|
|
790
844
|
expect(error).toMatchInlineSnapshot(`undefined`);
|
|
791
845
|
expect(stderr).toMatchInlineSnapshot(`""`);
|
|
792
|
-
expect(stdout).toEqual(keys
|
|
846
|
+
expect(JSON.parse(stdout).map((k) => k.name)).toEqual(keys);
|
|
793
847
|
expect(requests.count).toEqual(6);
|
|
794
848
|
});
|
|
795
849
|
|
|
@@ -801,9 +855,12 @@ describe("wrangler", () => {
|
|
|
801
855
|
expect(error).toMatchInlineSnapshot(
|
|
802
856
|
`[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
|
|
803
857
|
);
|
|
804
|
-
expect(stderr).toMatchInlineSnapshot(
|
|
805
|
-
|
|
806
|
-
|
|
858
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
859
|
+
"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
|
|
860
|
+
|
|
861
|
+
[32m%s[0m
|
|
862
|
+
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
863
|
+
`);
|
|
807
864
|
expect(stdout).toMatchInlineSnapshot(`""`);
|
|
808
865
|
});
|
|
809
866
|
});
|
|
@@ -988,9 +1045,12 @@ describe("wrangler", () => {
|
|
|
988
1045
|
`kv:key get key --binding otherBinding`
|
|
989
1046
|
);
|
|
990
1047
|
expect(stdout).toMatchInlineSnapshot(`""`);
|
|
991
|
-
expect(stderr).toMatchInlineSnapshot(
|
|
992
|
-
|
|
993
|
-
|
|
1048
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
1049
|
+
"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
|
|
1050
|
+
|
|
1051
|
+
[32m%s[0m
|
|
1052
|
+
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
1053
|
+
`);
|
|
994
1054
|
expect(error).toMatchInlineSnapshot(
|
|
995
1055
|
`[Error: A namespace with binding name "otherBinding" was not found in the configured "kv_namespaces".]`
|
|
996
1056
|
);
|
|
@@ -1048,9 +1108,12 @@ describe("wrangler", () => {
|
|
|
1048
1108
|
const { stderr } = await runWrangler(
|
|
1049
1109
|
`kv:key delete --binding otherBinding someKey`
|
|
1050
1110
|
);
|
|
1051
|
-
expect(stderr).toMatchInlineSnapshot(
|
|
1052
|
-
|
|
1053
|
-
|
|
1111
|
+
expect(stderr).toMatchInlineSnapshot(`
|
|
1112
|
+
"A namespace with binding name \\"otherBinding\\" was not found in the configured \\"kv_namespaces\\".
|
|
1113
|
+
|
|
1114
|
+
[32m%s[0m
|
|
1115
|
+
If you think this is a bug then please create an issue at https://github.com/cloudflare/wrangler2/issues/new."
|
|
1116
|
+
`);
|
|
1054
1117
|
});
|
|
1055
1118
|
|
|
1056
1119
|
it("should delete a key in a namespace specified by binding name in a given environment", async () => {
|
|
@@ -1096,3 +1159,41 @@ function writeWranglerConfig() {
|
|
|
1096
1159
|
"utf-8"
|
|
1097
1160
|
);
|
|
1098
1161
|
}
|
|
1162
|
+
|
|
1163
|
+
export function mockKeyListRequest(
|
|
1164
|
+
expectedNamespaceId: string,
|
|
1165
|
+
expectedKeys: string[],
|
|
1166
|
+
keysPerRequest = 1000
|
|
1167
|
+
) {
|
|
1168
|
+
const requests = { count: 0 };
|
|
1169
|
+
// See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys
|
|
1170
|
+
const expectedKeyObjects = expectedKeys.map((name) => ({
|
|
1171
|
+
name,
|
|
1172
|
+
expiration: 123456789,
|
|
1173
|
+
metadata: {},
|
|
1174
|
+
}));
|
|
1175
|
+
setMockRawResponse(
|
|
1176
|
+
"/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys",
|
|
1177
|
+
"GET",
|
|
1178
|
+
([_url, accountId, namespaceId], _init, query) => {
|
|
1179
|
+
requests.count++;
|
|
1180
|
+
expect(accountId).toEqual("some-account-id");
|
|
1181
|
+
expect(namespaceId).toEqual(expectedNamespaceId);
|
|
1182
|
+
if (expectedKeyObjects.length <= keysPerRequest) {
|
|
1183
|
+
return createFetchResult(expectedKeyObjects);
|
|
1184
|
+
} else {
|
|
1185
|
+
const start = parseInt(query.get("cursor") ?? "0") || 0;
|
|
1186
|
+
const end = start + keysPerRequest;
|
|
1187
|
+
const cursor = end < expectedKeyObjects.length ? end : undefined;
|
|
1188
|
+
return createFetchResult(
|
|
1189
|
+
expectedKeyObjects.slice(start, end),
|
|
1190
|
+
true,
|
|
1191
|
+
[],
|
|
1192
|
+
[],
|
|
1193
|
+
{ cursor }
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
return requests;
|
|
1199
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequestInit } from "node-fetch";
|
|
2
|
-
import
|
|
2
|
+
import { URLSearchParams } from "node:url";
|
|
3
3
|
import { pathToRegexp } from "path-to-regexp";
|
|
4
4
|
import { CF_API_BASE_URL } from "../cfetch";
|
|
5
5
|
import type { FetchResult } from "../cfetch";
|
|
@@ -9,8 +9,8 @@ import type { FetchResult } from "../cfetch";
|
|
|
9
9
|
*/
|
|
10
10
|
export type MockHandler<ResponseType> = (
|
|
11
11
|
uri: RegExpExecArray,
|
|
12
|
-
init
|
|
13
|
-
queryParams
|
|
12
|
+
init: RequestInit,
|
|
13
|
+
queryParams: URLSearchParams
|
|
14
14
|
) => ResponseType;
|
|
15
15
|
|
|
16
16
|
type RemoveMockFn = () => void;
|
|
@@ -32,19 +32,21 @@ const mocks: MockFetch<unknown>[] = [];
|
|
|
32
32
|
export async function mockFetchInternal(
|
|
33
33
|
resource: string,
|
|
34
34
|
init: RequestInit = {},
|
|
35
|
-
queryParams
|
|
35
|
+
queryParams: URLSearchParams = new URLSearchParams()
|
|
36
36
|
) {
|
|
37
37
|
for (const { regexp, method, handler } of mocks) {
|
|
38
38
|
const resourcePath = new URL(resource, CF_API_BASE_URL).pathname;
|
|
39
39
|
const uri = regexp.exec(resourcePath);
|
|
40
40
|
// Do the resource path and (if specified) the HTTP method match?
|
|
41
|
-
if (uri !== null && (!method || method === init.method)) {
|
|
41
|
+
if (uri !== null && (!method || method === (init.method ?? "GET"))) {
|
|
42
42
|
// The `resource` regular expression will extract the labelled groups from the URL.
|
|
43
43
|
// These are passed through to the `handler` call, to allow it to do additional checks or behaviour.
|
|
44
44
|
return handler(uri, init, queryParams); // TODO: should we have some kind of fallthrough system? we'll see.
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
throw new Error(
|
|
47
|
+
throw new Error(
|
|
48
|
+
`no mocks found for ${init.method ?? "any HTTP"} request to ${resource}`
|
|
49
|
+
);
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|