wrangler 0.0.0-e178d6fb → 0.0.0-e1c2f5b9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/bin/wrangler.js +31 -11
- package/miniflare-dist/index.mjs +1340 -330
- package/package.json +40 -13
- package/src/__tests__/access.test.ts +25 -0
- package/src/__tests__/api-dev.test.ts +238 -0
- package/src/__tests__/api-devregistry.test.js +64 -0
- package/src/__tests__/api.test.ts +80 -0
- package/src/__tests__/configuration.test.ts +683 -470
- package/src/__tests__/d1.test.ts +123 -0
- package/src/__tests__/delete.test.ts +263 -0
- package/src/__tests__/deployments.test.ts +105 -0
- package/src/__tests__/dev.test.tsx +453 -70
- package/src/__tests__/dialogs.test.tsx +40 -0
- package/src/__tests__/generate.test.ts +235 -0
- package/src/__tests__/get-host-from-url.test.ts +16 -0
- package/src/__tests__/helpers/mock-auth-domain.ts +20 -0
- package/src/__tests__/helpers/mock-cfetch.ts +92 -26
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/mock-dialogs.ts +1 -1
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
- package/src/__tests__/helpers/mock-known-routes.ts +7 -0
- package/src/__tests__/helpers/mock-kv.ts +1 -1
- package/src/__tests__/helpers/mock-oauth-flow.ts +74 -53
- package/src/__tests__/helpers/mock-process.ts +8 -13
- package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
- package/src/__tests__/helpers/msw/handlers/deployments.ts +59 -0
- package/src/__tests__/helpers/msw/handlers/namespaces.ts +104 -0
- package/src/__tests__/helpers/msw/handlers/oauth.ts +31 -0
- package/src/__tests__/helpers/msw/handlers/r2.ts +80 -0
- package/src/__tests__/helpers/msw/handlers/script.ts +75 -0
- package/src/__tests__/helpers/msw/handlers/user.ts +63 -0
- package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
- package/src/__tests__/helpers/msw/index.ts +21 -0
- package/src/__tests__/helpers/worker-scripts/child-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-worker.js +5 -0
- package/src/__tests__/helpers/worker-scripts/hello-world-wrangler.toml +1 -0
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +8 -0
- package/src/__tests__/helpers/worker-scripts/parent-wrangler.toml +5 -0
- package/src/__tests__/index.test.ts +103 -97
- package/src/__tests__/init.test.ts +1484 -382
- package/src/__tests__/jest.setup.ts +88 -5
- package/src/__tests__/kv.test.ts +655 -540
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/metrics.test.ts +95 -45
- package/src/__tests__/middleware.scheduled.test.ts +145 -0
- package/src/__tests__/middleware.test.ts +750 -0
- package/src/__tests__/pages-deployment-tail.test.ts +884 -0
- package/src/__tests__/pages.test.ts +1301 -119
- package/src/__tests__/paths.test.ts +39 -0
- package/src/__tests__/publish.test.ts +2266 -1180
- package/src/__tests__/pubsub.test.ts +211 -88
- package/src/__tests__/queues.test.ts +522 -0
- package/src/__tests__/r2.test.ts +363 -203
- package/src/__tests__/secret.test.ts +261 -59
- package/src/__tests__/tail.test.ts +352 -151
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/tsconfig-sanity.ts +12 -0
- package/src/__tests__/tsconfig.json +8 -0
- package/src/__tests__/tsconfig.tsbuildinfo +1 -0
- package/src/__tests__/type-generation.test.ts +233 -0
- package/src/__tests__/user.test.ts +59 -73
- package/src/__tests__/utils-collectKeyValues.test.ts +47 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/whoami.test.tsx +114 -66
- package/src/__tests__/worker-namespace.test.ts +154 -155
- package/src/api/dev.ts +170 -38
- package/src/api/index.ts +1 -0
- package/src/bundle-reporter.tsx +41 -2
- package/src/bundle.ts +668 -81
- package/src/cfetch/index.ts +40 -3
- package/src/cfetch/internal.ts +144 -32
- package/src/cli.ts +4 -3
- package/src/config/config.ts +27 -3
- package/src/config/environment.ts +131 -7
- package/src/config/index.ts +128 -8
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +411 -14
- package/src/config-cache.ts +3 -2
- package/src/create-worker-preview.ts +31 -6
- package/src/create-worker-upload-form.ts +84 -22
- package/src/d1/backups.tsx +223 -0
- package/src/d1/constants.ts +2 -0
- package/src/d1/create.tsx +63 -0
- package/src/d1/delete.tsx +56 -0
- package/src/d1/execute.tsx +352 -0
- package/src/d1/formatTimeAgo.ts +14 -0
- package/src/d1/index.ts +101 -0
- package/src/d1/list.tsx +48 -0
- package/src/d1/migrations/apply.tsx +196 -0
- package/src/d1/migrations/create.tsx +77 -0
- package/src/d1/migrations/helpers.ts +147 -0
- package/src/d1/migrations/index.tsx +3 -0
- package/src/d1/migrations/list.tsx +79 -0
- package/src/d1/migrations/options.ts +18 -0
- package/src/d1/options.ts +22 -0
- package/src/d1/types.tsx +24 -0
- package/src/d1/utils.ts +48 -0
- package/src/delete.ts +108 -0
- package/src/deployments.ts +83 -0
- package/src/deprecated/index.ts +145 -0
- package/src/dev/dev-vars.ts +11 -8
- package/src/dev/dev.tsx +220 -58
- package/src/dev/get-local-persistence-path.tsx +31 -0
- package/src/dev/local.tsx +740 -181
- package/src/dev/remote.tsx +340 -123
- package/src/dev/start-server.ts +543 -0
- package/src/dev/use-esbuild.ts +61 -7
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +201 -0
- package/src/dev.tsx +614 -258
- package/src/dialogs.tsx +41 -8
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +23 -22
- package/src/docs/index.ts +97 -0
- package/src/entry.ts +3 -2
- package/src/environment-variables/factory.ts +88 -0
- package/src/environment-variables/misc-variables.ts +30 -0
- package/src/generate/index.ts +300 -0
- package/src/git-client.ts +100 -7
- package/src/index.tsx +249 -1560
- package/src/init.ts +626 -150
- package/src/inspect.ts +137 -32
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/is-interactive.ts +4 -0
- package/src/jest.d.ts +4 -0
- package/src/{kv.ts → kv/helpers.ts} +3 -3
- package/src/kv/index.ts +596 -0
- package/src/logger.ts +27 -8
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-config.ts +4 -4
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +13 -8
- package/src/miniflare-cli/assets.ts +172 -465
- package/src/miniflare-cli/index.ts +149 -24
- package/src/miniflare-cli/tsconfig.json +9 -0
- package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -0
- package/src/miniflare-cli/types.ts +11 -0
- package/src/module-collection.ts +12 -3
- package/src/pages/build.tsx +108 -42
- package/src/pages/constants.ts +11 -0
- package/src/pages/deployment-tails.tsx +284 -0
- package/src/pages/deployments.tsx +9 -31
- package/src/pages/dev.tsx +417 -108
- package/src/pages/errors.ts +66 -0
- package/src/pages/functions/buildPlugin.ts +86 -84
- package/src/pages/functions/buildWorker.ts +121 -106
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +282 -0
- package/src/pages/functions/routes-transformation.ts +115 -0
- package/src/pages/functions/routes-validation.test.ts +403 -0
- package/src/pages/functions/routes-validation.ts +202 -0
- package/src/pages/functions/tsconfig.json +8 -0
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -0
- package/src/pages/functions.tsx +82 -0
- package/src/pages/hash.tsx +13 -0
- package/src/pages/index.tsx +73 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/prompt-select-project.tsx +31 -0
- package/src/pages/publish.tsx +183 -60
- package/src/pages/types.ts +1 -0
- package/src/pages/upload.tsx +107 -56
- package/src/pages/utils.ts +13 -2
- package/src/paths.ts +49 -4
- package/src/proxy.ts +375 -157
- package/src/publish/index.ts +270 -0
- package/src/{publish.ts → publish/publish.ts} +431 -188
- package/src/pubsub/pubsub-commands.tsx +4 -3
- package/src/queues/cli/commands/consumer/add.ts +71 -0
- package/src/queues/cli/commands/consumer/index.ts +22 -0
- package/src/queues/cli/commands/consumer/remove.ts +38 -0
- package/src/queues/cli/commands/create.ts +25 -0
- package/src/queues/cli/commands/delete.ts +26 -0
- package/src/queues/cli/commands/index.ts +36 -0
- package/src/queues/cli/commands/list.ts +25 -0
- package/src/queues/client.ts +135 -0
- package/src/queues/utils.ts +18 -0
- package/src/r2/constants.ts +4 -0
- package/src/r2/helpers.ts +131 -0
- package/src/r2/index.ts +289 -0
- package/src/routes.ts +140 -0
- package/src/secret/index.ts +379 -0
- package/src/sites.tsx +3 -3
- package/src/tail/createTail.ts +379 -0
- package/src/tail/filters.ts +4 -6
- package/src/tail/index.ts +199 -284
- package/src/tail/printing.ts +43 -3
- package/src/tsconfig-sanity.ts +16 -0
- package/src/type-generation.ts +181 -0
- package/src/user/access.ts +68 -0
- package/src/user/auth-variables.ts +113 -0
- package/src/user/choose-account.tsx +1 -1
- package/src/user/index.ts +0 -1
- package/src/user/user.tsx +116 -49
- package/src/utils/collectKeyValues.ts +14 -0
- package/src/whoami.tsx +80 -2
- package/src/worker.ts +69 -9
- package/src/yargs-types.ts +24 -0
- package/src/zones.ts +124 -9
- package/templates/__tests__/pages-dev-util.test.ts +128 -0
- package/templates/__tests__/tsconfig-sanity.ts +12 -0
- package/templates/__tests__/tsconfig.json +8 -0
- package/templates/__tests__/tsconfig.tsbuildinfo +1 -0
- package/templates/d1-beta-facade.js +205 -0
- package/templates/facade.d.ts +14 -0
- package/templates/first-party-worker-module-facade.ts +19 -0
- package/templates/format-dev-errors.ts +33 -0
- package/templates/init-tests/test-jest-new-worker.js +23 -0
- package/templates/init-tests/test-vitest-new-worker.js +24 -0
- package/templates/init-tests/test-vitest-new-worker.ts +25 -0
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +82 -0
- package/templates/middleware/loader-sw.ts +233 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +15 -0
- package/templates/new-worker.js +1 -1
- package/templates/pages-dev-pipeline.ts +31 -0
- package/templates/pages-dev-util.ts +52 -0
- package/templates/pages-shim.ts +4 -1
- package/templates/pages-template-plugin.ts +21 -5
- package/templates/pages-template-worker.ts +37 -9
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +23 -7
- package/templates/service-bindings-module-facade.js +54 -0
- package/templates/service-bindings-sw-facade.js +42 -0
- package/templates/tsconfig-sanity.ts +11 -0
- package/templates/tsconfig.init.json +106 -0
- package/templates/tsconfig.json +5 -102
- package/templates/tsconfig.tsbuildinfo +1 -0
- package/wrangler-dist/cli.d.ts +621 -42
- package/wrangler-dist/cli.js +144441 -102606
- package/src/environment-variables.ts +0 -35
- package/src/faye-websocket.d.ts +0 -6
- package/src/generate.ts +0 -33
- package/src/miniflare-cli/enum-keys.ts +0 -17
- package/src/preview.ts +0 -31
- package/src/r2.ts +0 -50
- package/src/user/env-vars.ts +0 -46
|
@@ -23,6 +23,34 @@ export function toMimeType(type: CfModuleType): string {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
type WorkerMetadataBinding =
|
|
27
|
+
// If you add any new binding types here, also add it to safeBindings
|
|
28
|
+
// under validateUnsafeBinding in config/validation.ts
|
|
29
|
+
| { type: "plain_text"; name: string; text: string }
|
|
30
|
+
| { type: "json"; name: string; json: unknown }
|
|
31
|
+
| { type: "wasm_module"; name: string; part: string }
|
|
32
|
+
| { type: "text_blob"; name: string; part: string }
|
|
33
|
+
| { type: "data_blob"; name: string; part: string }
|
|
34
|
+
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
35
|
+
| {
|
|
36
|
+
type: "durable_object_namespace";
|
|
37
|
+
name: string;
|
|
38
|
+
class_name: string;
|
|
39
|
+
script_name?: string;
|
|
40
|
+
environment?: string;
|
|
41
|
+
}
|
|
42
|
+
| { type: "queue"; name: string; queue_name: string }
|
|
43
|
+
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
44
|
+
| { type: "d1"; name: string; id: string; internalEnv?: string }
|
|
45
|
+
| { type: "service"; name: string; service: string; environment?: string }
|
|
46
|
+
| { type: "analytics_engine"; name: string; dataset?: string }
|
|
47
|
+
| { type: "dispatch_namespace"; name: string; namespace: string }
|
|
48
|
+
| {
|
|
49
|
+
type: "logfwdr";
|
|
50
|
+
name: string;
|
|
51
|
+
destination: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
26
54
|
export interface WorkerMetadata {
|
|
27
55
|
/** The name of the entry point module. Only exists when the worker is in the ES module format */
|
|
28
56
|
main_module?: string;
|
|
@@ -32,26 +60,10 @@ export interface WorkerMetadata {
|
|
|
32
60
|
compatibility_flags?: string[];
|
|
33
61
|
usage_model?: "bundled" | "unbound";
|
|
34
62
|
migrations?: CfDurableObjectMigrations;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| { type: "json"; name: string; json: unknown }
|
|
40
|
-
| { type: "wasm_module"; name: string; part: string }
|
|
41
|
-
| { type: "text_blob"; name: string; part: string }
|
|
42
|
-
| { type: "data_blob"; name: string; part: string }
|
|
43
|
-
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
44
|
-
| {
|
|
45
|
-
type: "durable_object_namespace";
|
|
46
|
-
name: string;
|
|
47
|
-
class_name: string;
|
|
48
|
-
script_name?: string;
|
|
49
|
-
environment?: string;
|
|
50
|
-
}
|
|
51
|
-
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
52
|
-
| { type: "service"; name: string; service: string; environment?: string }
|
|
53
|
-
| { type: "namespace"; name: string; namespace: string }
|
|
54
|
-
)[];
|
|
63
|
+
capnp_schema?: string;
|
|
64
|
+
bindings: WorkerMetadataBinding[];
|
|
65
|
+
keep_bindings?: WorkerMetadataBinding["type"][];
|
|
66
|
+
logpush?: boolean;
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
/**
|
|
@@ -66,6 +78,8 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
66
78
|
usage_model,
|
|
67
79
|
compatibility_date,
|
|
68
80
|
compatibility_flags,
|
|
81
|
+
keepVars,
|
|
82
|
+
logpush,
|
|
69
83
|
} = worker;
|
|
70
84
|
|
|
71
85
|
let { modules } = worker;
|
|
@@ -100,6 +114,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
100
114
|
}
|
|
101
115
|
);
|
|
102
116
|
|
|
117
|
+
bindings.queues?.forEach(({ binding, queue_name }) => {
|
|
118
|
+
metadataBindings.push({
|
|
119
|
+
type: "queue",
|
|
120
|
+
name: binding,
|
|
121
|
+
queue_name,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
103
125
|
bindings.r2_buckets?.forEach(({ binding, bucket_name }) => {
|
|
104
126
|
metadataBindings.push({
|
|
105
127
|
name: binding,
|
|
@@ -108,6 +130,17 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
108
130
|
});
|
|
109
131
|
});
|
|
110
132
|
|
|
133
|
+
bindings.d1_databases?.forEach(
|
|
134
|
+
({ binding, database_id, database_internal_env }) => {
|
|
135
|
+
metadataBindings.push({
|
|
136
|
+
name: binding,
|
|
137
|
+
type: "d1",
|
|
138
|
+
id: database_id,
|
|
139
|
+
internalEnv: database_internal_env,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
|
|
111
144
|
bindings.services?.forEach(({ binding, service, environment }) => {
|
|
112
145
|
metadataBindings.push({
|
|
113
146
|
name: binding,
|
|
@@ -117,14 +150,30 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
117
150
|
});
|
|
118
151
|
});
|
|
119
152
|
|
|
120
|
-
bindings.
|
|
153
|
+
bindings.analytics_engine_datasets?.forEach(({ binding, dataset }) => {
|
|
121
154
|
metadataBindings.push({
|
|
122
155
|
name: binding,
|
|
123
|
-
type: "
|
|
156
|
+
type: "analytics_engine",
|
|
157
|
+
dataset,
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
bindings.dispatch_namespaces?.forEach(({ binding, namespace }) => {
|
|
162
|
+
metadataBindings.push({
|
|
163
|
+
name: binding,
|
|
164
|
+
type: "dispatch_namespace",
|
|
124
165
|
namespace,
|
|
125
166
|
});
|
|
126
167
|
});
|
|
127
168
|
|
|
169
|
+
bindings.logfwdr?.bindings.forEach(({ name, destination }) => {
|
|
170
|
+
metadataBindings.push({
|
|
171
|
+
name: name,
|
|
172
|
+
type: "logfwdr",
|
|
173
|
+
destination,
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
128
177
|
for (const [name, filePath] of Object.entries(bindings.wasm_modules || {})) {
|
|
129
178
|
metadataBindings.push({
|
|
130
179
|
name,
|
|
@@ -240,6 +289,9 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
240
289
|
...(compatibility_flags && { compatibility_flags }),
|
|
241
290
|
...(usage_model && { usage_model }),
|
|
242
291
|
...(migrations && { migrations }),
|
|
292
|
+
capnp_schema: bindings.logfwdr?.schema,
|
|
293
|
+
...(keepVars && { keep_bindings: ["plain_text", "json"] }),
|
|
294
|
+
...(logpush !== undefined && { logpush }),
|
|
243
295
|
};
|
|
244
296
|
|
|
245
297
|
formData.set("metadata", JSON.stringify(metadata));
|
|
@@ -259,5 +311,15 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
259
311
|
);
|
|
260
312
|
}
|
|
261
313
|
|
|
314
|
+
if (bindings.logfwdr && bindings.logfwdr.schema) {
|
|
315
|
+
const filePath = bindings.logfwdr.schema;
|
|
316
|
+
formData.set(
|
|
317
|
+
filePath,
|
|
318
|
+
new File([readFileSync(filePath)], filePath, {
|
|
319
|
+
type: "application/octet-stream",
|
|
320
|
+
})
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
262
324
|
return formData;
|
|
263
325
|
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { render } from "ink";
|
|
4
|
+
import Table from "ink-table";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { fetchResult } from "../cfetch";
|
|
7
|
+
import { performApiFetch } from "../cfetch/internal";
|
|
8
|
+
import { withConfig } from "../config";
|
|
9
|
+
import { logger } from "../logger";
|
|
10
|
+
import { requireAuth } from "../user";
|
|
11
|
+
import { formatBytes, formatTimeAgo } from "./formatTimeAgo";
|
|
12
|
+
import { Name } from "./options";
|
|
13
|
+
import { d1BetaWarning, getDatabaseByNameOrBinding } from "./utils";
|
|
14
|
+
import type { Backup, Database } from "./types";
|
|
15
|
+
import type { Response } from "undici";
|
|
16
|
+
import type { Argv } from "yargs";
|
|
17
|
+
|
|
18
|
+
type BackupListArgs = { config?: string; name: string };
|
|
19
|
+
|
|
20
|
+
export function ListOptions(yargs: Argv): Argv<BackupListArgs> {
|
|
21
|
+
return Name(yargs);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ListHandler = withConfig<BackupListArgs>(
|
|
25
|
+
async ({ config, name }): Promise<void> => {
|
|
26
|
+
const accountId = await requireAuth({});
|
|
27
|
+
logger.log(d1BetaWarning);
|
|
28
|
+
const db: Database = await getDatabaseByNameOrBinding(
|
|
29
|
+
config,
|
|
30
|
+
accountId,
|
|
31
|
+
name
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const backups: Backup[] = await listBackups(accountId, db.uuid);
|
|
35
|
+
render(
|
|
36
|
+
<Table
|
|
37
|
+
data={backups}
|
|
38
|
+
columns={["created_at", "id", "num_tables", "size"]}
|
|
39
|
+
></Table>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const listBackups = async (
|
|
45
|
+
accountId: string,
|
|
46
|
+
uuid: string
|
|
47
|
+
): Promise<Array<Backup>> => {
|
|
48
|
+
const json: Backup[] = await fetchResult(
|
|
49
|
+
`/accounts/${accountId}/d1/database/${uuid}/backup`,
|
|
50
|
+
{}
|
|
51
|
+
);
|
|
52
|
+
const results: Record<string, Backup> = {};
|
|
53
|
+
|
|
54
|
+
json
|
|
55
|
+
// First, convert created_at to a Date
|
|
56
|
+
.map((backup) => ({
|
|
57
|
+
...backup,
|
|
58
|
+
created_at: new Date(backup.created_at),
|
|
59
|
+
}))
|
|
60
|
+
// Then, sort descending based on created_at
|
|
61
|
+
.sort((a, b) => +b.created_at - +a.created_at)
|
|
62
|
+
// then group_by their human-readable timestamp i.e. "2 days ago"
|
|
63
|
+
// (storing only the first of each group)
|
|
64
|
+
// and replace the Date version with this new human-readable one
|
|
65
|
+
.forEach((backup) => {
|
|
66
|
+
const timeAgo = formatTimeAgo(backup.created_at);
|
|
67
|
+
if (!results[timeAgo]) {
|
|
68
|
+
results[timeAgo] = {
|
|
69
|
+
...backup,
|
|
70
|
+
created_at: timeAgo,
|
|
71
|
+
size: formatBytes(backup.file_size),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Take advantage of JS objects' sorting to return the newest backup of a certain age
|
|
77
|
+
return Object.values(results);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type BackupCreateArgs = BackupListArgs;
|
|
81
|
+
|
|
82
|
+
export function CreateOptions(yargs: Argv): Argv<BackupCreateArgs> {
|
|
83
|
+
return ListOptions(yargs);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const CreateHandler = withConfig<BackupCreateArgs>(
|
|
87
|
+
async ({ config, name }): Promise<void> => {
|
|
88
|
+
const accountId = await requireAuth({});
|
|
89
|
+
logger.log(d1BetaWarning);
|
|
90
|
+
const db: Database = await getDatabaseByNameOrBinding(
|
|
91
|
+
config,
|
|
92
|
+
accountId,
|
|
93
|
+
name
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const backup: Backup = await createBackup(accountId, db.uuid);
|
|
97
|
+
render(
|
|
98
|
+
<Table
|
|
99
|
+
data={[backup]}
|
|
100
|
+
columns={["created_at", "id", "num_tables", "size", "state"]}
|
|
101
|
+
></Table>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
export const createBackup = async (
|
|
107
|
+
accountId: string,
|
|
108
|
+
uuid: string
|
|
109
|
+
): Promise<Backup> => {
|
|
110
|
+
const backup: Backup = await fetchResult(
|
|
111
|
+
`/accounts/${accountId}/d1/database/${uuid}/backup`,
|
|
112
|
+
{
|
|
113
|
+
method: "POST",
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
return {
|
|
117
|
+
...backup,
|
|
118
|
+
size: formatBytes(backup.file_size),
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
type BackupRestoreArgs = BackupListArgs & {
|
|
123
|
+
"backup-id": string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export function RestoreOptions(yargs: Argv): Argv<BackupRestoreArgs> {
|
|
127
|
+
return ListOptions(yargs).positional("backup-id", {
|
|
128
|
+
describe: "The Backup ID to restore",
|
|
129
|
+
type: "string",
|
|
130
|
+
demandOption: true,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const RestoreHandler = withConfig<BackupRestoreArgs>(
|
|
135
|
+
async ({ config, name, backupId }): Promise<void> => {
|
|
136
|
+
const accountId = await requireAuth({});
|
|
137
|
+
logger.log(d1BetaWarning);
|
|
138
|
+
const db: Database = await getDatabaseByNameOrBinding(
|
|
139
|
+
config,
|
|
140
|
+
accountId,
|
|
141
|
+
name
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
logger.log(`Restoring ${name} from backup ${backupId}....`);
|
|
145
|
+
await restoreBackup(accountId, db.uuid, backupId);
|
|
146
|
+
logger.log(`Done!`);
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
export const restoreBackup = async (
|
|
151
|
+
accountId: string,
|
|
152
|
+
uuid: string,
|
|
153
|
+
backupId: string
|
|
154
|
+
): Promise<void> => {
|
|
155
|
+
await fetchResult(
|
|
156
|
+
`/accounts/${accountId}/d1/database/${uuid}/backup/${backupId}/restore`,
|
|
157
|
+
{
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
type BackupDownloadArgs = BackupRestoreArgs & {
|
|
167
|
+
output?: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export function DownloadOptions(yargs: Argv): Argv<BackupDownloadArgs> {
|
|
171
|
+
return ListOptions(yargs)
|
|
172
|
+
.positional("backup-id", {
|
|
173
|
+
describe: "The Backup ID to download",
|
|
174
|
+
type: "string",
|
|
175
|
+
demandOption: true,
|
|
176
|
+
})
|
|
177
|
+
.option("output", {
|
|
178
|
+
describe:
|
|
179
|
+
"The .sqlite3 file to write to (defaults to '<db-name>.<short-backup-id>.sqlite3'",
|
|
180
|
+
type: "string",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const DownloadHandler = withConfig<BackupDownloadArgs>(
|
|
185
|
+
async ({ name, backupId, output, config }): Promise<void> => {
|
|
186
|
+
const accountId = await requireAuth({});
|
|
187
|
+
logger.log(d1BetaWarning);
|
|
188
|
+
const db: Database = await getDatabaseByNameOrBinding(
|
|
189
|
+
config,
|
|
190
|
+
accountId,
|
|
191
|
+
name
|
|
192
|
+
);
|
|
193
|
+
const filename =
|
|
194
|
+
output ||
|
|
195
|
+
path.join(
|
|
196
|
+
process.env.INIT_CWD as string,
|
|
197
|
+
`${name}.${backupId.slice(0, 8)}.sqlite3`
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
logger.log(`🌀 Downloading backup ${backupId} from '${name}'`);
|
|
201
|
+
const response = await getBackupResponse(accountId, db.uuid, backupId);
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Failed to download backup ${backupId} from '${name}' - got ${response.status} from the API`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
logger.log(`🌀 Saving to ${filename}`);
|
|
208
|
+
// TODO: stream this once we upgrade to Node18 and can use Writable.fromWeb
|
|
209
|
+
const buffer = await response.arrayBuffer();
|
|
210
|
+
await fs.writeFile(filename, new Buffer(buffer));
|
|
211
|
+
logger.log(`🌀 Done!`);
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
export const getBackupResponse = async (
|
|
216
|
+
accountId: string,
|
|
217
|
+
uuid: string,
|
|
218
|
+
backupId: string
|
|
219
|
+
): Promise<Response> => {
|
|
220
|
+
return await performApiFetch(
|
|
221
|
+
`/accounts/${accountId}/d1/database/${uuid}/backup/${backupId}/download`
|
|
222
|
+
);
|
|
223
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { render, Text, Box } from "ink";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { fetchResult } from "../cfetch";
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { requireAuth } from "../user";
|
|
6
|
+
import { d1BetaWarning } from "./utils";
|
|
7
|
+
import type { Database } from "./types";
|
|
8
|
+
import type { ArgumentsCamelCase, Argv } from "yargs";
|
|
9
|
+
|
|
10
|
+
type CreateArgs = { name: string };
|
|
11
|
+
|
|
12
|
+
export function Options(yargs: Argv): Argv<CreateArgs> {
|
|
13
|
+
return yargs
|
|
14
|
+
.positional("name", {
|
|
15
|
+
describe: "The name of the new DB",
|
|
16
|
+
type: "string",
|
|
17
|
+
demandOption: true,
|
|
18
|
+
})
|
|
19
|
+
.epilogue(d1BetaWarning);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function Handler({
|
|
23
|
+
name,
|
|
24
|
+
}: ArgumentsCamelCase<CreateArgs>): Promise<void> {
|
|
25
|
+
const accountId = await requireAuth({});
|
|
26
|
+
|
|
27
|
+
logger.log(d1BetaWarning);
|
|
28
|
+
|
|
29
|
+
let db: Database;
|
|
30
|
+
try {
|
|
31
|
+
db = await fetchResult(`/accounts/${accountId}/d1/database`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
name,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
} catch (e) {
|
|
41
|
+
if ((e as { code: number }).code === 7502) {
|
|
42
|
+
throw new Error("A database with that name already exists");
|
|
43
|
+
}
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
render(
|
|
48
|
+
<Box flexDirection="column">
|
|
49
|
+
<Text>✅ Successfully created DB '{db.name}'!</Text>
|
|
50
|
+
<Text> </Text>
|
|
51
|
+
<Text>
|
|
52
|
+
Add the following to your wrangler.toml to connect to it from a Worker:
|
|
53
|
+
</Text>
|
|
54
|
+
<Text> </Text>
|
|
55
|
+
<Text>[[ d1_databases ]]</Text>
|
|
56
|
+
<Text>
|
|
57
|
+
binding = "DB" # i.e. available in your Worker on env.DB
|
|
58
|
+
</Text>
|
|
59
|
+
<Text>database_name = "{db.name}"</Text>
|
|
60
|
+
<Text>database_id = "{db.uuid}"</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fetchResult } from "../cfetch";
|
|
2
|
+
import { withConfig } from "../config";
|
|
3
|
+
import { confirm } from "../dialogs";
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { requireAuth } from "../user";
|
|
6
|
+
import { Name } from "./options";
|
|
7
|
+
import { d1BetaWarning, getDatabaseByNameOrBinding } from "./utils";
|
|
8
|
+
import type { Database } from "./types";
|
|
9
|
+
import type { Argv } from "yargs";
|
|
10
|
+
|
|
11
|
+
type CreateArgs = {
|
|
12
|
+
config?: string;
|
|
13
|
+
name: string;
|
|
14
|
+
"skip-confirmation": boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function Options(d1ListYargs: Argv): Argv<CreateArgs> {
|
|
18
|
+
return Name(d1ListYargs)
|
|
19
|
+
.option("skip-confirmation", {
|
|
20
|
+
describe: "Skip confirmation",
|
|
21
|
+
type: "boolean",
|
|
22
|
+
alias: "y",
|
|
23
|
+
default: false,
|
|
24
|
+
})
|
|
25
|
+
.epilogue(d1BetaWarning);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Handler = withConfig<CreateArgs>(
|
|
29
|
+
async ({ name, skipConfirmation, config }): Promise<void> => {
|
|
30
|
+
const accountId = await requireAuth({});
|
|
31
|
+
logger.log(d1BetaWarning);
|
|
32
|
+
|
|
33
|
+
const db: Database = await getDatabaseByNameOrBinding(
|
|
34
|
+
config,
|
|
35
|
+
accountId,
|
|
36
|
+
name
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
logger.log(`About to delete DB '${name}' (${db.uuid}).`);
|
|
40
|
+
if (!skipConfirmation) {
|
|
41
|
+
const response = await confirm(`Ok to proceed?`);
|
|
42
|
+
if (!response) {
|
|
43
|
+
logger.log(`Not deleting.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
logger.log("Deleting...");
|
|
49
|
+
|
|
50
|
+
await fetchResult(`/accounts/${accountId}/d1/database/${db.uuid}`, {
|
|
51
|
+
method: "DELETE",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
logger.log(`Deleted '${name}' successfully.`);
|
|
55
|
+
}
|
|
56
|
+
);
|