wrangler 0.0.0-e7a0cd5b → 0.0.0-e7cf0caa

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 (237) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +30 -10
  3. package/miniflare-dist/index.mjs +126 -74
  4. package/package.json +27 -19
  5. package/src/__tests__/api-dev.test.ts +65 -36
  6. package/src/__tests__/api-devregistry.test.ts +121 -0
  7. package/src/__tests__/api.test.ts +27 -5
  8. package/src/__tests__/configuration.test.ts +567 -86
  9. package/src/__tests__/d1/d1.test.ts +82 -0
  10. package/src/__tests__/d1/execute.test.ts +66 -0
  11. package/src/__tests__/d1/migrate.test.ts +157 -0
  12. package/src/__tests__/d1/splitter.test.ts +255 -0
  13. package/src/__tests__/delete.test.ts +91 -49
  14. package/src/__tests__/deployments.test.ts +330 -56
  15. package/src/__tests__/dev.test.tsx +198 -90
  16. package/src/__tests__/generate.test.ts +12 -10
  17. package/src/__tests__/guess-worker-format.test.ts +35 -0
  18. package/src/__tests__/helpers/end-event-loop.ts +6 -0
  19. package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
  20. package/src/__tests__/helpers/mock-console.ts +6 -0
  21. package/src/__tests__/helpers/mock-dialogs.ts +71 -85
  22. package/src/__tests__/helpers/mock-get-pages-upload-token.ts +25 -0
  23. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -5
  24. package/src/__tests__/helpers/mock-known-routes.ts +7 -2
  25. package/src/__tests__/helpers/mock-kv.ts +29 -16
  26. package/src/__tests__/helpers/mock-oauth-flow.ts +103 -92
  27. package/src/__tests__/helpers/mock-process.ts +8 -13
  28. package/src/__tests__/helpers/mock-set-timeout.ts +16 -0
  29. package/src/__tests__/helpers/msw/blob-worker.cjs +19 -0
  30. package/src/__tests__/helpers/msw/handlers/deployments.ts +128 -27
  31. package/src/__tests__/helpers/msw/handlers/namespaces.ts +18 -41
  32. package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
  33. package/src/__tests__/helpers/msw/handlers/r2.ts +14 -34
  34. package/src/__tests__/helpers/msw/handlers/script.ts +9 -28
  35. package/src/__tests__/helpers/msw/handlers/user.ts +13 -24
  36. package/src/__tests__/helpers/msw/handlers/zones.ts +6 -8
  37. package/src/__tests__/helpers/msw/index.ts +32 -1
  38. package/src/__tests__/helpers/msw/read-file-sync.js +61 -0
  39. package/src/__tests__/helpers/string-dynamic-values-matcher.ts +28 -0
  40. package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
  41. package/src/__tests__/index.test.ts +90 -72
  42. package/src/__tests__/init.test.ts +927 -631
  43. package/src/__tests__/jest.setup.ts +19 -42
  44. package/src/__tests__/kv.test.ts +355 -231
  45. package/src/__tests__/logger.test.ts +68 -2
  46. package/src/__tests__/logout.test.ts +47 -0
  47. package/src/__tests__/metrics.test.ts +94 -51
  48. package/src/__tests__/middleware.scheduled.test.ts +42 -32
  49. package/src/__tests__/middleware.test.ts +212 -122
  50. package/src/__tests__/mtls-certificates.test.ts +588 -0
  51. package/src/__tests__/pages/deployment-list.test.ts +80 -0
  52. package/src/__tests__/pages/functions-build.test.ts +452 -0
  53. package/src/__tests__/pages/pages.test.ts +81 -0
  54. package/src/__tests__/pages/project-create.test.ts +63 -0
  55. package/src/__tests__/pages/project-list.test.ts +110 -0
  56. package/src/__tests__/pages/project-upload.test.ts +500 -0
  57. package/src/__tests__/pages/publish.test.ts +2864 -0
  58. package/src/__tests__/pages-deployment-tail.test.ts +955 -0
  59. package/src/__tests__/parse.test.ts +106 -0
  60. package/src/__tests__/publish.test.ts +1615 -852
  61. package/src/__tests__/pubsub.test.ts +226 -100
  62. package/src/__tests__/queues.test.ts +238 -77
  63. package/src/__tests__/r2.test.ts +55 -57
  64. package/src/__tests__/secret.test.ts +179 -130
  65. package/src/__tests__/tail.test.ts +267 -109
  66. package/src/__tests__/test-old-node-version.js +3 -3
  67. package/src/__tests__/traverse-module-graph.test.ts +220 -0
  68. package/src/__tests__/tsconfig-sanity.ts +12 -0
  69. package/src/__tests__/tsconfig.json +8 -0
  70. package/src/__tests__/tsconfig.tsbuildinfo +1 -0
  71. package/src/__tests__/type-generation.test.ts +43 -26
  72. package/src/__tests__/user.test.ts +35 -72
  73. package/src/__tests__/whoami.test.tsx +8 -114
  74. package/src/__tests__/worker-namespace.test.ts +83 -69
  75. package/src/api/dev.ts +168 -60
  76. package/src/api/index.ts +10 -1
  77. package/src/api/mtls-certificate.ts +148 -0
  78. package/src/api/pages/create-worker-bundle-contents.ts +76 -0
  79. package/src/api/pages/index.ts +5 -0
  80. package/src/api/pages/publish.tsx +355 -0
  81. package/src/bundle.ts +263 -140
  82. package/src/cfetch/index.ts +0 -2
  83. package/src/cfetch/internal.ts +16 -21
  84. package/src/cli.ts +4 -4
  85. package/src/config/config.ts +14 -7
  86. package/src/config/environment.ts +79 -24
  87. package/src/config/index.ts +99 -38
  88. package/src/config/validation-helpers.ts +1 -1
  89. package/src/config/validation.ts +248 -19
  90. package/src/create-worker-preview.ts +19 -8
  91. package/src/create-worker-upload-form.ts +60 -22
  92. package/src/d1/backups.tsx +43 -42
  93. package/src/d1/constants.ts +2 -0
  94. package/src/d1/create.tsx +44 -30
  95. package/src/d1/{delete.tsx → delete.ts} +11 -14
  96. package/src/d1/execute.tsx +209 -151
  97. package/src/d1/index.ts +31 -7
  98. package/src/d1/list.tsx +22 -10
  99. package/src/d1/migrations/apply.tsx +212 -0
  100. package/src/d1/migrations/create.tsx +79 -0
  101. package/src/d1/migrations/helpers.ts +172 -0
  102. package/src/d1/migrations/index.ts +3 -0
  103. package/src/d1/migrations/list.tsx +95 -0
  104. package/src/d1/migrations/options.ts +23 -0
  105. package/src/d1/options.ts +12 -2
  106. package/src/d1/splitter.ts +161 -0
  107. package/src/d1/{types.tsx → types.ts} +11 -0
  108. package/src/d1/utils.ts +12 -2
  109. package/src/delete.ts +8 -16
  110. package/src/deployments.ts +340 -46
  111. package/src/deprecated/index.ts +8 -302
  112. package/src/dev/dev.tsx +57 -44
  113. package/src/dev/local.tsx +294 -91
  114. package/src/dev/remote.tsx +32 -3
  115. package/src/dev/start-server.ts +89 -68
  116. package/src/dev/use-esbuild.ts +17 -18
  117. package/src/dev/validate-dev-props.ts +9 -0
  118. package/src/{dev-registry.tsx → dev-registry.ts} +2 -1
  119. package/src/dev.tsx +178 -118
  120. package/src/dialogs.ts +136 -0
  121. package/src/dispatch-namespace.ts +21 -17
  122. package/src/docs/index.ts +97 -0
  123. package/src/entry.ts +19 -11
  124. package/src/environment-variables/factory.ts +88 -0
  125. package/src/environment-variables/misc-variables.ts +30 -0
  126. package/src/generate/index.ts +298 -0
  127. package/src/git-client.ts +15 -4
  128. package/src/{index.tsx → index.ts} +137 -63
  129. package/src/init.ts +249 -187
  130. package/src/inspect.ts +16 -1
  131. package/src/jest.d.ts +4 -0
  132. package/src/kv/index.ts +15 -17
  133. package/src/logger.ts +32 -5
  134. package/src/metrics/metrics-config.ts +3 -3
  135. package/src/metrics/send-event.ts +6 -1
  136. package/src/miniflare-cli/assets.ts +64 -28
  137. package/src/miniflare-cli/index.ts +4 -7
  138. package/src/miniflare-cli/tsconfig.json +9 -0
  139. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
  140. package/src/miniflare-cli/types.ts +11 -0
  141. package/src/module-collection.ts +91 -25
  142. package/src/mtls-certificate/cli.ts +155 -0
  143. package/src/pages/build.ts +410 -0
  144. package/src/pages/buildFunctions.ts +140 -0
  145. package/src/pages/deployment-tails.ts +281 -0
  146. package/src/pages/deployments.tsx +11 -33
  147. package/src/pages/{dev.tsx → dev.ts} +147 -124
  148. package/src/pages/errors.ts +1 -0
  149. package/src/pages/functions/buildPlugin.ts +72 -69
  150. package/src/pages/functions/buildWorker.ts +249 -114
  151. package/src/pages/functions/tsconfig.json +8 -0
  152. package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
  153. package/src/pages/{functions.tsx → functions.ts} +8 -4
  154. package/src/pages/{index.tsx → index.ts} +11 -3
  155. package/src/pages/projects.tsx +12 -22
  156. package/src/pages/prompt-select-project.tsx +31 -0
  157. package/src/pages/publish.tsx +45 -214
  158. package/src/pages/types.ts +6 -9
  159. package/src/pages/upload.tsx +58 -14
  160. package/src/pages/utils.ts +14 -3
  161. package/src/parse.ts +23 -1
  162. package/src/paths.ts +1 -1
  163. package/src/proxy.ts +5 -0
  164. package/src/publish/index.ts +20 -16
  165. package/src/publish/publish.ts +72 -47
  166. package/src/pubsub/{pubsub-commands.tsx → pubsub-commands.ts} +19 -20
  167. package/src/queues/cli/commands/consumer/add.ts +24 -24
  168. package/src/queues/cli/commands/consumer/index.ts +3 -6
  169. package/src/queues/cli/commands/consumer/remove.ts +11 -18
  170. package/src/queues/cli/commands/create.ts +8 -8
  171. package/src/queues/cli/commands/delete.ts +8 -8
  172. package/src/queues/cli/commands/index.ts +6 -4
  173. package/src/queues/cli/commands/list.ts +8 -8
  174. package/src/queues/client.ts +1 -0
  175. package/src/queues/utils.ts +18 -0
  176. package/src/r2/constants.ts +4 -0
  177. package/src/r2/helpers.ts +1 -0
  178. package/src/r2/index.ts +40 -28
  179. package/src/secret/index.ts +14 -17
  180. package/src/{sites.tsx → sites.ts} +1 -1
  181. package/src/tail/createTail.ts +94 -3
  182. package/src/tail/filters.ts +1 -1
  183. package/src/tail/index.ts +8 -11
  184. package/src/tail/printing.ts +15 -0
  185. package/src/traverse-module-graph.ts +53 -0
  186. package/src/tsconfig-sanity.ts +16 -0
  187. package/src/type-generation.ts +79 -57
  188. package/src/user/access.ts +0 -1
  189. package/src/user/auth-variables.ts +113 -0
  190. package/src/user/choose-account.tsx +1 -31
  191. package/src/user/index.ts +0 -1
  192. package/src/user/{user.tsx → user.ts} +123 -82
  193. package/src/utils/render.ts +93 -0
  194. package/src/{whoami.tsx → whoami.ts} +37 -71
  195. package/src/worker.ts +35 -3
  196. package/src/yargs-types.ts +22 -5
  197. package/templates/__tests__/tsconfig-sanity.ts +12 -0
  198. package/templates/__tests__/tsconfig.json +8 -0
  199. package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
  200. package/templates/checked-fetch.js +9 -3
  201. package/templates/d1-beta-facade.js +95 -25
  202. package/templates/facade.d.ts +14 -0
  203. package/templates/first-party-worker-module-facade.ts +4 -3
  204. package/templates/format-dev-errors.ts +8 -6
  205. package/templates/gitignore +1 -0
  206. package/templates/init-tests/test-jest-new-worker.js +3 -5
  207. package/templates/init-tests/test-vitest-new-worker.js +3 -5
  208. package/templates/init-tests/test-vitest-new-worker.ts +25 -0
  209. package/templates/middleware/loader-modules.ts +2 -2
  210. package/templates/middleware/loader-sw.ts +51 -31
  211. package/templates/middleware/middleware-miniflare3-json-error.ts +32 -0
  212. package/templates/new-worker.ts +3 -0
  213. package/templates/pages-dev-pipeline.ts +4 -1
  214. package/templates/pages-shim.ts +4 -1
  215. package/templates/pages-template-plugin.ts +19 -10
  216. package/templates/serve-static-assets.ts +17 -14
  217. package/templates/service-bindings-module-facade.js +1 -0
  218. package/templates/tsconfig-sanity.ts +11 -0
  219. package/templates/tsconfig.init.json +105 -0
  220. package/templates/tsconfig.json +5 -103
  221. package/templates/tsconfig.tsbuildinfo +1 -0
  222. package/wrangler-dist/cli.d.ts +826 -77
  223. package/wrangler-dist/cli.js +130734 -140838
  224. package/wrangler-dist/wasm-sync.wasm +0 -0
  225. package/src/__tests__/api-devregistry.test.js +0 -57
  226. package/src/__tests__/d1.test.ts +0 -72
  227. package/src/__tests__/dialogs.test.tsx +0 -40
  228. package/src/__tests__/helpers/mock-cfetch.ts +0 -333
  229. package/src/__tests__/pages.test.ts +0 -2223
  230. package/src/dialogs.tsx +0 -163
  231. package/src/environment-variables.ts +0 -50
  232. package/src/pages/build.tsx +0 -238
  233. package/src/user/env-vars.ts +0 -46
  234. package/templates/experimental-local-cache-stubs.js +0 -27
  235. /package/src/{bundle-reporter.tsx → bundle-reporter.ts} +0 -0
  236. /package/src/dev/{get-local-persistence-path.tsx → get-local-persistence-path.ts} +0 -0
  237. /package/src/pages/{hash.tsx → hash.ts} +0 -0
@@ -1,67 +1,60 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { render, Static, Text } from "ink";
4
+ import chalk from "chalk";
5
+ import { Static, Text } from "ink";
5
6
  import Table from "ink-table";
6
7
  import { npxImport } from "npx-import";
7
8
  import React from "react";
8
9
  import { fetchResult } from "../cfetch";
9
- import { withConfig } from "../config";
10
+ import { readConfig } from "../config";
10
11
  import { getLocalPersistencePath } from "../dev/get-local-persistence-path";
11
- import { confirm, logDim } from "../dialogs";
12
+ import { confirm } from "../dialogs";
12
13
  import { logger } from "../logger";
14
+ import { readFileSync } from "../parse";
13
15
  import { readableRelative } from "../paths";
14
16
  import { requireAuth } from "../user";
15
- import { Name } from "./options";
17
+ import { renderToString } from "../utils/render";
18
+ import * as options from "./options";
19
+ import splitSqlQuery from "./splitter";
16
20
  import {
17
21
  d1BetaWarning,
18
22
  getDatabaseByNameOrBinding,
19
23
  getDatabaseInfoFromConfig,
20
24
  } from "./utils";
21
- import type { Config } from "../config";
25
+ import type { Config, ConfigFields, DevConfig, Environment } from "../config";
26
+ import type {
27
+ CommonYargsArgv,
28
+ StrictYargsOptionsToInterface,
29
+ } from "../yargs-types";
22
30
  import type { Database } from "./types";
23
- import type splitSqlQuery from "@databases/split-sql-query";
24
- import type { SQL, SQLQuery } from "@databases/sql";
25
- import type { Statement as StatementType } from "@miniflare/d1";
26
- import type { createSQLiteDB as createSQLiteDBType } from "@miniflare/shared";
27
- import type { Argv } from "yargs";
28
31
 
29
- type MiniflareNpxImportTypes = [
30
- {
31
- Statement: typeof StatementType;
32
- },
33
- {
34
- createSQLiteDB: typeof createSQLiteDBType;
35
- }
36
- ];
37
-
38
- type ExecuteArgs = {
39
- config?: string;
40
- name: string;
41
- file?: string;
42
- command?: string;
43
- local?: boolean;
44
- "persist-to"?: string;
45
- };
46
-
47
- type QueryResult = {
32
+ export type QueryResult = {
48
33
  results: Record<string, string | number | boolean>[];
49
34
  success: boolean;
50
- duration: number;
35
+ meta?: {
36
+ duration?: number;
37
+ };
51
38
  query?: string;
52
39
  };
53
- // Max number of bytes to send in a single /execute call
40
+ // Max number of statements to send in a single /execute call
54
41
  const QUERY_LIMIT = 10_000;
55
42
 
56
- export function Options(yargs: Argv): Argv<ExecuteArgs> {
57
- return Name(yargs)
43
+ export function Options(yargs: CommonYargsArgv) {
44
+ return options
45
+ .Database(yargs)
46
+ .option("yes", {
47
+ describe: 'Answer "yes" to any prompts',
48
+ type: "boolean",
49
+ alias: "y",
50
+ })
58
51
  .option("local", {
59
52
  describe:
60
53
  "Execute commands/files against a local DB for use with wrangler dev",
61
54
  type: "boolean",
62
55
  })
63
56
  .option("file", {
64
- describe: "A .sql file to injest",
57
+ describe: "A .sql file to ingest",
65
58
  type: "string",
66
59
  })
67
60
  .option("command", {
@@ -72,54 +65,54 @@ export function Options(yargs: Argv): Argv<ExecuteArgs> {
72
65
  describe: "Specify directory to use for local persistence (for --local)",
73
66
  type: "string",
74
67
  requiresArg: true,
68
+ })
69
+ .option("json", {
70
+ describe: "Return output as clean JSON",
71
+ type: "boolean",
72
+ default: false,
73
+ })
74
+ .option("preview", {
75
+ describe: "Execute commands/files against a preview D1 DB",
76
+ type: "boolean",
77
+ default: false,
75
78
  });
76
79
  }
77
80
 
78
- function shorten(query: string | undefined, length: number) {
79
- return query && query.length > length
80
- ? query.slice(0, length) + "..."
81
- : query;
82
- }
83
-
84
- export const Handler = withConfig<ExecuteArgs>(
85
- async ({ config, name, file, command, local, persistTo }): Promise<void> => {
86
- logger.log(d1BetaWarning);
87
- if (file && command)
88
- return console.error(`Error: can't provide both --command and --file.`);
89
- const { parser, splitter } = await loadSqlUtils();
81
+ type HandlerOptions = StrictYargsOptionsToInterface<typeof Options>;
90
82
 
91
- const sql = file
92
- ? parser.file(file)
93
- : command
94
- ? parser.__dangerous__rawValue(command)
95
- : null;
96
-
97
- if (!sql) throw new Error(`Error: must provide --command or --file.`);
98
- if (persistTo && !local)
99
- throw new Error(`Error: can't use --persist-to without --local`);
83
+ export const Handler = async (args: HandlerOptions): Promise<void> => {
84
+ const { local, database, yes, persistTo, file, command, json, preview } =
85
+ args;
86
+ const existingLogLevel = logger.loggerLevel;
87
+ if (json) {
88
+ // set loggerLevel to error to avoid readConfig warnings appearing in JSON output
89
+ logger.loggerLevel = "error";
90
+ }
91
+ const config = readConfig(args.config, args);
92
+ logger.log(d1BetaWarning);
93
+ if (file && command)
94
+ return logger.error(`Error: can't provide both --command and --file.`);
100
95
 
101
- const isInteractive = process.stdout.isTTY;
102
- const response: QueryResult[] | null = local
103
- ? await executeLocally(
104
- config,
105
- name,
106
- isInteractive,
107
- splitSql(splitter, sql),
108
- persistTo
109
- )
110
- : await executeRemotely(
111
- config,
112
- name,
113
- isInteractive,
114
- batchSplit(splitter, sql)
115
- );
96
+ const isInteractive = process.stdout.isTTY;
97
+ const response: QueryResult[] | null = await executeSql({
98
+ local,
99
+ config,
100
+ name: database,
101
+ shouldPrompt: isInteractive && !yes,
102
+ persistTo,
103
+ file,
104
+ command,
105
+ json,
106
+ preview,
107
+ });
116
108
 
117
- // Early exit if prompt rejected
118
- if (!response) return;
109
+ // Early exit if prompt rejected
110
+ if (!response) return;
119
111
 
120
- if (isInteractive) {
121
- // Render table if single result
122
- render(
112
+ if (isInteractive && !json) {
113
+ // Render table if single result
114
+ logger.log(
115
+ renderToString(
123
116
  <Static items={response}>
124
117
  {(result) => {
125
118
  // batch results
@@ -138,20 +131,88 @@ export const Handler = withConfig<ExecuteArgs>(
138
131
  }
139
132
  }}
140
133
  </Static>
134
+ )
135
+ );
136
+ } else {
137
+ // set loggerLevel back to what it was before to actually output the JSON in stdout
138
+ logger.loggerLevel = existingLogLevel;
139
+ logger.log(JSON.stringify(response, null, 2));
140
+ }
141
+ };
142
+
143
+ export async function executeSql({
144
+ local,
145
+ config,
146
+ name,
147
+ shouldPrompt,
148
+ persistTo,
149
+ file,
150
+ command,
151
+ json,
152
+ preview,
153
+ }: {
154
+ local: boolean | undefined;
155
+ config: ConfigFields<DevConfig> & Environment;
156
+ name: string;
157
+ shouldPrompt: boolean | undefined;
158
+ persistTo: string | undefined;
159
+ file: string | undefined;
160
+ command: string | undefined;
161
+ json: boolean | undefined;
162
+ preview: boolean | undefined;
163
+ }) {
164
+ const sql = file ? readFileSync(file) : command;
165
+ if (!sql) throw new Error(`Error: must provide --command or --file.`);
166
+ if (preview && local)
167
+ throw new Error(`Error: can't use --preview with --local`);
168
+ if (persistTo && !local)
169
+ throw new Error(`Error: can't use --persist-to without --local`);
170
+ logger.log(`🌀 Mapping SQL input into an array of statements`);
171
+ const queries = splitSqlQuery(sql);
172
+
173
+ if (file && sql) {
174
+ if (queries[0].startsWith("SQLite format 3")) {
175
+ //TODO: update this error to recommend using `wrangler d1 restore` when it exists
176
+ throw new Error(
177
+ "Provided file is a binary SQLite database file instead of an SQL text file.\nThe execute command can only process SQL text files.\nPlease export an SQL file from your SQLite database and try again."
141
178
  );
142
- } else {
143
- console.log(JSON.stringify(response, null, 2));
144
179
  }
145
180
  }
146
- );
147
181
 
148
- async function executeLocally(
149
- config: Config,
150
- name: string,
151
- isInteractive: boolean,
152
- queries: string[],
153
- persistTo: string | undefined
154
- ) {
182
+ return local
183
+ ? await executeLocally({
184
+ config,
185
+ name,
186
+ shouldPrompt,
187
+ queries,
188
+ persistTo,
189
+ json,
190
+ })
191
+ : await executeRemotely({
192
+ config,
193
+ name,
194
+ shouldPrompt,
195
+ batches: batchSplit(queries),
196
+ json,
197
+ preview,
198
+ });
199
+ }
200
+
201
+ async function executeLocally({
202
+ config,
203
+ name,
204
+ shouldPrompt,
205
+ queries,
206
+ persistTo,
207
+ json,
208
+ }: {
209
+ config: Config;
210
+ name: string;
211
+ shouldPrompt: boolean | undefined;
212
+ queries: string[];
213
+ persistTo: string | undefined;
214
+ json: boolean | undefined;
215
+ }) {
155
216
  const localDB = getDatabaseInfoFromConfig(config, name);
156
217
  if (!localDB) {
157
218
  throw new Error(
@@ -167,49 +228,56 @@ async function executeLocally(
167
228
 
168
229
  const dbDir = path.join(persistencePath, "d1");
169
230
  const dbPath = path.join(dbDir, `${localDB.binding}.sqlite3`);
170
- const [{ Statement }, { createSQLiteDB }] =
171
- await npxImport<MiniflareNpxImportTypes>(
172
- ["@miniflare/d1", "@miniflare/shared"],
173
- logDim
174
- );
231
+ const [{ D1Database, D1DatabaseAPI }, { createSQLiteDB }] = await npxImport<
232
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
233
+ [typeof import("@miniflare/d1"), typeof import("@miniflare/shared")]
234
+ >(["@miniflare/d1", "@miniflare/shared"], logger.log);
175
235
 
176
- if (!existsSync(dbDir) && isInteractive) {
177
- const ok = await confirm(
178
- `About to create ${readableRelative(dbPath)}, ok?`
179
- );
236
+ if (!existsSync(dbDir)) {
237
+ const ok =
238
+ json ||
239
+ !shouldPrompt ||
240
+ (await confirm(`About to create ${readableRelative(dbPath)}, ok?`));
180
241
  if (!ok) return null;
181
242
  await mkdir(dbDir, { recursive: true });
182
243
  }
183
244
 
184
245
  logger.log(`🌀 Loading DB at ${readableRelative(dbPath)}`);
185
- const db = await createSQLiteDB(dbPath);
186
-
187
- const results: QueryResult[] = [];
188
- for (const sql of queries) {
189
- const statement = new Statement(db, sql);
190
- results.push((await statement.all()) as QueryResult);
191
- }
192
246
 
193
- return results;
247
+ const sqliteDb = await createSQLiteDB(dbPath);
248
+ const db = new D1Database(new D1DatabaseAPI(sqliteDb));
249
+ const stmts = queries.map((query) => db.prepare(query));
250
+ return (await db.batch(stmts)) as QueryResult[];
194
251
  }
195
252
 
196
- async function executeRemotely(
197
- config: Config,
198
- name: string,
199
- isInteractive: boolean,
200
- batches: string[]
201
- ) {
202
- if (batches.length > 1) {
253
+ async function executeRemotely({
254
+ config,
255
+ name,
256
+ shouldPrompt,
257
+ batches,
258
+ json,
259
+ preview,
260
+ }: {
261
+ config: Config;
262
+ name: string;
263
+ shouldPrompt: boolean | undefined;
264
+ batches: string[];
265
+ json: boolean | undefined;
266
+ preview: boolean | undefined;
267
+ }) {
268
+ const multiple_batches = batches.length > 1;
269
+ // in JSON mode, we don't want a prompt here
270
+ if (multiple_batches && !json) {
203
271
  const warning = `⚠️ Too much SQL to send at once, this execution will be sent as ${batches.length} batches.`;
204
272
 
205
- if (isInteractive) {
273
+ if (shouldPrompt) {
206
274
  const ok = await confirm(
207
275
  `${warning}\nℹ️ Each batch is sent individually and may leave your DB in an unexpected state if a later batch fails.\n⚠️ Make sure you have a recent backup. Ok to proceed?`
208
276
  );
209
277
  if (!ok) return null;
210
278
  logger.log(`🌀 Let's go`);
211
279
  } else {
212
- console.error(warning);
280
+ logger.error(warning);
213
281
  }
214
282
  }
215
283
 
@@ -219,22 +287,28 @@ async function executeRemotely(
219
287
  accountId,
220
288
  name
221
289
  );
222
-
223
- if (isInteractive) {
224
- logger.log(`🌀 Executing on ${name} (${db.uuid}):`);
225
- } else {
226
- // Pipe to error so we don't break jq
227
- console.error(`Executing on ${name} (${db.uuid}):`);
290
+ if (preview && !db.previewDatabaseUuid) {
291
+ throw logger.error(
292
+ "Please define a `preview_database_id` in your wrangler.toml to execute your queries against a preview database"
293
+ );
228
294
  }
295
+ const dbUuid = preview ? db.previewDatabaseUuid : db.uuid;
296
+ logger.log(`🌀 Executing on ${name} (${dbUuid}):`);
229
297
 
230
298
  const results: QueryResult[] = [];
231
299
  for (const sql of batches) {
300
+ if (multiple_batches)
301
+ logger.log(
302
+ chalk.gray(` ${sql.slice(0, 70)}${sql.length > 70 ? "..." : ""}`)
303
+ );
304
+
232
305
  const result = await fetchResult<QueryResult[]>(
233
- `/accounts/${accountId}/d1/database/${db.uuid}/query`,
306
+ `/accounts/${accountId}/d1/database/${dbUuid}/query`,
234
307
  {
235
308
  method: "POST",
236
309
  headers: {
237
310
  "Content-Type": "application/json",
311
+ ...(db.internal_env ? { "x-d1-internal-env": db.internal_env } : {}),
238
312
  },
239
313
  body: JSON.stringify({ sql }),
240
314
  }
@@ -247,53 +321,37 @@ async function executeRemotely(
247
321
 
248
322
  function logResult(r: QueryResult | QueryResult[]) {
249
323
  logger.log(
250
- `🚣 Executed ${Array.isArray(r) ? r.length : "1"} command(s) in ${
324
+ `🚣 Executed ${
325
+ Array.isArray(r) ? `${r.length} commands` : "1 command"
326
+ } in ${
251
327
  Array.isArray(r)
252
328
  ? r
253
- .map((d: QueryResult) => d.duration)
329
+ .map((d: QueryResult) => d.meta?.duration || 0)
254
330
  .reduce((a: number, b: number) => a + b, 0)
255
- : r.duration
331
+ : r.meta?.duration
256
332
  }ms`
257
333
  );
258
334
  }
259
335
 
260
- function splitSql(splitter: (query: SQLQuery) => SQLQuery[], sql: SQLQuery) {
261
- // We have no interpolations, so convert everything to text
262
- logger.log(`🌀 Mapping SQL input into an array of statements`);
263
- return splitter(sql).map(
264
- (q) =>
265
- q.format({
266
- escapeIdentifier: (_) => "",
267
- formatValue: (_, __) => ({ placeholder: "", value: "" }),
268
- }).text
269
- );
270
- }
271
-
272
- function batchSplit(splitter: typeof splitSqlQuery, sql: SQLQuery) {
273
- const queries = splitSql(splitter, sql);
336
+ function batchSplit(queries: string[]) {
274
337
  logger.log(`🌀 Parsing ${queries.length} statements`);
338
+ const num_batches = Math.ceil(queries.length / QUERY_LIMIT);
275
339
  const batches: string[] = [];
276
- const nbatches = Math.floor(queries.length / QUERY_LIMIT);
277
- for (let i = 0; i <= nbatches; i++) {
340
+ for (let i = 0; i < num_batches; i++) {
278
341
  batches.push(
279
342
  queries.slice(i * QUERY_LIMIT, (i + 1) * QUERY_LIMIT).join("; ")
280
343
  );
281
344
  }
282
- logger.log(
283
- `🌀 We are sending ${batches.length} batch(es) to D1 (limited to ${QUERY_LIMIT} statements per batch)`
284
- );
345
+ if (num_batches > 1) {
346
+ logger.log(
347
+ `🌀 We are sending ${num_batches} batch(es) to D1 (limited to ${QUERY_LIMIT} statements per batch)`
348
+ );
349
+ }
285
350
  return batches;
286
351
  }
287
352
 
288
- async function loadSqlUtils() {
289
- const [
290
- { default: parser },
291
- {
292
- // No idea why this is doubly-nested, see https://github.com/ForbesLindesay/atdatabases/issues/255
293
- default: { default: splitter },
294
- },
295
- ] = await npxImport<
296
- [{ default: SQL }, { default: { default: typeof splitSqlQuery } }]
297
- >(["@databases/sql@3.2.0", "@databases/split-sql-query@1.0.3"], logDim);
298
- return { parser, splitter };
353
+ function shorten(query: string | undefined, length: number) {
354
+ return query && query.length > length
355
+ ? query.slice(0, length) + "..."
356
+ : query;
299
357
  }
package/src/d1/index.ts CHANGED
@@ -3,11 +3,11 @@ import * as Create from "./create";
3
3
  import * as Delete from "./delete";
4
4
  import * as Execute from "./execute";
5
5
  import * as List from "./list";
6
+ import * as Migrations from "./migrations";
6
7
  import { d1BetaWarning } from "./utils";
7
- import type { CommonYargsOptions } from "../yargs-types";
8
- import type { Argv } from "yargs";
8
+ import type { CommonYargsArgv } from "../yargs-types";
9
9
 
10
- export const d1 = (yargs: Argv<CommonYargsOptions>) => {
10
+ export function d1(yargs: CommonYargsArgv) {
11
11
  return (
12
12
  yargs
13
13
  .command("list", "List D1 databases", List.Options, List.Handler)
@@ -23,8 +23,9 @@ export const d1 = (yargs: Argv<CommonYargsOptions>) => {
23
23
  Delete.Options,
24
24
  Delete.Handler
25
25
  )
26
- .command("backup", "Interact with D1 Backups", (yargs2) =>
27
- yargs2
26
+ .command("backup", "Interact with D1 Backups", (backupArgs) =>
27
+ backupArgs
28
+ .demandCommand()
28
29
  .command(
29
30
  "list <name>",
30
31
  "List your D1 backups",
@@ -66,11 +67,34 @@ export const d1 = (yargs: Argv<CommonYargsOptions>) => {
66
67
  // }
67
68
  // )
68
69
  .command(
69
- "execute <name>",
70
+ "execute <database>",
70
71
  "Executed command or SQL file",
71
72
  Execute.Options,
72
73
  Execute.Handler
73
74
  )
75
+ .command("migrations", "Interact with D1 Migrations", (yargs2) =>
76
+ yargs2
77
+ .demandCommand()
78
+ .command(
79
+ "list <database>",
80
+ "List your D1 migrations",
81
+ Migrations.ListOptions,
82
+ Migrations.ListHandler
83
+ )
84
+ .command(
85
+ "create <database> <message>",
86
+ "Create a new Migration",
87
+ Migrations.CreateOptions,
88
+ Migrations.CreateHandler
89
+ )
90
+ .command(
91
+ "apply <database>",
92
+ "Apply D1 Migrations",
93
+ Migrations.ApplyOptions,
94
+ Migrations.ApplyHandler
95
+ )
96
+ .epilogue(d1BetaWarning)
97
+ )
74
98
  .epilogue(d1BetaWarning)
75
99
  );
76
- };
100
+ }
package/src/d1/list.tsx CHANGED
@@ -1,26 +1,38 @@
1
- import { render } from "ink";
2
1
  import Table from "ink-table";
3
2
  import React from "react";
4
3
  import { fetchResult } from "../cfetch";
5
4
  import { logger } from "../logger";
6
5
  import { requireAuth } from "../user";
6
+ import { renderToString } from "../utils/render";
7
7
  import { d1BetaWarning } from "./utils";
8
+ import type {
9
+ CommonYargsArgv,
10
+ StrictYargsOptionsToInterface,
11
+ } from "../yargs-types";
8
12
  import type { Database } from "./types";
9
- import type { ArgumentsCamelCase, Argv } from "yargs";
10
13
 
11
- type ListArgs = Record<string, never>;
12
-
13
- export function Options(d1ListYargs: Argv): Argv<ListArgs> {
14
- return d1ListYargs.epilogue(d1BetaWarning);
14
+ export function Options(d1ListYargs: CommonYargsArgv) {
15
+ return d1ListYargs
16
+ .option("json", {
17
+ describe: "return output as clean JSON",
18
+ type: "boolean",
19
+ default: false,
20
+ })
21
+ .epilogue(d1BetaWarning);
15
22
  }
16
23
 
17
- export async function Handler(_: ArgumentsCamelCase<ListArgs>): Promise<void> {
24
+ export async function Handler({
25
+ json,
26
+ }: StrictYargsOptionsToInterface<typeof Options>): Promise<void> {
18
27
  const accountId = await requireAuth({});
19
- logger.log(d1BetaWarning);
20
-
21
28
  const dbs: Array<Database> = await listDatabases(accountId);
22
29
 
23
- render(<Table data={dbs}></Table>);
30
+ if (json) {
31
+ logger.log(JSON.stringify(dbs, null, 2));
32
+ } else {
33
+ logger.log(d1BetaWarning);
34
+ logger.log(renderToString(<Table data={dbs}></Table>));
35
+ }
24
36
  }
25
37
 
26
38
  export const listDatabases = async (