wrangler 2.1.6 → 2.1.8

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 (52) hide show
  1. package/miniflare-dist/index.mjs +5 -20
  2. package/package.json +14 -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 +59 -1
  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 +30 -3
  32. package/src/dev/get-local-persistence-path.tsx +31 -0
  33. package/src/dev/local.tsx +73 -11
  34. package/src/dev/start-server.ts +6 -3
  35. package/src/dev/use-esbuild.ts +12 -1
  36. package/src/dev.tsx +48 -29
  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 +7 -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/templates/experimental-local-cache-stubs.js +27 -0
  50. package/wrangler-dist/cli.d.ts +438 -7
  51. package/wrangler-dist/cli.js +11679 -3911
  52. package/src/miniflare-cli/enum-keys.ts +0 -17
package/src/index.tsx CHANGED
@@ -7,33 +7,34 @@ import TOML from "@iarna/toml";
7
7
  import chalk from "chalk";
8
8
  import onExit from "signal-exit";
9
9
  import supportsColor from "supports-color";
10
- import { setGlobalDispatcher, ProxyAgent } from "undici";
10
+ import { ProxyAgent, setGlobalDispatcher } from "undici";
11
11
  import makeCLI from "yargs";
12
12
  import { version as wranglerVersion } from "../package.json";
13
13
  import { fetchResult } from "./cfetch";
14
14
  import { findWranglerToml, readConfig } from "./config";
15
15
  import { createWorkerUploadForm } from "./create-worker-upload-form";
16
+ import { d1api } from "./d1";
16
17
  import { devHandler, devOptions } from "./dev";
17
18
  import { confirm, prompt } from "./dialogs";
18
19
  import { workerNamespaceCommands } from "./dispatch-namespace";
19
20
  import { getEntry } from "./entry";
20
21
  import { DeprecationError } from "./errors";
21
22
  import { generateHandler, generateOptions } from "./generate";
22
- import { initOptions, initHandler } from "./init";
23
+ import { initHandler, initOptions } from "./init";
23
24
  import {
24
- getKVNamespaceId,
25
- listKVNamespaces,
26
- listKVNamespaceKeys,
27
- putKVKeyValue,
28
- putKVBulkKeyValue,
29
- deleteKVBulkKeyValue,
30
25
  createKVNamespace,
31
- isValidKVNamespaceBinding,
26
+ deleteKVBulkKeyValue,
27
+ deleteKVKeyValue,
28
+ deleteKVNamespace,
32
29
  getKVKeyValue,
30
+ getKVNamespaceId,
33
31
  isKVKeyValue,
32
+ isValidKVNamespaceBinding,
33
+ listKVNamespaceKeys,
34
+ listKVNamespaces,
35
+ putKVBulkKeyValue,
36
+ putKVKeyValue,
34
37
  unexpectedKVKeyValueProps,
35
- deleteKVNamespace,
36
- deleteKVKeyValue,
37
38
  } from "./kv";
38
39
  import { logger } from "./logger";
39
40
  import * as metrics from "./metrics";
@@ -66,11 +67,11 @@ import {
66
67
  } from "./tail";
67
68
  import { updateCheck } from "./update-check";
68
69
  import {
70
+ listScopes,
69
71
  login,
70
72
  logout,
71
- listScopes,
72
- validateScopeKeys,
73
73
  requireAuth,
74
+ validateScopeKeys,
74
75
  } from "./user";
75
76
  import { whoami } from "./whoami";
76
77
 
@@ -80,7 +81,6 @@ import type { KeyValue } from "./kv";
80
81
  import type { TailCLIFilters } from "./tail";
81
82
  import type { Readable } from "node:stream";
82
83
  import type { RawData } from "ws";
83
- import type { CommandModule } from "yargs";
84
84
  import type Yargs from "yargs";
85
85
 
86
86
  export type ConfigPath = string | undefined;
@@ -262,7 +262,7 @@ function createCLIParser(argv: string[]) {
262
262
  .wrap(null);
263
263
 
264
264
  // Default help command that supports the subcommands
265
- const subHelp: CommandModule = {
265
+ const subHelp: Yargs.CommandModule = {
266
266
  command: ["*"],
267
267
  handler: async (args) => {
268
268
  setImmediate(() =>
@@ -578,7 +578,6 @@ function createCLIParser(argv: string[]) {
578
578
  "The --assets argument is experimental and may change or break at any time"
579
579
  );
580
580
  }
581
-
582
581
  if (args.latest) {
583
582
  logger.warn(
584
583
  "Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
@@ -1015,6 +1014,7 @@ function createCLIParser(argv: string[]) {
1015
1014
  vars: {},
1016
1015
  durable_objects: { bindings: [] },
1017
1016
  r2_buckets: [],
1017
+ d1_databases: [],
1018
1018
  services: [],
1019
1019
  wasm_modules: {},
1020
1020
  text_blobs: {},
@@ -2182,6 +2182,8 @@ function createCLIParser(argv: string[]) {
2182
2182
  }
2183
2183
  );
2184
2184
 
2185
+ wrangler.command("d1", "🗄 Interact with a D1 database", d1api);
2186
+
2185
2187
  wrangler.command(
2186
2188
  "pubsub",
2187
2189
  "📮 Interact and manage Pub/Sub Brokers",
package/src/logger.ts CHANGED
@@ -3,10 +3,12 @@ import { formatMessagesSync } from "esbuild";
3
3
  import { getEnvironmentVariableFactory } from "./environment-variables";
4
4
 
5
5
  const LOGGER_LEVELS = {
6
+ none: -1,
6
7
  error: 0,
7
8
  warn: 1,
8
- log: 2,
9
- debug: 3,
9
+ info: 2,
10
+ log: 3,
11
+ debug: 4,
10
12
  } as const;
11
13
 
12
14
  type LoggerLevel = keyof typeof LOGGER_LEVELS;
@@ -15,6 +17,7 @@ type LoggerLevel = keyof typeof LOGGER_LEVELS;
15
17
  const LOGGER_LEVEL_FORMAT_TYPE_MAP = {
16
18
  error: "error",
17
19
  warn: "warning",
20
+ info: undefined,
18
21
  log: undefined,
19
22
  debug: undefined,
20
23
  } as const;
@@ -31,17 +34,21 @@ class Logger {
31
34
  columns = process.stdout.columns;
32
35
 
33
36
  debug = (...args: unknown[]) => this.doLog("debug", args);
37
+ info = (...args: unknown[]) => this.doLog("info", args);
34
38
  log = (...args: unknown[]) => this.doLog("log", args);
35
39
  warn = (...args: unknown[]) => this.doLog("warn", args);
36
40
  error = (...args: unknown[]) => this.doLog("error", args);
37
41
 
38
- private doLog(messageLevel: LoggerLevel, args: unknown[]) {
42
+ private doLog(messageLevel: Exclude<LoggerLevel, "none">, args: unknown[]) {
39
43
  if (LOGGER_LEVELS[this.loggerLevel] >= LOGGER_LEVELS[messageLevel]) {
40
44
  console[messageLevel](this.formatMessage(messageLevel, format(...args)));
41
45
  }
42
46
  }
43
47
 
44
- private formatMessage(level: LoggerLevel, message: string): string {
48
+ private formatMessage(
49
+ level: Exclude<LoggerLevel, "none">,
50
+ message: string
51
+ ): string {
45
52
  const kind = LOGGER_LEVEL_FORMAT_TYPE_MAP[level];
46
53
  if (kind) {
47
54
  // Format the message using the esbuild formatter.
@@ -5,7 +5,6 @@ import {
5
5
  } from "@miniflare/durable-objects";
6
6
  import {
7
7
  Log,
8
- LogLevel,
9
8
  Miniflare,
10
9
  Response as MiniflareResponse,
11
10
  Request as MiniflareRequest,
@@ -14,7 +13,6 @@ import yargs from "yargs";
14
13
  import { hideBin } from "yargs/helpers";
15
14
  import { FatalError } from "../errors";
16
15
  import generateASSETSBinding from "./assets";
17
- import { enumKeys } from "./enum-keys";
18
16
  import { getRequestContextCheckOptions } from "./request-context";
19
17
  import type { Options } from "./assets";
20
18
  import type { AddressInfo } from "net";
@@ -35,26 +33,23 @@ class NoOpLog extends Log {
35
33
  }
36
34
 
37
35
  async function main() {
38
- const args = await yargs(hideBin(process.argv))
39
- .help(false)
40
- .version(false)
41
- .option("log", {
42
- choices: enumKeys(LogLevel),
43
- }).argv;
44
-
45
- const logLevel = LogLevel[args.log ?? "INFO"];
36
+ const args = await yargs(hideBin(process.argv)).help(false).version(false)
37
+ .argv;
38
+
46
39
  const requestContextCheckOptions = await getRequestContextCheckOptions();
47
40
  const config = {
48
41
  ...JSON.parse((args._[0] as string) ?? "{}"),
49
42
  ...requestContextCheckOptions,
50
43
  };
51
- //miniflare's logLevel 0 still logs routes, so lets override the logger
52
- config.log = config.disableLogs
53
- ? new NoOpLog()
54
- : new Log(logLevel, config.logOptions);
44
+ const logLevel = config.logLevel.toUpperCase();
45
+
46
+ config.log =
47
+ config.logLevel === "none"
48
+ ? new NoOpLog()
49
+ : new Log(logLevel, config.logOptions);
55
50
 
56
- if (logLevel > LogLevel.INFO) {
57
- console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
51
+ if (logLevel === "DEBUG" || logLevel === "VERBOSE") {
52
+ console.log("MINIFLARE OPTIONS:\n", JSON.stringify(config, null, 2));
58
53
  }
59
54
 
60
55
  config.bindings = {
package/src/pages/dev.tsx CHANGED
@@ -99,6 +99,10 @@ export function Options(yargs: Argv) {
99
99
  description: "KV namespace to bind (--kv KV_BINDING)",
100
100
  alias: "k",
101
101
  },
102
+ d1: {
103
+ type: "array",
104
+ description: "D1 database to bind",
105
+ },
102
106
  do: {
103
107
  type: "array",
104
108
  description: "Durable Object to bind (--do NAME=CLASS)",
@@ -146,12 +150,9 @@ export function Options(yargs: Argv) {
146
150
  type: "string",
147
151
  hidden: true,
148
152
  },
149
-
150
153
  "log-level": {
151
- // "none" will currently default to "error" for Wrangler Logger
152
154
  choices: ["debug", "info", "log", "warn", "error", "none"] as const,
153
155
  describe: "Specify logging level",
154
- default: "log",
155
156
  },
156
157
  })
157
158
  .epilogue(pagesBetaWarning);
@@ -170,6 +171,7 @@ export const Handler = async ({
170
171
  binding: bindings = [],
171
172
  kv: kvs = [],
172
173
  do: durableObjects = [],
174
+ d1: d1s = [],
173
175
  r2: r2s = [],
174
176
  "live-reload": liveReload,
175
177
  "local-protocol": localProtocol,
@@ -186,12 +188,8 @@ export const Handler = async ({
186
188
 
187
189
  type LogLevelArg = "debug" | "info" | "log" | "warn" | "error" | "none";
188
190
  if (logLevel) {
189
- // we don't define a "none" logLevel, so "error" will do for now.
190
191
  // The YargsOptionsToInterface doesn't handle the passing in of Unions from choices in Yargs
191
- logger.loggerLevel =
192
- (logLevel as LogLevelArg) === "none"
193
- ? "error"
194
- : (logLevel as Exclude<"none", LogLevelArg>);
192
+ logger.loggerLevel = logLevel as LogLevelArg;
195
193
  }
196
194
 
197
195
  if (!local) {
@@ -499,6 +497,12 @@ export const Handler = async ({
499
497
  return { binding: binding.toString(), bucket_name: "" };
500
498
  }),
501
499
 
500
+ d1Databases: d1s.map((binding) => ({
501
+ binding: binding.toString(),
502
+ database_id: "", // Required for types, but unused by dev
503
+ database_name: `local-${binding}`,
504
+ })),
505
+
502
506
  enablePagesAssetsServiceBinding: {
503
507
  proxyPort,
504
508
  directory,
@@ -508,8 +512,8 @@ export const Handler = async ({
508
512
  persistTo,
509
513
  showInteractiveDevSession: undefined,
510
514
  inspect: true,
511
- logLevel: "warn",
512
515
  logPrefix: "pages",
516
+ logLevel: logLevel ?? "warn",
513
517
  },
514
518
  { testMode: false, disableExperimentalWarning: true }
515
519
  );
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,8 @@ 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,
437
+ experimentalLocalStubCache: false,
432
438
  }
433
439
  );
434
440
 
@@ -476,6 +482,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
476
482
  data_blobs: config.data_blobs,
477
483
  durable_objects: config.durable_objects,
478
484
  r2_buckets: config.r2_buckets,
485
+ d1_databases: identifyD1BindingsAsBeta(config.d1_databases),
479
486
  services: config.services,
480
487
  dispatch_namespaces: config.dispatch_namespaces,
481
488
  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 };
@@ -0,0 +1,27 @@
1
+ // `workerd` currently throws on any use of the Cache API. Workers Sites
2
+ // requires the Cache API to function though, so stub it out to no-ops, like in
3
+ // regular `wrangler dev`.
4
+
5
+ class Cache {
6
+ async put(req, res) {}
7
+
8
+ async match(req, options) {}
9
+
10
+ async delete(req, options) {
11
+ return false;
12
+ }
13
+ }
14
+
15
+ class CacheStorage {
16
+ #cache = new Cache();
17
+
18
+ get default() {
19
+ return this.#cache;
20
+ }
21
+
22
+ async open(cacheName) {
23
+ return this.#cache;
24
+ }
25
+ }
26
+
27
+ globalThis.caches = new CacheStorage();