wrangler 2.1.6 → 2.1.7
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/miniflare-dist/index.mjs +5 -20
- package/package.json +13 -3
- package/src/__tests__/api-dev.test.ts +20 -0
- package/src/__tests__/configuration.test.ts +125 -22
- package/src/__tests__/dev.test.tsx +0 -2
- package/src/__tests__/helpers/mock-oauth-flow.ts +4 -2
- package/src/__tests__/index.test.ts +2 -0
- package/src/__tests__/paths.test.ts +23 -1
- package/src/__tests__/publish.test.ts +8 -10
- package/src/__tests__/user.test.ts +4 -4
- package/src/__tests__/whoami.test.tsx +0 -1
- package/src/__tests__/worker-namespace.test.ts +102 -112
- package/src/api/dev.ts +12 -12
- package/src/bundle.ts +48 -0
- package/src/cfetch/internal.ts +37 -21
- package/src/config/environment.ts +20 -0
- package/src/config/index.ts +32 -0
- package/src/config/validation.ts +59 -0
- package/src/config-cache.ts +1 -1
- package/src/create-worker-upload-form.ts +9 -0
- package/src/d1/backups.tsx +212 -0
- package/src/d1/create.tsx +54 -0
- package/src/d1/delete.tsx +56 -0
- package/src/d1/execute.tsx +294 -0
- package/src/d1/formatTimeAgo.ts +14 -0
- package/src/d1/index.ts +75 -0
- package/src/d1/list.tsx +48 -0
- package/src/d1/options.ts +12 -0
- package/src/d1/types.tsx +14 -0
- package/src/d1/utils.ts +39 -0
- package/src/dev/dev.tsx +26 -3
- package/src/dev/get-local-persistence-path.tsx +31 -0
- package/src/dev/local.tsx +8 -8
- package/src/dev/start-server.ts +2 -3
- package/src/dev/use-esbuild.ts +8 -1
- package/src/dev.tsx +28 -28
- package/src/dialogs.tsx +4 -0
- package/src/environment-variables.ts +17 -2
- package/src/index.tsx +18 -16
- package/src/logger.ts +11 -4
- package/src/miniflare-cli/index.ts +11 -16
- package/src/pages/dev.tsx +13 -9
- package/src/paths.ts +30 -4
- package/src/proxy.ts +21 -1
- package/src/publish.ts +6 -0
- package/src/user/user.tsx +1 -0
- package/src/worker.ts +30 -0
- package/templates/d1-beta-facade.js +174 -0
- package/wrangler-dist/cli.d.ts +438 -7
- package/wrangler-dist/cli.js +10925 -3223
- package/src/miniflare-cli/enum-keys.ts +0 -17
package/src/paths.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert } from "node:console";
|
|
2
|
-
import { resolve } from "node:path";
|
|
2
|
+
import { relative, basename, resolve } from "node:path";
|
|
3
3
|
|
|
4
4
|
type DiscriminatedPath<Discriminator extends string> = string & {
|
|
5
5
|
_discriminator: Discriminator;
|
|
@@ -18,12 +18,38 @@ export type UrlPath = DiscriminatedPath<"UrlPath">;
|
|
|
18
18
|
* Use this helper to convert a `string` to a `UrlPath` when it is not clear whether the string needs normalizing.
|
|
19
19
|
* Replaces all back-slashes with forward-slashes, and throws an error if the path contains a drive letter (e.g. `C:`).
|
|
20
20
|
*/
|
|
21
|
-
export function toUrlPath(
|
|
21
|
+
export function toUrlPath(filePath: string): UrlPath {
|
|
22
22
|
assert(
|
|
23
|
-
!/^[a-z]:/i.test(
|
|
23
|
+
!/^[a-z]:/i.test(filePath),
|
|
24
24
|
"Tried to convert a Windows file path with a drive to a URL path."
|
|
25
25
|
);
|
|
26
|
-
return
|
|
26
|
+
return filePath.replace(/\\/g, "/") as UrlPath;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get a human-readable path, relative to process.cwd(), prefixed with ./ if
|
|
31
|
+
* in a nested subdirectory, to aid with readability.
|
|
32
|
+
* Only used for logging e.g. `Loading DB at ${readableRelative(dbPath)}`:
|
|
33
|
+
*
|
|
34
|
+
* E.g. (assuming process.cwd() is /pwd)
|
|
35
|
+
*
|
|
36
|
+
* readableRelative('/pwd/wrangler.toml') => 'wrangler.toml'
|
|
37
|
+
* readableRelative('/wrangler.toml') => '../wrangler.toml'
|
|
38
|
+
* readableRelative('/pwd/subdir/wrangler.toml') => './subdir/wrangler.toml'
|
|
39
|
+
*
|
|
40
|
+
* */
|
|
41
|
+
export function readableRelative(to: string) {
|
|
42
|
+
const relativePath = relative(process.cwd(), to);
|
|
43
|
+
if (
|
|
44
|
+
// No directory nesting, return as-is
|
|
45
|
+
basename(relativePath) === relativePath ||
|
|
46
|
+
// Outside current directory
|
|
47
|
+
relativePath.startsWith(".")
|
|
48
|
+
) {
|
|
49
|
+
return relativePath;
|
|
50
|
+
} else {
|
|
51
|
+
return "./" + relativePath;
|
|
52
|
+
}
|
|
27
53
|
}
|
|
28
54
|
|
|
29
55
|
/**
|
package/src/proxy.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer as createHttpServer } from "node:http";
|
|
2
2
|
import { connect } from "node:http2";
|
|
3
3
|
import { createServer as createHttpsServer } from "node:https";
|
|
4
|
+
import { networkInterfaces } from "node:os";
|
|
4
5
|
import WebSocket from "faye-websocket";
|
|
5
6
|
import { createHttpTerminator } from "http-terminator";
|
|
6
7
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -327,7 +328,15 @@ export function usePreviewServer({
|
|
|
327
328
|
})
|
|
328
329
|
.then(() => {
|
|
329
330
|
proxy.server.on("listening", () => {
|
|
330
|
-
|
|
331
|
+
const address = proxy.server.address();
|
|
332
|
+
const usedPort =
|
|
333
|
+
address && typeof address === "object" ? address.port : port;
|
|
334
|
+
logger.log(`⬣ Listening at ${localProtocol}://${ip}:${usedPort}`);
|
|
335
|
+
const accessibleHosts =
|
|
336
|
+
ip !== "0.0.0.0" ? [ip] : getAccessibleHosts();
|
|
337
|
+
for (const accessibleHost of accessibleHosts) {
|
|
338
|
+
logger.log(`- ${localProtocol}://${accessibleHost}:${usedPort}`);
|
|
339
|
+
}
|
|
331
340
|
});
|
|
332
341
|
proxy.server.listen(port, ip);
|
|
333
342
|
})
|
|
@@ -484,3 +493,14 @@ export async function waitForPortToBeAvailable(
|
|
|
484
493
|
}
|
|
485
494
|
});
|
|
486
495
|
}
|
|
496
|
+
|
|
497
|
+
function getAccessibleHosts(): string[] {
|
|
498
|
+
const hosts: string[] = [];
|
|
499
|
+
Object.values(networkInterfaces()).forEach((net) => {
|
|
500
|
+
net?.forEach(({ family, address }) => {
|
|
501
|
+
// @ts-expect-error the `family` property is numeric as of Node.js 18.0.0
|
|
502
|
+
if (family === "IPv4" || family === 4) hosts.push(address);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
return hosts;
|
|
506
|
+
}
|
package/src/publish.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { logger } from "./logger";
|
|
|
14
14
|
import { getMetricsUsageHeaders } from "./metrics";
|
|
15
15
|
import { ParseError } from "./parse";
|
|
16
16
|
import { syncAssets } from "./sites";
|
|
17
|
+
import { identifyD1BindingsAsBeta } from "./worker";
|
|
17
18
|
import { getZoneForRoute } from "./zones";
|
|
18
19
|
import type { Config } from "./config";
|
|
19
20
|
import type {
|
|
@@ -407,6 +408,9 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
407
408
|
{
|
|
408
409
|
serveAssetsFromWorker:
|
|
409
410
|
!props.isWorkersSite && Boolean(props.assetPaths),
|
|
411
|
+
betaD1Shims: identifyD1BindingsAsBeta(config.d1_databases)?.map(
|
|
412
|
+
(db) => db.binding
|
|
413
|
+
),
|
|
410
414
|
jsxFactory,
|
|
411
415
|
jsxFragment,
|
|
412
416
|
rules: props.rules,
|
|
@@ -429,6 +433,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
429
433
|
// We want to know if the build is for development or publishing
|
|
430
434
|
// This could potentially cause issues as we no longer have identical behaviour between dev and publish?
|
|
431
435
|
targetConsumer: "publish",
|
|
436
|
+
local: false,
|
|
432
437
|
}
|
|
433
438
|
);
|
|
434
439
|
|
|
@@ -476,6 +481,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
476
481
|
data_blobs: config.data_blobs,
|
|
477
482
|
durable_objects: config.durable_objects,
|
|
478
483
|
r2_buckets: config.r2_buckets,
|
|
484
|
+
d1_databases: identifyD1BindingsAsBeta(config.d1_databases),
|
|
479
485
|
services: config.services,
|
|
480
486
|
dispatch_namespaces: config.dispatch_namespaces,
|
|
481
487
|
logfwdr: config.logfwdr,
|
package/src/user/user.tsx
CHANGED
|
@@ -309,6 +309,7 @@ const Scopes = {
|
|
|
309
309
|
"workers_scripts:write":
|
|
310
310
|
"See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
|
|
311
311
|
"workers_tail:read": "See Cloudflare Workers tail and script data.",
|
|
312
|
+
"d1:write": "See and change D1 Databases.",
|
|
312
313
|
"pages:write":
|
|
313
314
|
"See and change Cloudflare Pages projects, settings and deployments.",
|
|
314
315
|
"zone:read": "Grants read level access to account zone.",
|
package/src/worker.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { Environment } from "./config";
|
|
1
2
|
import type { Route } from "./config/environment";
|
|
2
3
|
import type { ApiCredentials } from "./user";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* A Cloudflare account.
|
|
6
7
|
*/
|
|
8
|
+
|
|
7
9
|
export interface CfAccount {
|
|
8
10
|
/**
|
|
9
11
|
* An API token.
|
|
@@ -116,6 +118,17 @@ export interface CfR2Bucket {
|
|
|
116
118
|
bucket_name: string;
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
export const D1_BETA_PREFIX = `__D1_BETA__` as const;
|
|
122
|
+
export type D1PrefixedBinding = `${typeof D1_BETA_PREFIX}${string}`;
|
|
123
|
+
|
|
124
|
+
export interface CfD1Database {
|
|
125
|
+
// For now, all D1 bindings are beta
|
|
126
|
+
binding: D1PrefixedBinding;
|
|
127
|
+
database_id: string;
|
|
128
|
+
database_name?: string;
|
|
129
|
+
preview_database_id?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
interface CfService {
|
|
120
133
|
binding: string;
|
|
121
134
|
service: string;
|
|
@@ -182,6 +195,7 @@ export interface CfWorkerInit {
|
|
|
182
195
|
data_blobs: CfDataBlobBindings | undefined;
|
|
183
196
|
durable_objects: { bindings: CfDurableObject[] } | undefined;
|
|
184
197
|
r2_buckets: CfR2Bucket[] | undefined;
|
|
198
|
+
d1_databases: CfD1Database[] | undefined;
|
|
185
199
|
services: CfService[] | undefined;
|
|
186
200
|
dispatch_namespaces: CfDispatchNamespace[] | undefined;
|
|
187
201
|
logfwdr: CfLogfwdr | undefined;
|
|
@@ -202,3 +216,19 @@ export interface CfWorkerContext {
|
|
|
202
216
|
routes: Route[] | undefined;
|
|
203
217
|
sendMetrics: boolean | undefined;
|
|
204
218
|
}
|
|
219
|
+
|
|
220
|
+
// Prefix binding with identifier which will then get picked up by the D1 shim.
|
|
221
|
+
// Once the D1 Api is out of beta, this function can be removed.
|
|
222
|
+
export function identifyD1BindingsAsBeta(
|
|
223
|
+
dbs: Environment["d1_databases"]
|
|
224
|
+
): CfD1Database[] | undefined {
|
|
225
|
+
return dbs?.map((db) => ({
|
|
226
|
+
...db,
|
|
227
|
+
binding: `${D1_BETA_PREFIX}${db.binding}`,
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Remove beta prefix
|
|
232
|
+
export function removeD1BetaPrefix(binding: D1PrefixedBinding): string {
|
|
233
|
+
return binding.slice(D1_BETA_PREFIX.length);
|
|
234
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/shim.ts
|
|
2
|
+
import worker from "__ENTRY_POINT__";
|
|
3
|
+
export * from "__ENTRY_POINT__";
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var D1Database = class {
|
|
7
|
+
constructor(binding) {
|
|
8
|
+
this.binding = binding;
|
|
9
|
+
}
|
|
10
|
+
prepare(query) {
|
|
11
|
+
return new D1PreparedStatement(this, query);
|
|
12
|
+
}
|
|
13
|
+
async dump() {
|
|
14
|
+
const response = await this.binding.fetch("/dump", {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: {
|
|
17
|
+
"content-type": "application/json",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
if (response.status !== 200) {
|
|
21
|
+
const err = await response.json();
|
|
22
|
+
throw new Error("D1_DUMP_ERROR", {
|
|
23
|
+
cause: new Error(err.error),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return await response.arrayBuffer();
|
|
27
|
+
}
|
|
28
|
+
async batch(statements) {
|
|
29
|
+
const exec = await this._send(
|
|
30
|
+
"/query",
|
|
31
|
+
statements.map((s) => s.statement),
|
|
32
|
+
statements.map((s) => s.params)
|
|
33
|
+
);
|
|
34
|
+
return exec;
|
|
35
|
+
}
|
|
36
|
+
async exec(query) {
|
|
37
|
+
const lines = query.trim().split("\n");
|
|
38
|
+
const _exec = await this._send("/query", lines, []);
|
|
39
|
+
const exec = Array.isArray(_exec) ? _exec : [_exec];
|
|
40
|
+
const error = exec
|
|
41
|
+
.map((r) => {
|
|
42
|
+
return r.error ? 1 : 0;
|
|
43
|
+
})
|
|
44
|
+
.indexOf(1);
|
|
45
|
+
if (error !== -1) {
|
|
46
|
+
throw new Error("D1_EXEC_ERROR", {
|
|
47
|
+
cause: new Error(
|
|
48
|
+
`Error in line ${error + 1}: ${lines[error]}: ${exec[error].error}`
|
|
49
|
+
),
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
return {
|
|
53
|
+
count: exec.length,
|
|
54
|
+
duration: exec.reduce((p, c) => {
|
|
55
|
+
return p + c.duration;
|
|
56
|
+
}, 0),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async _send(endpoint, query, params) {
|
|
61
|
+
const body = JSON.stringify(
|
|
62
|
+
typeof query == "object"
|
|
63
|
+
? query.map((s, index) => {
|
|
64
|
+
return { sql: s, params: params[index] };
|
|
65
|
+
})
|
|
66
|
+
: {
|
|
67
|
+
sql: query,
|
|
68
|
+
params,
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
const response = await this.binding.fetch(endpoint, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: {
|
|
74
|
+
"content-type": "application/json",
|
|
75
|
+
},
|
|
76
|
+
body,
|
|
77
|
+
});
|
|
78
|
+
if (response.status !== 200) {
|
|
79
|
+
const err = await response.json();
|
|
80
|
+
throw new Error("D1_ERROR", { cause: new Error(err.error) });
|
|
81
|
+
}
|
|
82
|
+
const answer = await response.json();
|
|
83
|
+
return Array.isArray(answer) ? answer : answer;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var D1PreparedStatement = class {
|
|
87
|
+
constructor(database, statement, values) {
|
|
88
|
+
this.database = database;
|
|
89
|
+
this.statement = statement;
|
|
90
|
+
this.params = values || [];
|
|
91
|
+
}
|
|
92
|
+
bind(...values) {
|
|
93
|
+
return new D1PreparedStatement(this.database, this.statement, values);
|
|
94
|
+
}
|
|
95
|
+
async first(colName) {
|
|
96
|
+
const info = firstIfArray(
|
|
97
|
+
await this.database._send("/query", this.statement, this.params)
|
|
98
|
+
);
|
|
99
|
+
const results = info.results;
|
|
100
|
+
if (results.length < 1) {
|
|
101
|
+
throw new Error("D1_NORESULTS", { cause: new Error("No results") });
|
|
102
|
+
}
|
|
103
|
+
const result = results[0];
|
|
104
|
+
if (colName !== void 0) {
|
|
105
|
+
if (result[colName] === void 0) {
|
|
106
|
+
throw new Error("D1_COLUMN_NOTFOUND", {
|
|
107
|
+
cause: new Error(`Column not found`),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return result[colName];
|
|
111
|
+
} else {
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async run() {
|
|
116
|
+
return firstIfArray(
|
|
117
|
+
await this.database._send("/execute", this.statement, this.params)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
async all() {
|
|
121
|
+
return firstIfArray(
|
|
122
|
+
await this.database._send("/query", this.statement, this.params)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
async raw() {
|
|
126
|
+
const s = firstIfArray(
|
|
127
|
+
await this.database._send("/query", this.statement, this.params)
|
|
128
|
+
);
|
|
129
|
+
const raw = [];
|
|
130
|
+
for (var r in s.results) {
|
|
131
|
+
const entry = Object.keys(s.results[r]).map((k) => {
|
|
132
|
+
return s.results[r][k];
|
|
133
|
+
});
|
|
134
|
+
raw.push(entry);
|
|
135
|
+
}
|
|
136
|
+
return raw;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
function firstIfArray(results) {
|
|
140
|
+
return Array.isArray(results) ? results[0] : results;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/shim.ts
|
|
144
|
+
var D1_IMPORTS = __D1_IMPORTS__;
|
|
145
|
+
var LOCAL_MODE = __LOCAL_MODE__;
|
|
146
|
+
var D1_BETA_PREFIX = `__D1_BETA__`;
|
|
147
|
+
var envMap = /* @__PURE__ */ new Map();
|
|
148
|
+
function getMaskedEnv(env) {
|
|
149
|
+
if (envMap.has(env)) return envMap.get(env);
|
|
150
|
+
const newEnv = new Map(Object.entries(env));
|
|
151
|
+
D1_IMPORTS.filter((bindingName) =>
|
|
152
|
+
bindingName.startsWith(D1_BETA_PREFIX)
|
|
153
|
+
).forEach((bindingName) => {
|
|
154
|
+
newEnv.delete(bindingName);
|
|
155
|
+
const newName = bindingName.slice(D1_BETA_PREFIX.length);
|
|
156
|
+
const newBinding = !LOCAL_MODE
|
|
157
|
+
? new D1Database(env[bindingName])
|
|
158
|
+
: env[bindingName];
|
|
159
|
+
newEnv.set(newName, newBinding);
|
|
160
|
+
});
|
|
161
|
+
const newEnvObj = Object.fromEntries(newEnv.entries());
|
|
162
|
+
envMap.set(env, newEnvObj);
|
|
163
|
+
return newEnvObj;
|
|
164
|
+
}
|
|
165
|
+
var shim_default = {
|
|
166
|
+
async fetch(request, env, ctx) {
|
|
167
|
+
return worker.fetch(request, getMaskedEnv(env), ctx);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
async scheduled(controller, env, ctx) {
|
|
171
|
+
return worker.scheduled(controller, getMaskedEnv(env), ctx);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
export { shim_default as default };
|