wrangler 0.0.0-e7a0cd5b → 0.0.0-e87ad068

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