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.
Files changed (51) hide show
  1. package/miniflare-dist/index.mjs +5 -20
  2. package/package.json +13 -3
  3. package/src/__tests__/api-dev.test.ts +20 -0
  4. package/src/__tests__/configuration.test.ts +125 -22
  5. package/src/__tests__/dev.test.tsx +0 -2
  6. package/src/__tests__/helpers/mock-oauth-flow.ts +4 -2
  7. package/src/__tests__/index.test.ts +2 -0
  8. package/src/__tests__/paths.test.ts +23 -1
  9. package/src/__tests__/publish.test.ts +8 -10
  10. package/src/__tests__/user.test.ts +4 -4
  11. package/src/__tests__/whoami.test.tsx +0 -1
  12. package/src/__tests__/worker-namespace.test.ts +102 -112
  13. package/src/api/dev.ts +12 -12
  14. package/src/bundle.ts +48 -0
  15. package/src/cfetch/internal.ts +37 -21
  16. package/src/config/environment.ts +20 -0
  17. package/src/config/index.ts +32 -0
  18. package/src/config/validation.ts +59 -0
  19. package/src/config-cache.ts +1 -1
  20. package/src/create-worker-upload-form.ts +9 -0
  21. package/src/d1/backups.tsx +212 -0
  22. package/src/d1/create.tsx +54 -0
  23. package/src/d1/delete.tsx +56 -0
  24. package/src/d1/execute.tsx +294 -0
  25. package/src/d1/formatTimeAgo.ts +14 -0
  26. package/src/d1/index.ts +75 -0
  27. package/src/d1/list.tsx +48 -0
  28. package/src/d1/options.ts +12 -0
  29. package/src/d1/types.tsx +14 -0
  30. package/src/d1/utils.ts +39 -0
  31. package/src/dev/dev.tsx +26 -3
  32. package/src/dev/get-local-persistence-path.tsx +31 -0
  33. package/src/dev/local.tsx +8 -8
  34. package/src/dev/start-server.ts +2 -3
  35. package/src/dev/use-esbuild.ts +8 -1
  36. package/src/dev.tsx +28 -28
  37. package/src/dialogs.tsx +4 -0
  38. package/src/environment-variables.ts +17 -2
  39. package/src/index.tsx +18 -16
  40. package/src/logger.ts +11 -4
  41. package/src/miniflare-cli/index.ts +11 -16
  42. package/src/pages/dev.tsx +13 -9
  43. package/src/paths.ts +30 -4
  44. package/src/proxy.ts +21 -1
  45. package/src/publish.ts +6 -0
  46. package/src/user/user.tsx +1 -0
  47. package/src/worker.ts +30 -0
  48. package/templates/d1-beta-facade.js +174 -0
  49. package/wrangler-dist/cli.d.ts +438 -7
  50. package/wrangler-dist/cli.js +10925 -3223
  51. 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(path: string): UrlPath {
21
+ export function toUrlPath(filePath: string): UrlPath {
22
22
  assert(
23
- !/^[a-z]:/i.test(path),
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 path.replace(/\\/g, "/") as UrlPath;
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
- logger.log(`⬣ Listening at ${localProtocol}://${ip}:${port}`);
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 };