wrangler 0.0.0-e88a54e → 0.0.0-e8e684c7

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 (222) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +51 -14
  3. package/bin/wrangler.js +161 -29
  4. package/kv-asset-handler.js +1 -0
  5. package/miniflare-dist/index.mjs +5484 -21
  6. package/package.json +173 -143
  7. package/src/__tests__/config-cache-without-cache-dir.test.ts +38 -0
  8. package/src/__tests__/config-cache.test.ts +42 -0
  9. package/src/__tests__/configuration.test.ts +4016 -2786
  10. package/src/__tests__/dev.test.tsx +1305 -483
  11. package/src/__tests__/generate.test.ts +91 -0
  12. package/src/__tests__/guess-worker-format.test.ts +75 -48
  13. package/src/__tests__/helpers/cmd-shim.d.ts +6 -6
  14. package/src/__tests__/helpers/mock-account-id.ts +24 -24
  15. package/src/__tests__/helpers/mock-bin.ts +20 -20
  16. package/src/__tests__/helpers/mock-cfetch.ts +182 -93
  17. package/src/__tests__/helpers/mock-console.ts +59 -32
  18. package/src/__tests__/helpers/mock-dialogs.ts +121 -33
  19. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  20. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  21. package/src/__tests__/helpers/mock-istty.ts +74 -0
  22. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  23. package/src/__tests__/helpers/mock-kv.ts +26 -26
  24. package/src/__tests__/helpers/mock-oauth-flow.ts +250 -0
  25. package/src/__tests__/helpers/mock-process.ts +39 -0
  26. package/src/__tests__/helpers/mock-stdin.ts +82 -77
  27. package/src/__tests__/helpers/mock-web-socket.ts +21 -21
  28. package/src/__tests__/helpers/run-in-tmp.ts +28 -22
  29. package/src/__tests__/helpers/run-wrangler.ts +8 -8
  30. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  31. package/src/__tests__/helpers/write-wrangler-toml.ts +9 -9
  32. package/src/__tests__/https-options.test.ts +144 -108
  33. package/src/__tests__/index.test.ts +229 -975
  34. package/src/__tests__/init.test.ts +2170 -0
  35. package/src/__tests__/jest.setup.ts +122 -34
  36. package/src/__tests__/kv.test.ts +1376 -1087
  37. package/src/__tests__/logger.test.ts +141 -0
  38. package/src/__tests__/metrics.test.ts +450 -0
  39. package/src/__tests__/package-manager.test.ts +312 -165
  40. package/src/__tests__/pages.test.ts +1270 -39
  41. package/src/__tests__/parse.test.ts +253 -245
  42. package/src/__tests__/publish.test.ts +6878 -2627
  43. package/src/__tests__/pubsub.test.ts +367 -0
  44. package/src/__tests__/r2.test.ts +328 -190
  45. package/src/__tests__/route.test.ts +22 -22
  46. package/src/__tests__/secret.test.ts +474 -407
  47. package/src/__tests__/tail.test.ts +679 -441
  48. package/src/__tests__/test-old-node-version.js +31 -0
  49. package/src/__tests__/user.test.ts +155 -0
  50. package/src/__tests__/validate-dev-props.test.ts +56 -0
  51. package/src/__tests__/version.test.ts +35 -0
  52. package/src/__tests__/whoami.test.tsx +276 -87
  53. package/src/__tests__/worker-namespace.test.ts +329 -0
  54. package/src/abort.d.ts +3 -0
  55. package/src/api/dev.ts +93 -0
  56. package/src/api/index.ts +1 -0
  57. package/src/bundle-reporter.tsx +29 -0
  58. package/src/bundle.ts +441 -110
  59. package/src/cfetch/index.ts +83 -66
  60. package/src/cfetch/internal.ts +251 -92
  61. package/src/cli.ts +22 -14
  62. package/src/config/README.md +1 -1
  63. package/src/config/config.ts +224 -179
  64. package/src/config/diagnostics.ts +61 -61
  65. package/src/config/environment.ts +430 -311
  66. package/src/config/index.ts +227 -38
  67. package/src/config/validation-helpers.ts +414 -303
  68. package/src/config/validation.ts +1745 -973
  69. package/src/config-cache.ts +85 -0
  70. package/src/create-worker-preview.ts +209 -118
  71. package/src/create-worker-upload-form.ts +275 -201
  72. package/src/dev/dev-vars.ts +36 -0
  73. package/src/dev/dev.tsx +488 -307
  74. package/src/dev/local.tsx +427 -210
  75. package/src/dev/remote.tsx +407 -188
  76. package/src/dev/use-esbuild.ts +156 -78
  77. package/src/dev/validate-dev-props.ts +31 -0
  78. package/src/dev-registry.tsx +160 -0
  79. package/src/dev.tsx +696 -0
  80. package/src/dialogs.tsx +115 -57
  81. package/src/dispatch-namespace.ts +206 -0
  82. package/src/durable.ts +102 -0
  83. package/src/entry.ts +267 -147
  84. package/src/environment-variables.ts +25 -23
  85. package/src/errors.ts +11 -0
  86. package/src/faye-websocket.d.ts +6 -0
  87. package/src/generate.ts +131 -0
  88. package/src/git-client.ts +42 -0
  89. package/src/global-wrangler-config-path.ts +26 -0
  90. package/src/https-options.ts +85 -80
  91. package/src/index.tsx +2191 -2304
  92. package/src/init.ts +622 -0
  93. package/src/inspect.ts +687 -557
  94. package/src/intl-polyfill.d.ts +139 -0
  95. package/src/is-ci.ts +14 -0
  96. package/src/is-interactive.ts +12 -0
  97. package/src/kv.ts +353 -242
  98. package/src/logger.ts +73 -0
  99. package/src/metrics/index.ts +5 -0
  100. package/src/metrics/metrics-config.ts +239 -0
  101. package/src/metrics/metrics-dispatcher.ts +96 -0
  102. package/src/metrics/metrics-usage-headers.ts +24 -0
  103. package/src/metrics/send-event.ts +92 -0
  104. package/src/miniflare-cli/assets.ts +554 -0
  105. package/src/miniflare-cli/enum-keys.ts +8 -8
  106. package/src/miniflare-cli/index.ts +199 -29
  107. package/src/miniflare-cli/request-context.ts +40 -0
  108. package/src/module-collection.ts +213 -213
  109. package/src/open-in-browser.ts +8 -16
  110. package/src/package-manager.ts +183 -90
  111. package/src/pages/build.tsx +238 -0
  112. package/src/pages/constants.ts +12 -0
  113. package/src/pages/deployments.tsx +106 -0
  114. package/src/pages/dev.tsx +528 -0
  115. package/src/pages/errors.ts +22 -0
  116. package/src/pages/functions/buildPlugin.ts +109 -0
  117. package/src/pages/functions/buildWorker.ts +155 -0
  118. package/src/pages/functions/filepath-routing.test.ts +234 -0
  119. package/src/pages/functions/filepath-routing.ts +189 -0
  120. package/src/pages/functions/identifiers.ts +78 -0
  121. package/src/pages/functions/routes-consolidation.test.ts +250 -0
  122. package/src/pages/functions/routes-consolidation.ts +73 -0
  123. package/src/pages/functions/routes-transformation.test.ts +271 -0
  124. package/src/pages/functions/routes-transformation.ts +122 -0
  125. package/src/pages/functions/routes.ts +151 -0
  126. package/src/pages/functions.tsx +96 -0
  127. package/src/pages/index.tsx +94 -0
  128. package/src/pages/projects.tsx +169 -0
  129. package/src/pages/publish.tsx +391 -0
  130. package/src/pages/types.ts +49 -0
  131. package/src/pages/upload.tsx +413 -0
  132. package/src/pages/utils.ts +12 -0
  133. package/src/parse.ts +224 -121
  134. package/src/paths.ts +6 -6
  135. package/src/preview.ts +31 -0
  136. package/src/proxy.ts +416 -370
  137. package/src/publish.ts +796 -321
  138. package/src/pubsub/index.ts +286 -0
  139. package/src/pubsub/pubsub-commands.tsx +623 -0
  140. package/src/r2.ts +100 -19
  141. package/src/selfsigned.d.ts +23 -23
  142. package/src/sites.tsx +281 -189
  143. package/src/tail/filters.ts +111 -109
  144. package/src/tail/index.ts +230 -217
  145. package/src/tail/printing.ts +92 -47
  146. package/src/update-check.ts +19 -0
  147. package/src/user/choose-account.tsx +69 -0
  148. package/src/user/env-vars.ts +46 -0
  149. package/src/user/generate-auth-url.ts +33 -0
  150. package/src/user/generate-random-state.ts +16 -0
  151. package/src/user/index.ts +3 -0
  152. package/src/user/user.tsx +1191 -0
  153. package/src/whoami.tsx +142 -42
  154. package/src/worker.ts +135 -94
  155. package/src/zones.ts +167 -0
  156. package/templates/checked-fetch.js +17 -0
  157. package/templates/first-party-worker-module-facade.ts +18 -0
  158. package/templates/format-dev-errors.ts +32 -0
  159. package/templates/gitignore +171 -0
  160. package/templates/new-worker-scheduled.js +17 -0
  161. package/templates/new-worker-scheduled.ts +32 -0
  162. package/templates/new-worker.js +3 -3
  163. package/templates/new-worker.ts +18 -3
  164. package/templates/no-op-worker.js +10 -0
  165. package/templates/pages-shim.ts +9 -0
  166. package/templates/pages-template-plugin.ts +167 -0
  167. package/templates/pages-template-worker.ts +172 -0
  168. package/templates/serve-static-assets.ts +63 -0
  169. package/templates/service-bindings-module-facade.js +54 -0
  170. package/templates/service-bindings-sw-facade.js +42 -0
  171. package/templates/tsconfig.json +95 -95
  172. package/wrangler-dist/cli.d.ts +338 -0
  173. package/wrangler-dist/cli.js +124611 -112403
  174. package/miniflare-dist/index.mjs.map +0 -7
  175. package/pages/functions/buildWorker.ts +0 -62
  176. package/pages/functions/filepath-routing.test.ts +0 -223
  177. package/pages/functions/filepath-routing.ts +0 -181
  178. package/pages/functions/identifiers.ts +0 -78
  179. package/pages/functions/routes.ts +0 -151
  180. package/pages/functions/template-worker.ts +0 -130
  181. package/src/__tests__/dev2.test.tsx +0 -154
  182. package/src/__tests__/helpers/faye-websocket.d.ts +0 -6
  183. package/src/__tests__/logout.test.ts +0 -52
  184. package/src/__tests__/sentry.test.ts +0 -146
  185. package/src/pages.tsx +0 -1047
  186. package/src/reporting.ts +0 -131
  187. package/src/user.tsx +0 -1118
  188. package/templates/static-asset-facade.js +0 -47
  189. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  190. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  191. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  192. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  193. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  194. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  195. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  196. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  197. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  198. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  199. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  200. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  201. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  202. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  203. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  204. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  205. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  206. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  207. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  208. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  209. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  210. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  211. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  212. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  213. package/vendor/wrangler-mime/LICENSE +0 -21
  214. package/vendor/wrangler-mime/Mime.js +0 -97
  215. package/vendor/wrangler-mime/README.md +0 -187
  216. package/vendor/wrangler-mime/cli.js +0 -46
  217. package/vendor/wrangler-mime/index.js +0 -4
  218. package/vendor/wrangler-mime/lite.js +0 -4
  219. package/vendor/wrangler-mime/package.json +0 -52
  220. package/vendor/wrangler-mime/types/other.js +0 -1
  221. package/vendor/wrangler-mime/types/standard.js +0 -1
  222. package/wrangler-dist/cli.js.map +0 -7
package/src/kv.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { URLSearchParams } from "node:url";
2
+ import { FormData } from "undici";
2
3
  import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
4
+ import { logger } from "./logger";
3
5
  import type { Config } from "./config";
4
6
 
5
7
  /** The largest number of kv items we can pass to the API in a single request. */
@@ -9,9 +11,9 @@ const API_MAX = 10000;
9
11
  const BATCH_KEY_MAX = API_MAX / 2;
10
12
 
11
13
  type KvArgs = {
12
- binding?: string;
13
- "namespace-id"?: string;
14
- preview?: boolean;
14
+ binding?: string;
15
+ "namespace-id"?: string;
16
+ preview?: boolean;
15
17
  };
16
18
 
17
19
  /**
@@ -19,301 +21,410 @@ type KvArgs = {
19
21
  *
20
22
  * @returns the generated id of the created namespace.
21
23
  */
22
- export async function createNamespace(
23
- accountId: string,
24
- title: string
24
+ export async function createKVNamespace(
25
+ accountId: string,
26
+ title: string
25
27
  ): Promise<string> {
26
- const response = await fetchResult<{ id: string }>(
27
- `/accounts/${accountId}/storage/kv/namespaces`,
28
- {
29
- method: "POST",
30
- headers: {
31
- "Content-Type": "application/json",
32
- },
33
- body: JSON.stringify({
34
- title,
35
- }),
36
- }
37
- );
38
-
39
- return response.id;
28
+ const response = await fetchResult<{ id: string }>(
29
+ `/accounts/${accountId}/storage/kv/namespaces`,
30
+ {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ },
35
+ body: JSON.stringify({
36
+ title,
37
+ }),
38
+ }
39
+ );
40
+
41
+ return response.id;
40
42
  }
41
43
 
42
44
  /**
43
45
  * The information about a namespace that is returned from `listNamespaces()`.
44
46
  */
45
47
  export interface KVNamespaceInfo {
46
- id: string;
47
- title: string;
48
- supports_url_encoding?: boolean;
48
+ id: string;
49
+ title: string;
50
+ supports_url_encoding?: boolean;
49
51
  }
50
52
 
51
53
  /**
52
54
  * Fetch a list of all the namespaces under the given `accountId`.
53
55
  */
54
- export async function listNamespaces(
55
- accountId: string
56
+ export async function listKVNamespaces(
57
+ accountId: string
56
58
  ): Promise<KVNamespaceInfo[]> {
57
- const pageSize = 100;
58
- let page = 1;
59
- const results: KVNamespaceInfo[] = [];
60
- while (results.length % pageSize === 0) {
61
- const json = await fetchResult<KVNamespaceInfo[]>(
62
- `/accounts/${accountId}/storage/kv/namespaces`,
63
- {},
64
- new URLSearchParams({
65
- per_page: pageSize.toString(),
66
- order: "title",
67
- direction: "asc",
68
- page: page.toString(),
69
- })
70
- );
71
- page++;
72
- results.push(...json);
73
- if (json.length < pageSize) {
74
- break;
75
- }
76
- }
77
- return results;
59
+ const pageSize = 100;
60
+ let page = 1;
61
+ const results: KVNamespaceInfo[] = [];
62
+ while (results.length % pageSize === 0) {
63
+ const json = await fetchResult<KVNamespaceInfo[]>(
64
+ `/accounts/${accountId}/storage/kv/namespaces`,
65
+ {},
66
+ new URLSearchParams({
67
+ per_page: pageSize.toString(),
68
+ order: "title",
69
+ direction: "asc",
70
+ page: page.toString(),
71
+ })
72
+ );
73
+ page++;
74
+ results.push(...json);
75
+ if (json.length < pageSize) {
76
+ break;
77
+ }
78
+ }
79
+ return results;
78
80
  }
79
81
 
80
82
  export interface NamespaceKeyInfo {
81
- name: string;
82
- expiration?: number;
83
- metadata?: { [key: string]: unknown };
83
+ name: string;
84
+ expiration?: number;
85
+ metadata?: { [key: string]: unknown };
84
86
  }
85
87
 
86
- export async function listNamespaceKeys(
87
- accountId: string,
88
- namespaceId: string,
89
- prefix = ""
88
+ export async function listKVNamespaceKeys(
89
+ accountId: string,
90
+ namespaceId: string,
91
+ prefix = ""
90
92
  ) {
91
- return await fetchListResult<NamespaceKeyInfo>(
92
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/keys`,
93
- {},
94
- new URLSearchParams({ prefix })
95
- );
93
+ return await fetchListResult<NamespaceKeyInfo>(
94
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/keys`,
95
+ {},
96
+ new URLSearchParams({ prefix })
97
+ );
98
+ }
99
+
100
+ export async function deleteKVNamespace(
101
+ accountId: string,
102
+ namespaceId: string
103
+ ) {
104
+ return await fetchResult<{ id: string }>(
105
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}`,
106
+ { method: "DELETE" }
107
+ );
96
108
  }
97
109
 
98
110
  /**
99
111
  * Information about a key-value pair, including its "metadata" fields.
100
112
  */
101
113
  export interface KeyValue {
102
- key: string;
103
- value: string;
104
- expiration?: number;
105
- expiration_ttl?: number;
106
- metadata?: object;
107
- base64?: boolean;
114
+ key: string;
115
+ value: string | Buffer;
116
+ expiration?: number;
117
+ expiration_ttl?: number;
118
+ metadata?: object;
119
+ base64?: boolean;
108
120
  }
109
121
 
110
122
  const KeyValueKeys = new Set([
111
- "key",
112
- "value",
113
- "expiration",
114
- "expiration_ttl",
115
- "metadata",
116
- "base64",
123
+ "key",
124
+ "value",
125
+ "expiration",
126
+ "expiration_ttl",
127
+ "metadata",
128
+ "base64",
117
129
  ]);
118
130
 
131
+ /**
132
+ * The object has the specified property.
133
+ */
134
+ function hasProperty<T extends object>(
135
+ obj: object,
136
+ property: keyof T
137
+ ): obj is T {
138
+ return property in obj;
139
+ }
140
+
141
+ /**
142
+ * The object has a required property of the specified type.
143
+ */
144
+ function hasTypedProperty<T extends object>(
145
+ obj: object,
146
+ property: keyof T,
147
+ type: string
148
+ ): obj is T {
149
+ return hasProperty(obj, property) && typeof obj[property] === type;
150
+ }
151
+
152
+ /**
153
+ * The object an optional property, of the specified type.
154
+ */
155
+ function hasOptionalTypedProperty<T extends object>(
156
+ obj: object,
157
+ property: keyof T,
158
+ type: string
159
+ ): obj is Omit<T, typeof property> | T {
160
+ return !hasProperty(obj, property) || typeof obj[property] === type;
161
+ }
162
+
119
163
  /**
120
164
  * Is the given object a valid `KeyValue` type?
121
165
  */
122
- export function isKeyValue(keyValue: object): keyValue is KeyValue {
123
- const props = Object.keys(keyValue);
124
- if (!props.includes("key") || !props.includes("value")) {
125
- return false;
126
- }
127
- return true;
166
+ export function isKVKeyValue(keyValue: unknown): keyValue is KeyValue {
167
+ if (
168
+ keyValue === null ||
169
+ typeof keyValue !== "object" ||
170
+ !hasTypedProperty(keyValue, "key", "string") ||
171
+ !hasTypedProperty(keyValue, "value", "string") ||
172
+ !hasOptionalTypedProperty(keyValue, "expiration", "number") ||
173
+ !hasOptionalTypedProperty(keyValue, "expiration_ttl", "number") ||
174
+ !hasOptionalTypedProperty(keyValue, "base64", "boolean") ||
175
+ !hasOptionalTypedProperty(keyValue, "metadata", "object")
176
+ ) {
177
+ return false;
178
+ }
179
+ return true;
128
180
  }
129
181
 
130
182
  /**
131
183
  * Get all the properties on the `keyValue` that are not expected.
132
184
  */
133
- export function unexpectedKeyValueProps(keyValue: KeyValue): string[] {
134
- const props = Object.keys(keyValue);
135
- return props.filter((prop) => !KeyValueKeys.has(prop));
185
+ export function unexpectedKVKeyValueProps(keyValue: KeyValue): string[] {
186
+ const props = Object.keys(keyValue);
187
+ return props.filter((prop) => !KeyValueKeys.has(prop));
136
188
  }
137
189
 
138
- export async function putKeyValue(
139
- accountId: string,
140
- namespaceId: string,
141
- keyValue: KeyValue
190
+ /**
191
+ * Turn object with fields into FormData
192
+ */
193
+ function asFormData(fields: Record<string, unknown>): FormData {
194
+ const formData = new FormData();
195
+
196
+ for (const [name, value] of Object.entries(fields)) {
197
+ formData.append(name, value);
198
+ }
199
+
200
+ return formData;
201
+ }
202
+
203
+ export async function putKVKeyValue(
204
+ accountId: string,
205
+ namespaceId: string,
206
+ keyValue: KeyValue
142
207
  ) {
143
- let searchParams: URLSearchParams | undefined;
144
- if (keyValue.expiration || keyValue.expiration_ttl) {
145
- searchParams = new URLSearchParams();
146
- if (keyValue.expiration) {
147
- searchParams.set("expiration", `${keyValue.expiration}`);
148
- }
149
- if (keyValue.expiration_ttl) {
150
- searchParams.set("expiration_ttl", `${keyValue.expiration_ttl}`);
151
- }
152
- }
153
- return await fetchResult(
154
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${keyValue.key}`,
155
- { method: "PUT", body: keyValue.value },
156
- searchParams
157
- );
208
+ let searchParams: URLSearchParams | undefined;
209
+ if (keyValue.expiration || keyValue.expiration_ttl) {
210
+ searchParams = new URLSearchParams();
211
+ if (keyValue.expiration) {
212
+ searchParams.set("expiration", `${keyValue.expiration}`);
213
+ }
214
+ if (keyValue.expiration_ttl) {
215
+ searchParams.set("expiration_ttl", `${keyValue.expiration_ttl}`);
216
+ }
217
+ }
218
+ return await fetchResult(
219
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
220
+ keyValue.key
221
+ )}`,
222
+ {
223
+ method: "PUT",
224
+ body: keyValue.metadata
225
+ ? asFormData({
226
+ value: keyValue.value,
227
+ metadata: JSON.stringify(keyValue.metadata),
228
+ })
229
+ : keyValue.value,
230
+ },
231
+ searchParams
232
+ );
158
233
  }
159
234
 
160
- export async function getKeyValue(
161
- accountId: string,
162
- namespaceId: string,
163
- key: string
164
- ): Promise<string> {
165
- return await fetchKVGetValue(accountId, namespaceId, key);
235
+ export async function getKVKeyValue(
236
+ accountId: string,
237
+ namespaceId: string,
238
+ key: string
239
+ ): Promise<ArrayBuffer> {
240
+ return await fetchKVGetValue(accountId, namespaceId, encodeURIComponent(key));
241
+ }
242
+
243
+ export async function deleteKVKeyValue(
244
+ accountId: string,
245
+ namespaceId: string,
246
+ key: string
247
+ ) {
248
+ return await fetchResult(
249
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
250
+ key
251
+ )}`,
252
+ { method: "DELETE" }
253
+ );
254
+ }
255
+
256
+ /**
257
+ * Formatter for converting e.g. 5328 --> 5,328
258
+ */
259
+ const formatNumber = new Intl.NumberFormat("en-US", {
260
+ notation: "standard",
261
+ }).format;
262
+
263
+ /**
264
+ * Helper function for bulk requests, logs ongoing output to console.
265
+ */
266
+ function logBulkProgress(
267
+ operation: "put" | "delete",
268
+ index: number,
269
+ total: number
270
+ ) {
271
+ logger.log(
272
+ `${operation === "put" ? "Uploaded" : "Deleted"} ${Math.floor(
273
+ (100 * index) / total
274
+ )}% (${formatNumber(index)} out of ${formatNumber(total)})`
275
+ );
166
276
  }
167
277
 
168
- export async function putBulkKeyValue(
169
- accountId: string,
170
- namespaceId: string,
171
- keyValues: KeyValue[],
172
- progressCallback: (index: number, total: number) => void
278
+ export async function putKVBulkKeyValue(
279
+ accountId: string,
280
+ namespaceId: string,
281
+ keyValues: KeyValue[],
282
+ quiet = false
173
283
  ) {
174
- for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
175
- if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
176
- progressCallback(index, keyValues.length);
177
- }
178
-
179
- await fetchResult(
180
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
181
- {
182
- method: "PUT",
183
- body: JSON.stringify(keyValues.slice(index, index + BATCH_KEY_MAX)),
184
- headers: { "Content-Type": "application/json" },
185
- }
186
- );
187
- }
188
- if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
189
- progressCallback(keyValues.length, keyValues.length);
190
- }
284
+ for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
285
+ if (!quiet && keyValues.length > BATCH_KEY_MAX) {
286
+ logBulkProgress("put", index, keyValues.length);
287
+ }
288
+
289
+ await fetchResult(
290
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
291
+ {
292
+ method: "PUT",
293
+ body: JSON.stringify(keyValues.slice(index, index + BATCH_KEY_MAX)),
294
+ headers: { "Content-Type": "application/json" },
295
+ }
296
+ );
297
+ }
298
+
299
+ if (!quiet && keyValues.length > BATCH_KEY_MAX) {
300
+ logBulkProgress("put", keyValues.length, keyValues.length);
301
+ }
191
302
  }
192
303
 
193
- export async function deleteBulkKeyValue(
194
- accountId: string,
195
- namespaceId: string,
196
- keys: string[],
197
- progressCallback: (index: number, total: number) => void
304
+ export async function deleteKVBulkKeyValue(
305
+ accountId: string,
306
+ namespaceId: string,
307
+ keys: string[],
308
+ quiet = false
198
309
  ) {
199
- for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
200
- if (progressCallback && keys.length > BATCH_KEY_MAX) {
201
- progressCallback(index, keys.length);
202
- }
203
-
204
- await fetchResult(
205
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
206
- {
207
- method: "DELETE",
208
- body: JSON.stringify(keys.slice(index, index + BATCH_KEY_MAX)),
209
- headers: { "Content-Type": "application/json" },
210
- }
211
- );
212
- }
213
- if (progressCallback && keys.length > BATCH_KEY_MAX) {
214
- progressCallback(keys.length, keys.length);
215
- }
310
+ for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
311
+ if (!quiet && keys.length > BATCH_KEY_MAX) {
312
+ logBulkProgress("delete", index, keys.length);
313
+ }
314
+
315
+ await fetchResult(
316
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
317
+ {
318
+ method: "DELETE",
319
+ body: JSON.stringify(keys.slice(index, index + BATCH_KEY_MAX)),
320
+ headers: { "Content-Type": "application/json" },
321
+ }
322
+ );
323
+ }
324
+ if (!quiet && keys.length > BATCH_KEY_MAX) {
325
+ logBulkProgress("delete", keys.length, keys.length);
326
+ }
216
327
  }
217
328
 
218
- export function getNamespaceId(
219
- { preview, binding, "namespace-id": namespaceId }: KvArgs,
220
- config: Config
329
+ export function getKVNamespaceId(
330
+ { preview, binding, "namespace-id": namespaceId }: KvArgs,
331
+ config: Config
221
332
  ): string {
222
- // nice
223
- if (namespaceId) {
224
- return namespaceId;
225
- }
226
-
227
- // begin pre-flight checks
228
-
229
- // `--binding` is only valid if there's a wrangler configuration.
230
- if (binding && !config) {
231
- throw new Error("--binding specified, but no config file was found.");
232
- }
233
-
234
- // there's no config. abort here
235
- if (!config) {
236
- throw new Error(
237
- "Failed to find a config file.\n" +
238
- "Either use --namespace-id to upload directly or create a configuration file with a binding."
239
- );
240
- }
241
-
242
- // there's no KV namespaces
243
- if (!config.kv_namespaces || config.kv_namespaces.length === 0) {
244
- throw new Error(
245
- "No KV Namespaces configured! Either use --namespace-id to upload directly or add a KV namespace to your wrangler config file."
246
- );
247
- }
248
-
249
- const namespace = config.kv_namespaces.find((ns) => ns.binding === binding);
250
-
251
- // we couldn't find a namespace with that binding
252
- if (!namespace) {
253
- throw new Error(
254
- `A namespace with binding name "${binding}" was not found in the configured "kv_namespaces".`
255
- );
256
- }
257
-
258
- // end pre-flight checks
259
-
260
- // we're in preview mode, `--preview true` or `--preview` was passed
261
- if (preview && namespace.preview_id) {
262
- namespaceId = namespace.preview_id;
263
- // We don't want to execute code below if preview is set to true, so we just return. Otherwise we will get errors!
264
- return namespaceId;
265
- } else if (preview) {
266
- throw new Error(
267
- `No preview ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
268
- );
269
- }
270
-
271
- // either `--preview false`, or preview wasn't passed
272
- // TODO: should we care? or should we just treat false and undefined the same
273
- const previewIsDefined = typeof preview !== "undefined";
274
-
275
- // --preview false was passed
276
- if (previewIsDefined && namespace.id) {
277
- namespaceId = namespace.id;
278
- // We don't want to execute code below if preview is set to true, so we just return. Otherwise we can get error!
279
- return namespaceId;
280
- } else if (previewIsDefined) {
281
- throw new Error(
282
- `No namespace ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
283
- );
284
- }
285
-
286
- // `--preview` wasn't passed
287
- const bindingHasOnlyOneId =
288
- (namespace.id && !namespace.preview_id) ||
289
- (!namespace.id && namespace.preview_id);
290
- if (bindingHasOnlyOneId) {
291
- namespaceId = namespace.id || namespace.preview_id;
292
- } else {
293
- throw new Error(
294
- `${binding} has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.`
295
- );
296
- }
297
-
298
- // shouldn't happen. we should be able to prove this with strong typing.
299
- // TODO: when we add strongly typed commands, rewrite these checks so they're exhaustive
300
- if (!namespaceId) {
301
- throw Error(
302
- "Something went wrong trying to determine which namespace to upload to.\n" +
303
- "Please create a github issue with the command you just ran along with your wrangler configuration."
304
- );
305
- }
306
-
307
- return namespaceId;
333
+ // nice
334
+ if (namespaceId) {
335
+ return namespaceId;
336
+ }
337
+
338
+ // begin pre-flight checks
339
+
340
+ // `--binding` is only valid if there's a wrangler configuration.
341
+ if (binding && !config) {
342
+ throw new Error("--binding specified, but no config file was found.");
343
+ }
344
+
345
+ // there's no config. abort here
346
+ if (!config) {
347
+ throw new Error(
348
+ "Failed to find a config file.\n" +
349
+ "Either use --namespace-id to upload directly or create a configuration file with a binding."
350
+ );
351
+ }
352
+
353
+ // there's no KV namespaces
354
+ if (!config.kv_namespaces || config.kv_namespaces.length === 0) {
355
+ throw new Error(
356
+ "No KV Namespaces configured! Either use --namespace-id to upload directly or add a KV namespace to your wrangler config file."
357
+ );
358
+ }
359
+
360
+ const namespace = config.kv_namespaces.find((ns) => ns.binding === binding);
361
+
362
+ // we couldn't find a namespace with that binding
363
+ if (!namespace) {
364
+ throw new Error(
365
+ `A namespace with binding name "${binding}" was not found in the configured "kv_namespaces".`
366
+ );
367
+ }
368
+
369
+ // end pre-flight checks
370
+
371
+ // we're in preview mode, `--preview true` or `--preview` was passed
372
+ if (preview && namespace.preview_id) {
373
+ namespaceId = namespace.preview_id;
374
+ // We don't want to execute code below if preview is set to true, so we just return. Otherwise we will get errors!
375
+ return namespaceId;
376
+ } else if (preview) {
377
+ throw new Error(
378
+ `No preview ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
379
+ );
380
+ }
381
+
382
+ // either `--preview false`, or preview wasn't passed
383
+ // TODO: should we care? or should we just treat false and undefined the same
384
+ const previewIsDefined = typeof preview !== "undefined";
385
+
386
+ // --preview false was passed
387
+ if (previewIsDefined && namespace.id) {
388
+ namespaceId = namespace.id;
389
+ // We don't want to execute code below if preview is set to true, so we just return. Otherwise we can get error!
390
+ return namespaceId;
391
+ } else if (previewIsDefined) {
392
+ throw new Error(
393
+ `No namespace ID found for ${binding}. Add one to your wrangler config file to use a separate namespace for previewing your worker.`
394
+ );
395
+ }
396
+
397
+ // `--preview` wasn't passed
398
+ const bindingHasOnlyOneId =
399
+ (namespace.id && !namespace.preview_id) ||
400
+ (!namespace.id && namespace.preview_id);
401
+ if (bindingHasOnlyOneId) {
402
+ namespaceId = namespace.id || namespace.preview_id;
403
+ } else {
404
+ throw new Error(
405
+ `${binding} has both a namespace ID and a preview ID. Specify "--preview" or "--preview false" to avoid writing data to the wrong namespace.`
406
+ );
407
+ }
408
+
409
+ // shouldn't happen. we should be able to prove this with strong typing.
410
+ // TODO: when we add strongly typed commands, rewrite these checks so they're exhaustive
411
+ if (!namespaceId) {
412
+ throw Error(
413
+ "Something went wrong trying to determine which namespace to upload to.\n" +
414
+ "Please create a github issue with the command you just ran along with your wrangler configuration."
415
+ );
416
+ }
417
+
418
+ return namespaceId;
308
419
  }
309
420
 
310
421
  /**
311
422
  * KV namespace binding names must be valid JS identifiers.
312
423
  */
313
- export function isValidNamespaceBinding(
314
- binding: string | undefined
424
+ export function isValidKVNamespaceBinding(
425
+ binding: string | undefined
315
426
  ): binding is string {
316
- return (
317
- typeof binding === "string" && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(binding)
318
- );
427
+ return (
428
+ typeof binding === "string" && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(binding)
429
+ );
319
430
  }