@hot-updater/cloudflare 0.35.7 → 0.35.9

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.
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import crypto from "crypto";
3
3
  import fs from "fs/promises";
4
4
  import path from "path";
5
- import { ConfigBuilder, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getCwd, link, makeEnv, p, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
5
+ import { ConfigBuilder, InitError, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, readHotUpdaterInitEnv, resolveInitProviderInput, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
6
6
  import { Cloudflare } from "cloudflare";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
@@ -20,6 +20,7 @@ import { appendFileSync, createReadStream, createWriteStream, readFileSync, stat
20
20
  import { finished } from "node:stream/promises";
21
21
  import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
22
22
  import { Buffer as Buffer$1 } from "node:buffer";
23
+ import { APIError } from "cloudflare/error";
23
24
  import os from "os";
24
25
  //#region \0rolldown/runtime.js
25
26
  var __create = Object.create;
@@ -46,302 +47,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
46
47
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
47
48
  //#endregion
48
49
  //#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
49
- var import_dayjs_min = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
50
- (function(t, e) {
51
- "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
52
- })(exports, (function() {
53
- "use strict";
54
- var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = {
55
- name: "en",
56
- weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
57
- months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
58
- ordinal: function(t) {
59
- var e = [
60
- "th",
61
- "st",
62
- "nd",
63
- "rd"
64
- ], n = t % 100;
65
- return "[" + t + (e[(n - 20) % 10] || e[n] || e[0]) + "]";
66
- }
67
- }, m = function(t, e, n) {
68
- var r = String(t);
69
- return !r || r.length >= e ? t : "" + Array(e + 1 - r.length).join(n) + t;
70
- }, v = {
71
- s: m,
72
- z: function(t) {
73
- var e = -t.utcOffset(), n = Math.abs(e), r = Math.floor(n / 60), i = n % 60;
74
- return (e <= 0 ? "+" : "-") + m(r, 2, "0") + ":" + m(i, 2, "0");
75
- },
76
- m: function t(e, n) {
77
- if (e.date() < n.date()) return -t(n, e);
78
- var r = 12 * (n.year() - e.year()) + (n.month() - e.month()), i = e.clone().add(r, c), s = n - i < 0, u = e.clone().add(r + (s ? -1 : 1), c);
79
- return +(-(r + (n - i) / (s ? i - u : u - i)) || 0);
80
- },
81
- a: function(t) {
82
- return t < 0 ? Math.ceil(t) || 0 : Math.floor(t);
83
- },
84
- p: function(t) {
85
- return {
86
- M: c,
87
- y: h,
88
- w: o,
89
- d: a,
90
- D: d,
91
- h: u,
92
- m: s,
93
- s: i,
94
- ms: r,
95
- Q: f
96
- }[t] || String(t || "").toLowerCase().replace(/s$/, "");
97
- },
98
- u: function(t) {
99
- return void 0 === t;
100
- }
101
- }, g = "en", D = {};
102
- D[g] = M;
103
- var p = "$isDayjsObject", S = function(t) {
104
- return t instanceof _ || !(!t || !t[p]);
105
- }, w = function t(e, n, r) {
106
- var i;
107
- if (!e) return g;
108
- if ("string" == typeof e) {
109
- var s = e.toLowerCase();
110
- D[s] && (i = s), n && (D[s] = n, i = s);
111
- var u = e.split("-");
112
- if (!i && u.length > 1) return t(u[0]);
113
- } else {
114
- var a = e.name;
115
- D[a] = e, i = a;
116
- }
117
- return !r && i && (g = i), i || !r && g;
118
- }, O = function(t, e) {
119
- if (S(t)) return t.clone();
120
- var n = "object" == typeof e ? e : {};
121
- return n.date = t, n.args = arguments, new _(n);
122
- }, b = v;
123
- b.l = w, b.i = S, b.w = function(t, e) {
124
- return O(t, {
125
- locale: e.$L,
126
- utc: e.$u,
127
- x: e.$x,
128
- $offset: e.$offset
129
- });
130
- };
131
- var _ = function() {
132
- function M(t) {
133
- this.$L = w(t.locale, null, !0), this.parse(t), this.$x = this.$x || t.x || {}, this[p] = !0;
134
- }
135
- var m = M.prototype;
136
- return m.parse = function(t) {
137
- this.$d = function(t) {
138
- var e = t.date, n = t.utc;
139
- if (null === e) return /* @__PURE__ */ new Date(NaN);
140
- if (b.u(e)) return /* @__PURE__ */ new Date();
141
- if (e instanceof Date) return new Date(e);
142
- if ("string" == typeof e && !/Z$/i.test(e)) {
143
- var r = e.match($);
144
- if (r) {
145
- var i = r[2] - 1 || 0, s = (r[7] || "0").substring(0, 3);
146
- return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s);
147
- }
148
- }
149
- return new Date(e);
150
- }(t), this.init();
151
- }, m.init = function() {
152
- var t = this.$d;
153
- this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds();
154
- }, m.$utils = function() {
155
- return b;
156
- }, m.isValid = function() {
157
- return !(this.$d.toString() === l);
158
- }, m.isSame = function(t, e) {
159
- var n = O(t);
160
- return this.startOf(e) <= n && n <= this.endOf(e);
161
- }, m.isAfter = function(t, e) {
162
- return O(t) < this.startOf(e);
163
- }, m.isBefore = function(t, e) {
164
- return this.endOf(e) < O(t);
165
- }, m.$g = function(t, e, n) {
166
- return b.u(t) ? this[e] : this.set(n, t);
167
- }, m.unix = function() {
168
- return Math.floor(this.valueOf() / 1e3);
169
- }, m.valueOf = function() {
170
- return this.$d.getTime();
171
- }, m.startOf = function(t, e) {
172
- var n = this, r = !!b.u(e) || e, f = b.p(t), l = function(t, e) {
173
- var i = b.w(n.$u ? Date.UTC(n.$y, e, t) : new Date(n.$y, e, t), n);
174
- return r ? i : i.endOf(a);
175
- }, $ = function(t, e) {
176
- return b.w(n.toDate()[t].apply(n.toDate("s"), (r ? [
177
- 0,
178
- 0,
179
- 0,
180
- 0
181
- ] : [
182
- 23,
183
- 59,
184
- 59,
185
- 999
186
- ]).slice(e)), n);
187
- }, y = this.$W, M = this.$M, m = this.$D, v = "set" + (this.$u ? "UTC" : "");
188
- switch (f) {
189
- case h: return r ? l(1, 0) : l(31, 11);
190
- case c: return r ? l(1, M) : l(0, M + 1);
191
- case o:
192
- var g = this.$locale().weekStart || 0, D = (y < g ? y + 7 : y) - g;
193
- return l(r ? m - D : m + (6 - D), M);
194
- case a:
195
- case d: return $(v + "Hours", 0);
196
- case u: return $(v + "Minutes", 1);
197
- case s: return $(v + "Seconds", 2);
198
- case i: return $(v + "Milliseconds", 3);
199
- default: return this.clone();
200
- }
201
- }, m.endOf = function(t) {
202
- return this.startOf(t, !1);
203
- }, m.$set = function(t, e) {
204
- var n, o = b.p(t), f = "set" + (this.$u ? "UTC" : ""), l = (n = {}, n[a] = f + "Date", n[d] = f + "Date", n[c] = f + "Month", n[h] = f + "FullYear", n[u] = f + "Hours", n[s] = f + "Minutes", n[i] = f + "Seconds", n[r] = f + "Milliseconds", n)[o], $ = o === a ? this.$D + (e - this.$W) : e;
205
- if (o === c || o === h) {
206
- var y = this.clone().set(d, 1);
207
- y.$d[l]($), y.init(), this.$d = y.set(d, Math.min(this.$D, y.daysInMonth())).$d;
208
- } else l && this.$d[l]($);
209
- return this.init(), this;
210
- }, m.set = function(t, e) {
211
- return this.clone().$set(t, e);
212
- }, m.get = function(t) {
213
- return this[b.p(t)]();
214
- }, m.add = function(r, f) {
215
- var d, l = this;
216
- r = Number(r);
217
- var $ = b.p(f), y = function(t) {
218
- var e = O(l);
219
- return b.w(e.date(e.date() + Math.round(t * r)), l);
220
- };
221
- if ($ === c) return this.set(c, this.$M + r);
222
- if ($ === h) return this.set(h, this.$y + r);
223
- if ($ === a) return y(1);
224
- if ($ === o) return y(7);
225
- var M = (d = {}, d[s] = e, d[u] = n, d[i] = t, d)[$] || 1, m = this.$d.getTime() + r * M;
226
- return b.w(m, this);
227
- }, m.subtract = function(t, e) {
228
- return this.add(-1 * t, e);
229
- }, m.format = function(t) {
230
- var e = this, n = this.$locale();
231
- if (!this.isValid()) return n.invalidDate || l;
232
- var r = t || "YYYY-MM-DDTHH:mm:ssZ", i = b.z(this), s = this.$H, u = this.$m, a = this.$M, o = n.weekdays, c = n.months, f = n.meridiem, h = function(t, n, i, s) {
233
- return t && (t[n] || t(e, r)) || i[n].slice(0, s);
234
- }, d = function(t) {
235
- return b.s(s % 12 || 12, t, "0");
236
- }, $ = f || function(t, e, n) {
237
- var r = t < 12 ? "AM" : "PM";
238
- return n ? r.toLowerCase() : r;
239
- };
240
- return r.replace(y, (function(t, r) {
241
- return r || function(t) {
242
- switch (t) {
243
- case "YY": return String(e.$y).slice(-2);
244
- case "YYYY": return b.s(e.$y, 4, "0");
245
- case "M": return a + 1;
246
- case "MM": return b.s(a + 1, 2, "0");
247
- case "MMM": return h(n.monthsShort, a, c, 3);
248
- case "MMMM": return h(c, a);
249
- case "D": return e.$D;
250
- case "DD": return b.s(e.$D, 2, "0");
251
- case "d": return String(e.$W);
252
- case "dd": return h(n.weekdaysMin, e.$W, o, 2);
253
- case "ddd": return h(n.weekdaysShort, e.$W, o, 3);
254
- case "dddd": return o[e.$W];
255
- case "H": return String(s);
256
- case "HH": return b.s(s, 2, "0");
257
- case "h": return d(1);
258
- case "hh": return d(2);
259
- case "a": return $(s, u, !0);
260
- case "A": return $(s, u, !1);
261
- case "m": return String(u);
262
- case "mm": return b.s(u, 2, "0");
263
- case "s": return String(e.$s);
264
- case "ss": return b.s(e.$s, 2, "0");
265
- case "SSS": return b.s(e.$ms, 3, "0");
266
- case "Z": return i;
267
- }
268
- return null;
269
- }(t) || i.replace(":", "");
270
- }));
271
- }, m.utcOffset = function() {
272
- return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
273
- }, m.diff = function(r, d, l) {
274
- var $, y = this, M = b.p(d), m = O(r), v = (m.utcOffset() - this.utcOffset()) * e, g = this - m, D = function() {
275
- return b.m(y, m);
276
- };
277
- switch (M) {
278
- case h:
279
- $ = D() / 12;
280
- break;
281
- case c:
282
- $ = D();
283
- break;
284
- case f:
285
- $ = D() / 3;
286
- break;
287
- case o:
288
- $ = (g - v) / 6048e5;
289
- break;
290
- case a:
291
- $ = (g - v) / 864e5;
292
- break;
293
- case u:
294
- $ = g / n;
295
- break;
296
- case s:
297
- $ = g / e;
298
- break;
299
- case i:
300
- $ = g / t;
301
- break;
302
- default: $ = g;
303
- }
304
- return l ? $ : b.a($);
305
- }, m.daysInMonth = function() {
306
- return this.endOf(c).$D;
307
- }, m.$locale = function() {
308
- return D[this.$L];
309
- }, m.locale = function(t, e) {
310
- if (!t) return this.$L;
311
- var n = this.clone(), r = w(t, e, !0);
312
- return r && (n.$L = r), n;
313
- }, m.clone = function() {
314
- return b.w(this.$d, this);
315
- }, m.toDate = function() {
316
- return new Date(this.valueOf());
317
- }, m.toJSON = function() {
318
- return this.isValid() ? this.toISOString() : null;
319
- }, m.toISOString = function() {
320
- return this.$d.toISOString();
321
- }, m.toString = function() {
322
- return this.$d.toUTCString();
323
- }, M;
324
- }(), Y = _.prototype;
325
- return O.prototype = Y, [
326
- ["$ms", r],
327
- ["$s", i],
328
- ["$m", s],
329
- ["$H", u],
330
- ["$W", a],
331
- ["$M", c],
332
- ["$y", h],
333
- ["$D", d]
334
- ].forEach((function(t) {
335
- Y[t[1]] = function(e) {
336
- return this.$g(e, t[0], t[1]);
337
- };
338
- })), O.extend = function(t, e) {
339
- return t.$i || (t(e, _, O), t.$i = !0), O;
340
- }, O.locale = w, O.isDayjs = S, O.unix = function(t) {
341
- return O(1e3 * t);
342
- }, O.en = D[g], O.Ls = D, O.p = {}, O;
343
- }));
344
- })))(), 1);
345
50
  function isPlainObject(value) {
346
51
  if (typeof value !== "object" || value === null) return false;
347
52
  const prototype = Object.getPrototypeOf(value);
@@ -6501,13 +6206,14 @@ createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
6501
6206
  const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
6502
6207
  //#endregion
6503
6208
  //#region src/utils/createWrangler.ts
6504
- const createWrangler = ({ stdio, accountId, cloudflareApiToken, cwd }) => {
6209
+ const createWrangler = ({ stdio, accountId, cloudflareApiToken, cwd, nonInteractive }) => {
6505
6210
  const $ = execa({
6506
6211
  stdio,
6507
6212
  extendsEnv: true,
6508
- shell: stdio === "inherit",
6213
+ shell: false,
6509
6214
  cwd,
6510
6215
  env: {
6216
+ ...nonInteractive ? { CI: "true" } : {},
6511
6217
  CLOUDFLARE_ACCOUNT_ID: accountId,
6512
6218
  CLOUDFLARE_API_TOKEN: cloudflareApiToken
6513
6219
  }
@@ -6515,6 +6221,465 @@ const createWrangler = ({ stdio, accountId, cloudflareApiToken, cwd }) => {
6515
6221
  return (...command) => $("npx", ["wrangler", ...command]);
6516
6222
  };
6517
6223
  //#endregion
6224
+ //#region iac/cloudflareInitErrors.ts
6225
+ const CLOUDFLARE_INIT_PERMISSION = { d1: "D1: Edit" };
6226
+ const CLOUDFLARE_INIT_PERMISSIONS = [CLOUDFLARE_INIT_PERMISSION.d1];
6227
+ const getCredentialRemediation = (source) => {
6228
+ switch (source.kind) {
6229
+ case "environment": return ["Update HOT_UPDATER_CLOUDFLARE_API_TOKEN in the environment, then rerun init."];
6230
+ case "env-file": return [`Update HOT_UPDATER_CLOUDFLARE_API_TOKEN in ${source.envFile}, then rerun init.`, "Alternatively, rerun without --env-file to enter a new token."];
6231
+ case "prompt": return ["Create a new API token, then rerun init and enter it again."];
6232
+ case "wrangler-oauth": return ["Refresh Wrangler authentication with `npx wrangler login`, then rerun init."];
6233
+ }
6234
+ };
6235
+ const getErrorOutput = (error) => {
6236
+ const output = [];
6237
+ if (error instanceof Error) output.push(error.message);
6238
+ if (typeof error === "object" && error !== null && "stderr" in error && typeof error.stderr === "string") output.push(error.stderr);
6239
+ if (typeof error === "object" && error !== null && "stdout" in error && typeof error.stdout === "string") output.push(error.stdout);
6240
+ return output.join("\n");
6241
+ };
6242
+ const isCloudflareAuthenticationError = (error) => {
6243
+ if (error instanceof APIError) return error.status === 401 || error.status === 403 || error.errors.some(({ code }) => code === 6003 || code === 6111 || code === 1e4 || code === 9109) || /invalid format for authorization header/i.test(error.message);
6244
+ return [
6245
+ /authentication error/i,
6246
+ /invalid access token/i,
6247
+ /invalid format for authorization header/i,
6248
+ /code:\s*(?:6003|6111|10000|9109)/i,
6249
+ /"code"\s*:\s*(?:6003|6111|10000|9109)/i
6250
+ ].some((pattern) => pattern.test(getErrorOutput(error)));
6251
+ };
6252
+ var CloudflareAuthenticationError = class extends InitError {
6253
+ name = "CloudflareAuthenticationError";
6254
+ constructor(source, accountId, options) {
6255
+ super([
6256
+ source.kind === "wrangler-oauth" ? "Cloudflare Wrangler authentication failed." : "Cloudflare API token is invalid, expired, or missing required permissions.",
6257
+ ...accountId ? [`Selected account: ${accountId}`, "Confirm the token is active and its resource scope includes this account."] : [],
6258
+ ...getCredentialRemediation(source),
6259
+ ...source.kind === "wrangler-oauth" ? [] : [`Required permissions: ${CLOUDFLARE_INIT_PERMISSIONS.join(", ")}`],
6260
+ "Verify token status: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/#test-the-token"
6261
+ ].join("\n"), options);
6262
+ this.source = source;
6263
+ this.accountId = accountId;
6264
+ }
6265
+ };
6266
+ var CloudflarePermissionError = class extends InitError {
6267
+ name = "CloudflarePermissionError";
6268
+ constructor(source, { accountId, check, requiredPermission }, options) {
6269
+ super([
6270
+ "Cloudflare API token cannot access a required account resource.",
6271
+ `Selected account: ${accountId}`,
6272
+ `Failed check: ${check}`,
6273
+ `Required permission: ${requiredPermission}`,
6274
+ "Confirm the token permission and account resource scope, then rerun init.",
6275
+ ...getCredentialRemediation(source)
6276
+ ].join("\n"), options);
6277
+ this.source = source;
6278
+ this.accountId = accountId;
6279
+ this.check = check;
6280
+ this.requiredPermission = requiredPermission;
6281
+ }
6282
+ accountId;
6283
+ check;
6284
+ requiredPermission;
6285
+ };
6286
+ var CloudflareDeploymentError = class extends InitError {
6287
+ name = "CloudflareDeploymentError";
6288
+ constructor(cause) {
6289
+ super([
6290
+ "Cloudflare Worker deployment failed.",
6291
+ "Review the Wrangler error above, fix the reported resource or permission, then rerun init.",
6292
+ `Cause: ${cause.message}`
6293
+ ].join("\n"), { cause });
6294
+ }
6295
+ };
6296
+ var CloudflareApiRequestError = class extends InitError {
6297
+ name = "CloudflareApiRequestError";
6298
+ constructor(cause) {
6299
+ super([
6300
+ "Cloudflare infrastructure request failed.",
6301
+ "Review the Cloudflare error, confirm the token permissions and selected resources, then rerun init.",
6302
+ `Cause: ${cause.message}`
6303
+ ].join("\n"), { cause });
6304
+ }
6305
+ };
6306
+ const toCloudflareApiError = (error, source) => {
6307
+ if (error instanceof InitError) return error;
6308
+ if (isCloudflareAuthenticationError(error)) return new CloudflareAuthenticationError(source, void 0, { cause: error });
6309
+ if (error instanceof APIError) return new CloudflareApiRequestError(error);
6310
+ return error instanceof Error ? error : new Error(String(error));
6311
+ };
6312
+ const runCloudflareApiRequest = async ({ request, source }) => {
6313
+ try {
6314
+ return await request();
6315
+ } catch (error) {
6316
+ if (error instanceof Error) throw toCloudflareApiError(error, source);
6317
+ throw new Error(String(error));
6318
+ }
6319
+ };
6320
+ const toCloudflareDeploymentError = (error, source) => {
6321
+ const apiError = toCloudflareApiError(error, source);
6322
+ return apiError instanceof InitError ? apiError : new CloudflareDeploymentError(apiError);
6323
+ };
6324
+ //#endregion
6325
+ //#region iac/cloudflareApiToken.ts
6326
+ const verifyCloudflareApiTokenIdentity = async ({ accountId, apiToken, verifyAccountToken, verifyUserToken }) => {
6327
+ if (apiToken.startsWith("cfat_")) return verifyAccountToken(accountId);
6328
+ if (apiToken.startsWith("cfut_")) return verifyUserToken();
6329
+ try {
6330
+ return await verifyAccountToken(accountId);
6331
+ } catch (error) {
6332
+ if (!isCloudflareAuthenticationError(error)) throw error;
6333
+ return verifyUserToken();
6334
+ }
6335
+ };
6336
+ const validateCloudflareApiToken = async ({ accountId, probes, source, verify }) => {
6337
+ let verification;
6338
+ try {
6339
+ verification = await verify();
6340
+ } catch (error) {
6341
+ if (isCloudflareAuthenticationError(error)) throw new CloudflareAuthenticationError(source, accountId, { cause: error });
6342
+ throw toCloudflareApiError(error, source);
6343
+ }
6344
+ if (verification.status !== "active") throw new CloudflareAuthenticationError(source, accountId);
6345
+ for (const probe of probes) try {
6346
+ await probe.request();
6347
+ } catch (error) {
6348
+ if (isCloudflareAuthenticationError(error)) throw new CloudflarePermissionError(source, {
6349
+ accountId,
6350
+ check: probe.check,
6351
+ requiredPermission: probe.requiredPermission
6352
+ }, { cause: error });
6353
+ throw toCloudflareApiError(error, source);
6354
+ }
6355
+ };
6356
+ //#endregion
6357
+ //#region iac/cloudflareD1Selection.ts
6358
+ var CloudflareD1IdentifierConflictError = class extends InitError {
6359
+ name = "CloudflareD1IdentifierConflictError";
6360
+ constructor({ databaseId, databaseName }) {
6361
+ super([
6362
+ "Cloudflare D1 identifiers conflict.",
6363
+ `Database ID: ${databaseId}`,
6364
+ `Database name: ${databaseName}`,
6365
+ "Update the env file so both values identify the same database, then rerun init."
6366
+ ].join("\n"));
6367
+ }
6368
+ };
6369
+ const resolveCloudflareReplayD1Database = ({ availableDatabases, databaseId, databaseName }) => {
6370
+ const exactMatch = availableDatabases.find((database) => database.uuid === databaseId && database.name === databaseName);
6371
+ if (exactMatch) return {
6372
+ database: exactMatch,
6373
+ kind: "existing"
6374
+ };
6375
+ if (availableDatabases.some((database) => database.uuid === databaseId || database.name === databaseName)) throw new CloudflareD1IdentifierConflictError({
6376
+ databaseId,
6377
+ databaseName
6378
+ });
6379
+ return {
6380
+ kind: "create",
6381
+ name: databaseName
6382
+ };
6383
+ };
6384
+ //#endregion
6385
+ //#region ../../node_modules/.pnpm/dayjs@1.11.21/node_modules/dayjs/dayjs.min.js
6386
+ var require_dayjs_min = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6387
+ (function(t, e) {
6388
+ "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
6389
+ })(exports, (function() {
6390
+ "use strict";
6391
+ var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = {
6392
+ name: "en",
6393
+ weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
6394
+ months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
6395
+ ordinal: function(t) {
6396
+ var e = [
6397
+ "th",
6398
+ "st",
6399
+ "nd",
6400
+ "rd"
6401
+ ], n = t % 100;
6402
+ return "[" + t + (e[(n - 20) % 10] || e[n] || e[0]) + "]";
6403
+ }
6404
+ }, m = function(t, e, n) {
6405
+ var r = String(t);
6406
+ return !r || r.length >= e ? t : "" + Array(e + 1 - r.length).join(n) + t;
6407
+ }, v = {
6408
+ s: m,
6409
+ z: function(t) {
6410
+ var e = -t.utcOffset(), n = Math.abs(e), r = Math.floor(n / 60), i = n % 60;
6411
+ return (e <= 0 ? "+" : "-") + m(r, 2, "0") + ":" + m(i, 2, "0");
6412
+ },
6413
+ m: function t(e, n) {
6414
+ if (e.date() < n.date()) return -t(n, e);
6415
+ var r = 12 * (n.year() - e.year()) + (n.month() - e.month()), i = e.clone().add(r, c), s = n - i < 0, u = e.clone().add(r + (s ? -1 : 1), c);
6416
+ return +(-(r + (n - i) / (s ? i - u : u - i)) || 0);
6417
+ },
6418
+ a: function(t) {
6419
+ return t < 0 ? Math.ceil(t) || 0 : Math.floor(t);
6420
+ },
6421
+ p: function(t) {
6422
+ return {
6423
+ M: c,
6424
+ y: h,
6425
+ w: o,
6426
+ d: a,
6427
+ D: d,
6428
+ h: u,
6429
+ m: s,
6430
+ s: i,
6431
+ ms: r,
6432
+ Q: f
6433
+ }[t] || String(t || "").toLowerCase().replace(/s$/, "");
6434
+ },
6435
+ u: function(t) {
6436
+ return void 0 === t;
6437
+ }
6438
+ }, g = "en", D = {};
6439
+ D[g] = M;
6440
+ var p = "$isDayjsObject", S = function(t) {
6441
+ return t instanceof _ || !(!t || !t[p]);
6442
+ }, w = function t(e, n, r) {
6443
+ var i;
6444
+ if (!e) return g;
6445
+ if ("string" == typeof e) {
6446
+ var s = e.toLowerCase();
6447
+ D[s] && (i = s), n && (D[s] = n, i = s);
6448
+ var u = e.split("-");
6449
+ if (!i && u.length > 1) return t(u[0]);
6450
+ } else {
6451
+ var a = e.name;
6452
+ D[a] = e, i = a;
6453
+ }
6454
+ return !r && i && (g = i), i || !r && g;
6455
+ }, O = function(t, e) {
6456
+ if (S(t)) return t.clone();
6457
+ var n = "object" == typeof e ? e : {};
6458
+ return n.date = t, n.args = arguments, new _(n);
6459
+ }, b = v;
6460
+ b.l = w, b.i = S, b.w = function(t, e) {
6461
+ return O(t, {
6462
+ locale: e.$L,
6463
+ utc: e.$u,
6464
+ x: e.$x,
6465
+ $offset: e.$offset
6466
+ });
6467
+ };
6468
+ var _ = function() {
6469
+ function M(t) {
6470
+ this.$L = w(t.locale, null, !0), this.parse(t), this.$x = this.$x || t.x || {}, this[p] = !0;
6471
+ }
6472
+ var m = M.prototype;
6473
+ return m.parse = function(t) {
6474
+ this.$d = function(t) {
6475
+ var e = t.date, n = t.utc;
6476
+ if (null === e) return /* @__PURE__ */ new Date(NaN);
6477
+ if (b.u(e)) return /* @__PURE__ */ new Date();
6478
+ if (e instanceof Date) return new Date(e);
6479
+ if ("string" == typeof e && !/Z$/i.test(e)) {
6480
+ var r = e.match($);
6481
+ if (r) {
6482
+ var i = r[2] - 1 || 0, s = (r[7] || "0").substring(0, 3);
6483
+ return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s);
6484
+ }
6485
+ }
6486
+ return new Date(e);
6487
+ }(t), this.init();
6488
+ }, m.init = function() {
6489
+ var t = this.$d;
6490
+ this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds();
6491
+ }, m.$utils = function() {
6492
+ return b;
6493
+ }, m.isValid = function() {
6494
+ return !(this.$d.toString() === l);
6495
+ }, m.isSame = function(t, e) {
6496
+ var n = O(t);
6497
+ return this.startOf(e) <= n && n <= this.endOf(e);
6498
+ }, m.isAfter = function(t, e) {
6499
+ return O(t) < this.startOf(e);
6500
+ }, m.isBefore = function(t, e) {
6501
+ return this.endOf(e) < O(t);
6502
+ }, m.$g = function(t, e, n) {
6503
+ return b.u(t) ? this[e] : this.set(n, t);
6504
+ }, m.unix = function() {
6505
+ return Math.floor(this.valueOf() / 1e3);
6506
+ }, m.valueOf = function() {
6507
+ return this.$d.getTime();
6508
+ }, m.startOf = function(t, e) {
6509
+ var n = this, r = !!b.u(e) || e, f = b.p(t), l = function(t, e) {
6510
+ var i = b.w(n.$u ? Date.UTC(n.$y, e, t) : new Date(n.$y, e, t), n);
6511
+ return r ? i : i.endOf(a);
6512
+ }, $ = function(t, e) {
6513
+ return b.w(n.toDate()[t].apply(n.toDate("s"), (r ? [
6514
+ 0,
6515
+ 0,
6516
+ 0,
6517
+ 0
6518
+ ] : [
6519
+ 23,
6520
+ 59,
6521
+ 59,
6522
+ 999
6523
+ ]).slice(e)), n);
6524
+ }, y = this.$W, M = this.$M, m = this.$D, v = "set" + (this.$u ? "UTC" : "");
6525
+ switch (f) {
6526
+ case h: return r ? l(1, 0) : l(31, 11);
6527
+ case c: return r ? l(1, M) : l(0, M + 1);
6528
+ case o:
6529
+ var g = this.$locale().weekStart || 0, D = (y < g ? y + 7 : y) - g;
6530
+ return l(r ? m - D : m + (6 - D), M);
6531
+ case a:
6532
+ case d: return $(v + "Hours", 0);
6533
+ case u: return $(v + "Minutes", 1);
6534
+ case s: return $(v + "Seconds", 2);
6535
+ case i: return $(v + "Milliseconds", 3);
6536
+ default: return this.clone();
6537
+ }
6538
+ }, m.endOf = function(t) {
6539
+ return this.startOf(t, !1);
6540
+ }, m.$set = function(t, e) {
6541
+ var n, o = b.p(t), f = "set" + (this.$u ? "UTC" : ""), l = (n = {}, n[a] = f + "Date", n[d] = f + "Date", n[c] = f + "Month", n[h] = f + "FullYear", n[u] = f + "Hours", n[s] = f + "Minutes", n[i] = f + "Seconds", n[r] = f + "Milliseconds", n)[o], $ = o === a ? this.$D + (e - this.$W) : e;
6542
+ if (o === c || o === h) {
6543
+ var y = this.clone().set(d, 1);
6544
+ y.$d[l]($), y.init(), this.$d = y.set(d, Math.min(this.$D, y.daysInMonth())).$d;
6545
+ } else l && this.$d[l]($);
6546
+ return this.init(), this;
6547
+ }, m.set = function(t, e) {
6548
+ return this.clone().$set(t, e);
6549
+ }, m.get = function(t) {
6550
+ return this[b.p(t)]();
6551
+ }, m.add = function(r, f) {
6552
+ var d, l = this;
6553
+ r = Number(r);
6554
+ var $ = b.p(f), y = function(t) {
6555
+ var e = O(l);
6556
+ return b.w(e.date(e.date() + Math.round(t * r)), l);
6557
+ };
6558
+ if ($ === c) return this.set(c, this.$M + r);
6559
+ if ($ === h) return this.set(h, this.$y + r);
6560
+ if ($ === a) return y(1);
6561
+ if ($ === o) return y(7);
6562
+ var M = (d = {}, d[s] = e, d[u] = n, d[i] = t, d)[$] || 1, m = this.$d.getTime() + r * M;
6563
+ return b.w(m, this);
6564
+ }, m.subtract = function(t, e) {
6565
+ return this.add(-1 * t, e);
6566
+ }, m.format = function(t) {
6567
+ var e = this, n = this.$locale();
6568
+ if (!this.isValid()) return n.invalidDate || l;
6569
+ var r = t || "YYYY-MM-DDTHH:mm:ssZ", i = b.z(this), s = this.$H, u = this.$m, a = this.$M, o = n.weekdays, c = n.months, f = n.meridiem, h = function(t, n, i, s) {
6570
+ return t && (t[n] || t(e, r)) || i[n].slice(0, s);
6571
+ }, d = function(t) {
6572
+ return b.s(s % 12 || 12, t, "0");
6573
+ }, $ = f || function(t, e, n) {
6574
+ var r = t < 12 ? "AM" : "PM";
6575
+ return n ? r.toLowerCase() : r;
6576
+ };
6577
+ return r.replace(y, (function(t, r) {
6578
+ return r || function(t) {
6579
+ switch (t) {
6580
+ case "YY": return String(e.$y).slice(-2);
6581
+ case "YYYY": return b.s(e.$y, 4, "0");
6582
+ case "M": return a + 1;
6583
+ case "MM": return b.s(a + 1, 2, "0");
6584
+ case "MMM": return h(n.monthsShort, a, c, 3);
6585
+ case "MMMM": return h(c, a);
6586
+ case "D": return e.$D;
6587
+ case "DD": return b.s(e.$D, 2, "0");
6588
+ case "d": return String(e.$W);
6589
+ case "dd": return h(n.weekdaysMin, e.$W, o, 2);
6590
+ case "ddd": return h(n.weekdaysShort, e.$W, o, 3);
6591
+ case "dddd": return o[e.$W];
6592
+ case "H": return String(s);
6593
+ case "HH": return b.s(s, 2, "0");
6594
+ case "h": return d(1);
6595
+ case "hh": return d(2);
6596
+ case "a": return $(s, u, !0);
6597
+ case "A": return $(s, u, !1);
6598
+ case "m": return String(u);
6599
+ case "mm": return b.s(u, 2, "0");
6600
+ case "s": return String(e.$s);
6601
+ case "ss": return b.s(e.$s, 2, "0");
6602
+ case "SSS": return b.s(e.$ms, 3, "0");
6603
+ case "Z": return i;
6604
+ }
6605
+ return null;
6606
+ }(t) || i.replace(":", "");
6607
+ }));
6608
+ }, m.utcOffset = function() {
6609
+ return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
6610
+ }, m.diff = function(r, d, l) {
6611
+ var $, y = this, M = b.p(d), m = O(r), v = (m.utcOffset() - this.utcOffset()) * e, g = this - m, D = function() {
6612
+ return b.m(y, m);
6613
+ };
6614
+ switch (M) {
6615
+ case h:
6616
+ $ = D() / 12;
6617
+ break;
6618
+ case c:
6619
+ $ = D();
6620
+ break;
6621
+ case f:
6622
+ $ = D() / 3;
6623
+ break;
6624
+ case o:
6625
+ $ = (g - v) / 6048e5;
6626
+ break;
6627
+ case a:
6628
+ $ = (g - v) / 864e5;
6629
+ break;
6630
+ case u:
6631
+ $ = g / n;
6632
+ break;
6633
+ case s:
6634
+ $ = g / e;
6635
+ break;
6636
+ case i:
6637
+ $ = g / t;
6638
+ break;
6639
+ default: $ = g;
6640
+ }
6641
+ return l ? $ : b.a($);
6642
+ }, m.daysInMonth = function() {
6643
+ return this.endOf(c).$D;
6644
+ }, m.$locale = function() {
6645
+ return D[this.$L];
6646
+ }, m.locale = function(t, e) {
6647
+ if (!t) return this.$L;
6648
+ var n = this.clone(), r = w(t, e, !0);
6649
+ return r && (n.$L = r), n;
6650
+ }, m.clone = function() {
6651
+ return b.w(this.$d, this);
6652
+ }, m.toDate = function() {
6653
+ return new Date(this.valueOf());
6654
+ }, m.toJSON = function() {
6655
+ return this.isValid() ? this.toISOString() : null;
6656
+ }, m.toISOString = function() {
6657
+ return this.$d.toISOString();
6658
+ }, m.toString = function() {
6659
+ return this.$d.toUTCString();
6660
+ }, M;
6661
+ }(), Y = _.prototype;
6662
+ return O.prototype = Y, [
6663
+ ["$ms", r],
6664
+ ["$s", i],
6665
+ ["$m", s],
6666
+ ["$H", u],
6667
+ ["$W", a],
6668
+ ["$M", c],
6669
+ ["$y", h],
6670
+ ["$D", d]
6671
+ ].forEach((function(t) {
6672
+ Y[t[1]] = function(e) {
6673
+ return this.$g(e, t[0], t[1]);
6674
+ };
6675
+ })), O.extend = function(t, e) {
6676
+ return t.$i || (t(e, _, O), t.$i = !0), O;
6677
+ }, O.locale = w, O.isDayjs = S, O.unix = function(t) {
6678
+ return O(1e3 * t);
6679
+ }, O.en = D[g], O.Ls = D, O.p = {}, O;
6680
+ }));
6681
+ }));
6682
+ //#endregion
6518
6683
  //#region ../../node_modules/.pnpm/toml@3.0.0/node_modules/toml/lib/parser.js
6519
6684
  var require_parser = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6520
6685
  module.exports = (function() {
@@ -10802,6 +10967,7 @@ var require_mod_cjs = /* @__PURE__ */ __commonJSMin(((exports, module) => {
10802
10967
  }));
10803
10968
  //#endregion
10804
10969
  //#region ../../node_modules/.pnpm/xdg-app-paths@8.3.0/node_modules/xdg-app-paths/dist/cjs/esm-wrapper/mod.esm.js
10970
+ var import_dayjs_min = /* @__PURE__ */ __toESM(require_dayjs_min(), 1);
10805
10971
  var import_toml = /* @__PURE__ */ __toESM(require_toml(), 1);
10806
10972
  var mod_esm_default = (/* @__PURE__ */ __toESM(require_mod_cjs())).default;
10807
10973
  //#endregion
@@ -10829,6 +10995,206 @@ const getWranglerLoginAuthToken = () => {
10829
10995
  }
10830
10996
  };
10831
10997
  //#endregion
10998
+ //#region iac/cloudflareInfrastructureAuth.ts
10999
+ var CloudflareWranglerLoginRequiredError = class extends InitError {
11000
+ name = "CloudflareWranglerLoginRequiredError";
11001
+ constructor() {
11002
+ super(["Cloudflare Wrangler login is required.", "Run `npx wrangler login` interactively, then rerun init."].join("\n"));
11003
+ }
11004
+ };
11005
+ const getValidWranglerAuthToken = () => {
11006
+ const auth = getWranglerLoginAuthToken();
11007
+ if (!auth || (0, import_dayjs_min.default)(auth.expiration_time).isBefore((0, import_dayjs_min.default)())) return;
11008
+ return auth.oauth_token;
11009
+ };
11010
+ const resolveCloudflareInfrastructureApiToken = async ({ cwd, nonInteractive }) => {
11011
+ const existingToken = getValidWranglerAuthToken();
11012
+ if (existingToken) return existingToken;
11013
+ if (nonInteractive) throw new CloudflareWranglerLoginRequiredError();
11014
+ await execa("npx", [
11015
+ "wrangler",
11016
+ "login",
11017
+ "--scopes",
11018
+ "account:read",
11019
+ "user:read",
11020
+ "d1:write",
11021
+ "workers:write",
11022
+ "workers_scripts:write"
11023
+ ], { cwd });
11024
+ const authenticatedToken = getValidWranglerAuthToken();
11025
+ if (!authenticatedToken) throw new CloudflareWranglerLoginRequiredError();
11026
+ return authenticatedToken;
11027
+ };
11028
+ //#endregion
11029
+ //#region iac/init/index.ts
11030
+ const initProvider = {
11031
+ label: "Cloudflare D1 + R2 + Worker",
11032
+ inputs: {
11033
+ accountId: {
11034
+ envKey: "HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID",
11035
+ help: "Cloudflare account ID",
11036
+ prompt: {
11037
+ message: "Account List",
11038
+ type: "select"
11039
+ }
11040
+ },
11041
+ apiToken: {
11042
+ envKey: "HOT_UPDATER_CLOUDFLARE_API_TOKEN",
11043
+ help: "API token with D1 edit permission",
11044
+ persistence: "with-consent",
11045
+ prompt: {
11046
+ message: "Enter the Cloudflare D1 API Token",
11047
+ type: "password"
11048
+ }
11049
+ },
11050
+ bucketName: {
11051
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME",
11052
+ help: "R2 bucket name",
11053
+ prompt: {
11054
+ defaultValue: "hot-updater-storage",
11055
+ message: "Enter the name of the new R2 Bucket",
11056
+ placeholder: "hot-updater-storage",
11057
+ type: "text"
11058
+ }
11059
+ },
11060
+ accessKeyId: {
11061
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID",
11062
+ help: "R2 access key ID",
11063
+ persistence: "with-consent",
11064
+ prompt: {
11065
+ message: "Enter the R2 Access Key ID",
11066
+ type: "password"
11067
+ }
11068
+ },
11069
+ secretAccessKey: {
11070
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY",
11071
+ help: "R2 secret access key",
11072
+ persistence: "with-consent",
11073
+ prompt: {
11074
+ message: "Enter the R2 Secret Access Key",
11075
+ type: "password"
11076
+ }
11077
+ },
11078
+ workerName: {
11079
+ envKey: "HOT_UPDATER_CLOUDFLARE_WORKER_NAME",
11080
+ help: "Worker name",
11081
+ prompt: {
11082
+ defaultValue: "hot-updater",
11083
+ message: "Enter the name of the worker",
11084
+ placeholder: "hot-updater",
11085
+ type: "text"
11086
+ }
11087
+ },
11088
+ d1DatabaseId: {
11089
+ envKey: "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID",
11090
+ help: "D1 database ID"
11091
+ },
11092
+ d1DatabaseName: {
11093
+ envKey: "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_NAME",
11094
+ help: "D1 database name",
11095
+ prompt: {
11096
+ defaultValue: "hot-updater",
11097
+ message: "Enter the name of the new D1 Database",
11098
+ placeholder: "hot-updater",
11099
+ type: "text"
11100
+ }
11101
+ },
11102
+ r2Private: {
11103
+ envKey: "HOT_UPDATER_CLOUDFLARE_R2_PRIVATE",
11104
+ help: "Whether the R2 bucket is private (true or false)",
11105
+ prompt: {
11106
+ message: "Make R2 bucket private?",
11107
+ type: "confirm"
11108
+ },
11109
+ validate: (value) => value === "true" || value === "false"
11110
+ }
11111
+ }
11112
+ };
11113
+ //#endregion
11114
+ //#region iac/cloudflareInitInputs.ts
11115
+ const shouldUpdateR2ManagedDomain = ({ isPrivate, managedDomainEnabled }) => managedDomainEnabled !== !isPrivate;
11116
+ const resolveR2Privacy = ({ createBucket, managedDomainEnabled, savedPrivateSetting }) => {
11117
+ if (savedPrivateSetting === "true" || savedPrivateSetting === "false") return {
11118
+ isPrivate: savedPrivateSetting === "true",
11119
+ kind: "resolved"
11120
+ };
11121
+ if (!createBucket && managedDomainEnabled !== void 0) return {
11122
+ isPrivate: !managedDomainEnabled,
11123
+ kind: "resolved"
11124
+ };
11125
+ return { kind: "prompt" };
11126
+ };
11127
+ const resolveCloudflareInitInputs = (existingEnv) => {
11128
+ const { inputs } = initProvider;
11129
+ return {
11130
+ accessKeyId: resolveInitProviderInput(existingEnv, inputs.accessKeyId),
11131
+ accountId: resolveInitProviderInput(existingEnv, inputs.accountId),
11132
+ apiToken: resolveInitProviderInput(existingEnv, inputs.apiToken),
11133
+ bucketName: resolveInitProviderInput(existingEnv, inputs.bucketName),
11134
+ d1DatabaseId: resolveInitProviderInput(existingEnv, inputs.d1DatabaseId),
11135
+ d1DatabaseName: resolveInitProviderInput(existingEnv, inputs.d1DatabaseName),
11136
+ r2Private: resolveInitProviderInput(existingEnv, inputs.r2Private),
11137
+ secretAccessKey: resolveInitProviderInput(existingEnv, inputs.secretAccessKey),
11138
+ workerName: resolveInitProviderInput(existingEnv, inputs.workerName)
11139
+ };
11140
+ };
11141
+ const assertCloudflareNonInteractiveInputs = (inputs, nonInteractive) => {
11142
+ assertInitProviderInputs({
11143
+ inputs,
11144
+ provider: initProvider,
11145
+ strict: nonInteractive
11146
+ });
11147
+ };
11148
+ //#endregion
11149
+ //#region iac/cloudflareInitSecrets.ts
11150
+ const inputCloudflareInitSecrets = async ({ accountId, bucketName, apiToken, accessKeyId, secretAccessKey, workerName, nonInteractive }) => {
11151
+ const inputDefinitions = initProvider.inputs;
11152
+ assertInitInputs({
11153
+ inputs: {
11154
+ [inputDefinitions.apiToken.envKey]: apiToken,
11155
+ [inputDefinitions.accessKeyId.envKey]: accessKeyId,
11156
+ [inputDefinitions.secretAccessKey.envKey]: secretAccessKey,
11157
+ [inputDefinitions.workerName.envKey]: workerName
11158
+ },
11159
+ strict: nonInteractive
11160
+ });
11161
+ if (nonInteractive && apiToken && accessKeyId && secretAccessKey && workerName) return {
11162
+ accessKeyId,
11163
+ apiToken,
11164
+ secretAccessKey,
11165
+ workerName
11166
+ };
11167
+ if (!nonInteractive) {
11168
+ p.log.step(`D1 API Token dashboard: ${link(`https://dash.cloudflare.com/${accountId}/api-tokens`)}`);
11169
+ p.log.step(`Required permissions: ${CLOUDFLARE_INIT_PERMISSIONS.join(", ")}`);
11170
+ p.log.step("Used by d1Database for bundle metadata writes.");
11171
+ }
11172
+ if (!nonInteractive) {
11173
+ p.log.step(`R2 API Tokens dashboard: ${link(`https://dash.cloudflare.com/${accountId}/r2/api-tokens`)}`);
11174
+ p.log.step("Required permission: Object Read & Write");
11175
+ p.log.step(`Target bucket: ${bucketName}`);
11176
+ }
11177
+ return await p.group({
11178
+ apiToken: () => nonInteractive && apiToken ? Promise.resolve(apiToken) : p.password({
11179
+ message: inputDefinitions.apiToken.prompt.message,
11180
+ validate: (value) => value ? void 0 : "Cloudflare API Token is required"
11181
+ }),
11182
+ accessKeyId: () => nonInteractive && accessKeyId ? Promise.resolve(accessKeyId) : p.password({
11183
+ message: inputDefinitions.accessKeyId.prompt.message,
11184
+ validate: (value) => value ? void 0 : "R2 Access Key ID is required"
11185
+ }),
11186
+ secretAccessKey: () => nonInteractive && secretAccessKey ? Promise.resolve(secretAccessKey) : p.password({
11187
+ message: inputDefinitions.secretAccessKey.prompt.message,
11188
+ validate: (value) => value ? void 0 : "R2 Secret Access Key is required"
11189
+ }),
11190
+ workerName: () => nonInteractive && workerName ? Promise.resolve(workerName) : p.text({
11191
+ ...getInitProviderTextPromptValues(inputDefinitions.workerName.prompt, workerName),
11192
+ message: inputDefinitions.workerName.prompt.message,
11193
+ validate: (value) => value ? void 0 : "Worker name is required"
11194
+ })
11195
+ }, { onCancel: () => process.exit(1) });
11196
+ };
11197
+ //#endregion
10832
11198
  //#region iac/index.ts
10833
11199
  const getConfigScaffold = (build) => {
10834
11200
  return createHotUpdaterConfigScaffoldFromBuilder(new ConfigBuilder().setBuildType(build).setStorage({
@@ -10867,50 +11233,7 @@ export default HotUpdater.wrap({
10867
11233
  baseURL: "%%source%%",
10868
11234
  updateStrategy: "appVersion", // or "fingerprint"
10869
11235
  })(App);`;
10870
- const HOT_UPDATER_ENV_PATH = ".env.hotupdater";
10871
- const unquoteEnvValue = (value) => {
10872
- const trimmed = value.trim();
10873
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
10874
- return trimmed;
10875
- };
10876
- const readHotUpdaterEnv = async (cwd) => {
10877
- const envPath = path.join(cwd, HOT_UPDATER_ENV_PATH);
10878
- const content = await fs.readFile(envPath, "utf-8").catch(() => "");
10879
- const env = {};
10880
- for (const line of content.split("\n")) {
10881
- const trimmed = line.trim();
10882
- if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;
10883
- const [key, ...valueParts] = trimmed.split("=");
10884
- if (!key) continue;
10885
- env[key.trim()] = unquoteEnvValue(valueParts.join("="));
10886
- }
10887
- return env;
10888
- };
10889
- const getEnvValue = (env, key) => {
10890
- return process.env[key]?.trim() || env[key]?.trim() || void 0;
10891
- };
10892
- const inputR2ApiCredentials = async ({ accountId, bucketName, accessKeyId, secretAccessKey }) => {
10893
- p.log.step(`R2 API Tokens dashboard: ${link(`https://dash.cloudflare.com/${accountId}/r2/api-tokens`)}`);
10894
- p.log.step("Required permission: Object Read & Write");
10895
- p.log.step(`Target bucket: ${bucketName}`);
10896
- let resolvedAccessKeyId = accessKeyId;
10897
- if (!resolvedAccessKeyId) {
10898
- const inputR2AccessKeyId = await p.password({ message: "Enter the R2 Access Key ID" });
10899
- if (p.isCancel(inputR2AccessKeyId)) process.exit(1);
10900
- resolvedAccessKeyId = inputR2AccessKeyId;
10901
- }
10902
- let resolvedSecretAccessKey = secretAccessKey;
10903
- if (!resolvedSecretAccessKey) {
10904
- const inputR2SecretAccessKey = await p.password({ message: "Enter the R2 Secret Access Key" });
10905
- if (p.isCancel(inputR2SecretAccessKey)) process.exit(1);
10906
- resolvedSecretAccessKey = inputR2SecretAccessKey;
10907
- }
10908
- return {
10909
- accessKeyId: resolvedAccessKeyId,
10910
- secretAccessKey: resolvedSecretAccessKey
10911
- };
10912
- };
10913
- const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseName, r2BucketName, workerName }) => {
11236
+ const deployWorker = async (apiToken, accountId, { credentialSource, d1DatabaseId, d1DatabaseName, nonInteractive, r2BucketName, workerName }) => {
10914
11237
  const cwd = getCwd();
10915
11238
  const cloudflarePackagePath = __require.resolve("@hot-updater/cloudflare/package.json", { paths: [cwd] });
10916
11239
  const { tmpDir, removeTmpDir } = await copyDirToTmp(path.dirname(cloudflarePackagePath));
@@ -10930,9 +11253,10 @@ const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseNa
10930
11253
  await fs.writeFile(path.join(workerRoot, "wrangler.json"), JSON.stringify(wranglerConfig, null, 2));
10931
11254
  const wrangler = await createWrangler({
10932
11255
  stdio: "inherit",
10933
- cloudflareApiToken: oauth_token,
11256
+ cloudflareApiToken: apiToken,
10934
11257
  cwd: workerRoot,
10935
- accountId
11258
+ accountId,
11259
+ nonInteractive
10936
11260
  });
10937
11261
  const migrationPath = await path.join(workerRoot, "migrations");
10938
11262
  const migrationFiles = await fs.readdir(migrationPath);
@@ -10942,48 +11266,35 @@ const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseNa
10942
11266
  await fs.writeFile(filePath, transformTemplate(content, { BUCKET_NAME: r2BucketName }));
10943
11267
  }
10944
11268
  await wrangler("d1", "migrations", "apply", d1DatabaseName, "--remote");
10945
- let resolvedWorkerName = workerName;
10946
- if (resolvedWorkerName) p.log.info("Using existing Cloudflare Worker name.");
10947
- else {
10948
- const inputWorkerName = await p.text({
10949
- message: "Enter the name of the worker",
10950
- defaultValue: "hot-updater",
10951
- placeholder: "hot-updater"
10952
- });
10953
- if (p.isCancel(inputWorkerName)) process.exit(1);
10954
- resolvedWorkerName = inputWorkerName;
10955
- }
10956
- await wrangler("deploy", "--name", resolvedWorkerName);
10957
- return resolvedWorkerName;
11269
+ await wrangler("deploy", "--name", workerName);
11270
+ return workerName;
10958
11271
  } catch (error) {
10959
- throw new Error("Failed to deploy worker", { cause: error });
11272
+ if (error instanceof Error) throw toCloudflareDeploymentError(error, credentialSource);
11273
+ throw new Error(String(error));
10960
11274
  } finally {
10961
11275
  await removeTmpDir();
10962
11276
  }
10963
11277
  };
10964
- const runInit = async ({ build }) => {
11278
+ const runInit = async ({ build, envFile }) => {
10965
11279
  const cwd = getCwd();
10966
- const existingEnv = await readHotUpdaterEnv(cwd);
10967
- let auth = getWranglerLoginAuthToken();
10968
- if (!auth || (0, import_dayjs_min.default)(auth?.expiration_time).isBefore((0, import_dayjs_min.default)())) {
10969
- await execa("npx", [
10970
- "wrangler",
10971
- "login",
10972
- "--scopes",
10973
- "account:read",
10974
- "user:read",
10975
- "d1:write",
10976
- "workers:write",
10977
- "workers_scripts:write"
10978
- ], { cwd });
10979
- auth = getWranglerLoginAuthToken();
10980
- }
10981
- if (!auth) throw new Error("'npx wrangler login' is required to use this command");
10982
- const cf = new Cloudflare({ apiToken: auth.oauth_token });
11280
+ const nonInteractive = envFile !== void 0;
11281
+ const initEnvSources = await readHotUpdaterInitEnv(cwd, envFile);
11282
+ const { managedEnv } = initEnvSources;
11283
+ const existingInputs = resolveCloudflareInitInputs(getHotUpdaterInitInputEnv(initEnvSources, nonInteractive));
11284
+ assertCloudflareNonInteractiveInputs(existingInputs, nonInteractive);
11285
+ const { accessKeyId: existingR2AccessKeyId, accountId: existingAccountId, apiToken: existingApiToken, bucketName: existingBucketName, d1DatabaseId: existingD1DatabaseId, d1DatabaseName: existingD1DatabaseName, r2Private: savedPrivateSetting, secretAccessKey: existingR2SecretAccessKey, workerName: existingWorkerName } = existingInputs;
11286
+ const infrastructureCredentialSource = { kind: "wrangler-oauth" };
11287
+ const infrastructureApiToken = await resolveCloudflareInfrastructureApiToken({
11288
+ cwd,
11289
+ nonInteractive
11290
+ });
11291
+ const cf = new Cloudflare({ apiToken: infrastructureApiToken });
10983
11292
  const createKey = `create/${Math.random().toString(36).substring(2, 15)}`;
10984
- let accountId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID");
10985
- if (accountId) p.log.info("Using existing Cloudflare account ID.");
10986
- else {
11293
+ let accountId;
11294
+ if (nonInteractive && existingAccountId) {
11295
+ accountId = existingAccountId;
11296
+ p.log.info("Using existing Cloudflare account ID.");
11297
+ } else {
10987
11298
  const accounts = [];
10988
11299
  try {
10989
11300
  await p.tasks([{
@@ -10995,12 +11306,15 @@ const runInit = async ({ build }) => {
10995
11306
  })));
10996
11307
  }
10997
11308
  }]);
10998
- } catch (e) {
10999
- if (e instanceof Error) p.log.error(e.message);
11000
- throw e;
11309
+ } catch (error) {
11310
+ if (error instanceof Error) throw toCloudflareApiError(error, infrastructureCredentialSource);
11311
+ throw new Error(String(error));
11001
11312
  }
11313
+ const savedAccount = accounts.find((account) => account.id === existingAccountId);
11314
+ if (existingAccountId && !savedAccount) p.log.warn("Saved Cloudflare account was not found. Select an account again.");
11002
11315
  const selectedAccountId = await p.select({
11003
- message: "Account List",
11316
+ initialValue: savedAccount?.id ?? accounts[0]?.id,
11317
+ message: initProvider.inputs.accountId.prompt.message,
11004
11318
  options: accounts.map((account) => ({
11005
11319
  value: account.id,
11006
11320
  label: `${account.name} (${account.id})`
@@ -11009,103 +11323,60 @@ const runInit = async ({ build }) => {
11009
11323
  if (p.isCancel(selectedAccountId)) process.exit(1);
11010
11324
  accountId = selectedAccountId;
11011
11325
  }
11012
- let apiToken = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_API_TOKEN");
11013
- if (apiToken) p.log.info("Using existing Cloudflare API token.");
11014
- else {
11015
- p.log.step(`D1 API Token dashboard: ${link(`https://dash.cloudflare.com/${accountId}/api-tokens`)}`);
11016
- p.log.step("Required permission: D1 Edit");
11017
- p.log.step("Used for bundle metadata writes after init.");
11018
- const inputApiToken = await p.password({ message: "Enter the D1 API Token" });
11019
- if (p.isCancel(inputApiToken)) process.exit(1);
11020
- apiToken = inputApiToken;
11021
- if (!apiToken) p.log.warn("Skipping API Token. You can set it later in .env HOT_UPDATER_CLOUDFLARE_API_TOKEN file.");
11022
- }
11023
- const existingBucketName = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME");
11326
+ const availableBuckets = [];
11327
+ try {
11328
+ await p.tasks([{
11329
+ title: "Checking R2 Buckets...",
11330
+ task: async () => {
11331
+ const buckets = (await cf.r2.buckets.list({ account_id: accountId })).buckets ?? [];
11332
+ availableBuckets.push(...buckets.flatMap((bucket) => bucket.name ? [{ name: bucket.name }] : []));
11333
+ }
11334
+ }]);
11335
+ } catch (error) {
11336
+ if (error instanceof Error) throw toCloudflareApiError(error, infrastructureCredentialSource);
11337
+ throw new Error(String(error));
11338
+ }
11339
+ const hasExistingBucket = availableBuckets.some((bucket) => bucket.name === existingBucketName);
11340
+ let createBucket = false;
11024
11341
  let selectedBucketName;
11025
- if (existingBucketName) {
11342
+ if (nonInteractive && existingBucketName && hasExistingBucket) {
11343
+ selectedBucketName = existingBucketName;
11344
+ p.log.info("Using existing Cloudflare R2 bucket.");
11345
+ } else if (nonInteractive && existingBucketName) {
11026
11346
  selectedBucketName = existingBucketName;
11027
- p.log.info("Using existing Cloudflare R2 bucket name.");
11347
+ createBucket = true;
11028
11348
  } else {
11029
- const availableBuckets = [];
11030
- try {
11031
- await p.tasks([{
11032
- title: "Checking R2 Buckets...",
11033
- task: async () => {
11034
- const buckets = (await cf.r2.buckets.list({ account_id: accountId })).buckets ?? [];
11035
- availableBuckets.push(...buckets.filter((bucket) => bucket.name).map((bucket) => ({ name: bucket.name })));
11036
- }
11037
- }]);
11038
- } catch (e) {
11039
- if (e instanceof Error) p.log.error(e.message);
11040
- throw e;
11041
- }
11042
- if (availableBuckets.length === 1) {
11043
- selectedBucketName = availableBuckets[0].name;
11044
- p.log.info("Using the only Cloudflare R2 bucket.");
11045
- } else {
11046
- const selectedR2BucketName = await p.select({
11047
- message: "R2 List",
11048
- options: [...availableBuckets.map((bucket) => ({
11049
- value: bucket.name,
11050
- label: bucket.name
11051
- })), {
11052
- value: createKey,
11053
- label: "Create New R2 Bucket"
11054
- }]
11349
+ if (existingBucketName && !hasExistingBucket) p.log.warn("Saved Cloudflare R2 bucket was not found. Select a bucket again.");
11350
+ const selectedR2BucketName = await p.select({
11351
+ initialValue: hasExistingBucket ? existingBucketName : availableBuckets[0]?.name,
11352
+ message: "R2 List",
11353
+ options: [...availableBuckets.map((bucket) => ({
11354
+ value: bucket.name,
11355
+ label: bucket.name
11356
+ })), {
11357
+ value: createKey,
11358
+ label: "Create New R2 Bucket"
11359
+ }]
11360
+ });
11361
+ if (p.isCancel(selectedR2BucketName)) process.exit(1);
11362
+ if (selectedR2BucketName === createKey) {
11363
+ const prompt = initProvider.inputs.bucketName.prompt;
11364
+ const name = await p.text({
11365
+ ...getInitProviderTextPromptValues(prompt, existingBucketName),
11366
+ message: prompt.message,
11367
+ validate: (value) => value ? void 0 : "R2 bucket name is required"
11055
11368
  });
11056
- if (p.isCancel(selectedR2BucketName)) process.exit(1);
11057
- selectedBucketName = selectedR2BucketName;
11058
- }
11059
- if (selectedBucketName === createKey) {
11060
- const name = await p.text({ message: "Enter the name of the new R2 Bucket" });
11061
11369
  if (p.isCancel(name)) process.exit(1);
11062
- const newR2 = await cf.r2.buckets.create({
11063
- account_id: accountId,
11064
- name
11065
- });
11066
- if (!newR2.name) throw new Error("Failed to create new R2 Bucket");
11067
- selectedBucketName = newR2.name;
11068
- }
11069
- }
11070
- p.log.info(`Selected R2: ${selectedBucketName}`);
11071
- const existingR2AccessKeyId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID");
11072
- const existingR2SecretAccessKey = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY");
11073
- let r2AccessKeyId = existingR2AccessKeyId;
11074
- let r2SecretAccessKey = existingR2SecretAccessKey;
11075
- if (r2AccessKeyId && r2SecretAccessKey) p.log.info("Using existing Cloudflare R2 API credentials.");
11076
- else if (r2AccessKeyId || r2SecretAccessKey) {
11077
- p.log.warn("Existing Cloudflare R2 API credentials are incomplete.");
11078
- const credentials = await inputR2ApiCredentials({
11079
- accountId,
11080
- bucketName: selectedBucketName,
11081
- accessKeyId: r2AccessKeyId,
11082
- secretAccessKey: r2SecretAccessKey
11083
- });
11084
- r2AccessKeyId = credentials.accessKeyId;
11085
- r2SecretAccessKey = credentials.secretAccessKey;
11086
- } else {
11087
- const credentials = await inputR2ApiCredentials({
11088
- accountId,
11089
- bucketName: selectedBucketName
11090
- });
11091
- r2AccessKeyId = credentials.accessKeyId;
11092
- r2SecretAccessKey = credentials.secretAccessKey;
11370
+ selectedBucketName = name;
11371
+ createBucket = true;
11372
+ } else selectedBucketName = selectedR2BucketName;
11093
11373
  }
11094
- if ((existingBucketName ? { enabled: false } : await cf.r2.buckets.domains.managed.list(selectedBucketName, { account_id: accountId })).enabled) {
11095
- if (await p.confirm({ message: "Make R2 bucket private?" })) try {
11096
- await p.tasks([{
11097
- title: "Making R2 bucket private...",
11098
- task: async () => {
11099
- await cf.r2.buckets.domains.managed.update(selectedBucketName, {
11100
- account_id: accountId,
11101
- enabled: false
11102
- });
11103
- }
11104
- }]);
11105
- } catch (e) {
11106
- if (e instanceof Error) p.log.error(e.message);
11107
- throw e;
11108
- }
11374
+ let managedDomainEnabled;
11375
+ if (!createBucket) try {
11376
+ managedDomainEnabled = (await cf.r2.buckets.domains.managed.list(selectedBucketName, { account_id: accountId })).enabled;
11377
+ } catch (error) {
11378
+ if (error instanceof Error) throw toCloudflareApiError(error, infrastructureCredentialSource);
11379
+ throw new Error(String(error));
11109
11380
  }
11110
11381
  const availableD1List = [];
11111
11382
  try {
@@ -11113,25 +11384,38 @@ const runInit = async ({ build }) => {
11113
11384
  title: "Checking D1 List...",
11114
11385
  task: async () => {
11115
11386
  const d1List = (await cf.d1.database.list({ account_id: accountId })).result ?? [];
11116
- availableD1List.push(...d1List.filter((d1) => d1.name || d1.uuid).map((d1) => ({
11387
+ availableD1List.push(...d1List.flatMap((d1) => d1.name && d1.uuid ? [{
11117
11388
  name: d1.name,
11118
11389
  uuid: d1.uuid
11119
- })));
11390
+ }] : []));
11120
11391
  }
11121
11392
  }]);
11122
- } catch (e) {
11123
- if (e instanceof Error) p.log.error(e.message);
11124
- throw e;
11393
+ } catch (error) {
11394
+ if (error instanceof Error) throw toCloudflareApiError(error, infrastructureCredentialSource);
11395
+ throw new Error(String(error));
11125
11396
  }
11126
- const existingD1DatabaseId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID");
11127
- const hasExistingD1Database = availableD1List.some((d1) => d1.uuid === existingD1DatabaseId);
11397
+ const existingD1Database = availableD1List.find((d1) => d1.uuid === existingD1DatabaseId || d1.name === existingD1DatabaseName);
11398
+ let createD1Database = false;
11128
11399
  let selectedD1DatabaseId;
11129
- if (existingD1DatabaseId && hasExistingD1Database) {
11130
- selectedD1DatabaseId = existingD1DatabaseId;
11131
- p.log.info("Using existing Cloudflare D1 database ID.");
11400
+ let d1DatabaseName;
11401
+ if (nonInteractive && existingD1DatabaseId && existingD1DatabaseName) {
11402
+ const replayResolution = resolveCloudflareReplayD1Database({
11403
+ availableDatabases: availableD1List,
11404
+ databaseId: existingD1DatabaseId,
11405
+ databaseName: existingD1DatabaseName
11406
+ });
11407
+ if (replayResolution.kind === "existing") {
11408
+ selectedD1DatabaseId = replayResolution.database.uuid;
11409
+ d1DatabaseName = replayResolution.database.name;
11410
+ p.log.info("Using existing Cloudflare D1 database.");
11411
+ } else {
11412
+ createD1Database = true;
11413
+ d1DatabaseName = replayResolution.name;
11414
+ }
11132
11415
  } else {
11133
- if (existingD1DatabaseId) p.log.warn("Existing Cloudflare D1 database ID was not found. Select a database again.");
11416
+ if ((existingD1DatabaseId || existingD1DatabaseName) && !existingD1Database) p.log.warn("Existing Cloudflare D1 database ID was not found. Select a database again.");
11134
11417
  const selectedD1 = await p.select({
11418
+ initialValue: existingD1Database?.uuid ?? availableD1List[0]?.uuid,
11135
11419
  message: "D1 List",
11136
11420
  options: [...availableD1List.map((d1) => ({
11137
11421
  value: d1.uuid,
@@ -11142,43 +11426,149 @@ const runInit = async ({ build }) => {
11142
11426
  }]
11143
11427
  });
11144
11428
  if (p.isCancel(selectedD1)) process.exit(1);
11145
- selectedD1DatabaseId = selectedD1;
11146
- }
11147
- if (selectedD1DatabaseId === createKey) {
11148
- const name = await p.text({ message: "Enter the name of the new D1 Database" });
11149
- if (p.isCancel(name)) process.exit(1);
11150
- const newD1 = await cf.d1.database.create({
11151
- account_id: accountId,
11152
- name
11429
+ if (selectedD1 === createKey) {
11430
+ const prompt = initProvider.inputs.d1DatabaseName.prompt;
11431
+ const name = await p.text({
11432
+ ...getInitProviderTextPromptValues(prompt, existingD1DatabaseName),
11433
+ message: prompt.message,
11434
+ validate: (value) => value ? void 0 : "D1 database name is required"
11435
+ });
11436
+ if (p.isCancel(name)) process.exit(1);
11437
+ createD1Database = true;
11438
+ d1DatabaseName = name;
11439
+ } else {
11440
+ const selectedDatabase = availableD1List.find((d1) => d1.uuid === selectedD1);
11441
+ if (!selectedDatabase) throw new Error("Failed to get D1 Database");
11442
+ selectedD1DatabaseId = selectedDatabase.uuid;
11443
+ d1DatabaseName = selectedDatabase.name;
11444
+ p.log.info(`Selected D1: ${selectedD1DatabaseId}`);
11445
+ }
11446
+ }
11447
+ if (nonInteractive && existingR2AccessKeyId && existingR2SecretAccessKey) p.log.info("Using existing Cloudflare R2 API credentials.");
11448
+ else if (nonInteractive && (existingR2AccessKeyId || existingR2SecretAccessKey)) p.log.warn("Existing Cloudflare R2 API credentials are incomplete.");
11449
+ const { apiToken, accessKeyId, secretAccessKey, workerName } = await inputCloudflareInitSecrets({
11450
+ accountId,
11451
+ bucketName: selectedBucketName,
11452
+ apiToken: existingApiToken,
11453
+ accessKeyId: existingR2AccessKeyId,
11454
+ secretAccessKey: existingR2SecretAccessKey,
11455
+ workerName: existingWorkerName,
11456
+ nonInteractive
11457
+ });
11458
+ const privacyResolution = resolveR2Privacy({
11459
+ createBucket,
11460
+ managedDomainEnabled,
11461
+ savedPrivateSetting
11462
+ });
11463
+ const isPrivate = nonInteractive && privacyResolution.kind === "resolved" ? privacyResolution.isPrivate : await p.confirm({
11464
+ message: initProvider.inputs.r2Private.prompt.message,
11465
+ initialValue: privacyResolution.kind === "resolved" ? privacyResolution.isPrivate : true
11466
+ });
11467
+ if (p.isCancel(isPrivate)) process.exit(1);
11468
+ const databaseCredentialSource = existingApiToken ? envFile ? {
11469
+ envFile,
11470
+ kind: "env-file"
11471
+ } : { kind: "environment" } : { kind: "prompt" };
11472
+ const credentialClient = new Cloudflare({ apiToken });
11473
+ await validateCloudflareApiToken({
11474
+ accountId,
11475
+ probes: [{
11476
+ check: "D1 database access",
11477
+ request: () => credentialClient.d1.database.list({ account_id: accountId }),
11478
+ requiredPermission: CLOUDFLARE_INIT_PERMISSION.d1
11479
+ }],
11480
+ source: databaseCredentialSource,
11481
+ verify: () => verifyCloudflareApiTokenIdentity({
11482
+ accountId,
11483
+ apiToken,
11484
+ verifyAccountToken: (selectedAccountId) => credentialClient.accounts.tokens.verify({ account_id: selectedAccountId }),
11485
+ verifyUserToken: () => credentialClient.user.tokens.verify()
11486
+ })
11487
+ });
11488
+ const resolvedInputs = {
11489
+ ...existingInputs,
11490
+ accessKeyId,
11491
+ accountId,
11492
+ apiToken,
11493
+ bucketName: selectedBucketName,
11494
+ d1DatabaseId: selectedD1DatabaseId,
11495
+ d1DatabaseName,
11496
+ r2Private: String(isPrivate),
11497
+ secretAccessKey,
11498
+ workerName
11499
+ };
11500
+ await makeEnv({ ...getInitProviderEnvVars({
11501
+ includeConsentInputs: await confirmInitInputPersistence({
11502
+ existingEnv: managedEnv,
11503
+ inputs: resolvedInputs,
11504
+ nonInteractive,
11505
+ provider: initProvider
11506
+ }),
11507
+ inputs: resolvedInputs,
11508
+ provider: initProvider
11509
+ }) });
11510
+ if (createBucket) {
11511
+ const newR2 = await runCloudflareApiRequest({
11512
+ request: () => cf.r2.buckets.create({
11513
+ account_id: accountId,
11514
+ name: selectedBucketName
11515
+ }),
11516
+ source: infrastructureCredentialSource
11153
11517
  });
11154
- if (!newD1.uuid || !newD1.name) throw new Error("Failed to create new D1 Database");
11155
- selectedD1DatabaseId = newD1.uuid;
11156
- availableD1List.push({
11157
- name: newD1.name,
11158
- uuid: newD1.uuid
11518
+ if (!newR2.name) throw new Error("Failed to create new R2 Bucket");
11519
+ p.log.info(`Created R2: ${newR2.name}`);
11520
+ managedDomainEnabled = (await runCloudflareApiRequest({
11521
+ request: () => cf.r2.buckets.domains.managed.list(selectedBucketName, { account_id: accountId }),
11522
+ source: infrastructureCredentialSource
11523
+ })).enabled;
11524
+ } else p.log.info(`Selected R2: ${selectedBucketName}`);
11525
+ if (managedDomainEnabled === void 0) throw new Error("Failed to resolve the R2 managed domain state.");
11526
+ if (shouldUpdateR2ManagedDomain({
11527
+ isPrivate,
11528
+ managedDomainEnabled
11529
+ })) await p.tasks([{
11530
+ title: `Making R2 bucket ${isPrivate ? "private" : "public"}...`,
11531
+ task: async () => {
11532
+ await runCloudflareApiRequest({
11533
+ request: () => cf.r2.buckets.domains.managed.update(selectedBucketName, {
11534
+ account_id: accountId,
11535
+ enabled: !isPrivate
11536
+ }),
11537
+ source: infrastructureCredentialSource
11538
+ });
11539
+ }
11540
+ }]);
11541
+ if (createD1Database) {
11542
+ const newD1 = await runCloudflareApiRequest({
11543
+ request: () => cf.d1.database.create({
11544
+ account_id: accountId,
11545
+ name: d1DatabaseName
11546
+ }),
11547
+ source: infrastructureCredentialSource
11159
11548
  });
11160
- p.log.info(`Created new D1 Database: ${newD1.name} (${newD1.uuid})`);
11161
- } else p.log.info(`Selected D1: ${selectedD1DatabaseId}`);
11162
- const d1DatabaseName = availableD1List.find((d1) => d1.uuid === selectedD1DatabaseId)?.name;
11163
- if (!d1DatabaseName) throw new Error("Failed to get D1 Database name");
11164
- const subdomains = await cf.workers.subdomains.get({ account_id: accountId });
11165
- const existingWorkerName = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_WORKER_NAME");
11166
- const workerName = await deployWorker(auth.oauth_token, accountId, {
11549
+ if (!newD1.uuid || !newD1.name) throw new Error("Failed to create the requested D1 Database");
11550
+ selectedD1DatabaseId = newD1.uuid;
11551
+ d1DatabaseName = newD1.name;
11552
+ p.log.info(`Created D1 Database: ${newD1.name} (${newD1.uuid})`);
11553
+ }
11554
+ if (!selectedD1DatabaseId) throw new Error("Failed to resolve the D1 Database");
11555
+ await makeEnv({
11556
+ [initProvider.inputs.d1DatabaseId.envKey]: selectedD1DatabaseId,
11557
+ [initProvider.inputs.d1DatabaseName.envKey]: d1DatabaseName
11558
+ });
11559
+ const subdomains = await runCloudflareApiRequest({
11560
+ request: () => cf.workers.subdomains.get({ account_id: accountId }),
11561
+ source: infrastructureCredentialSource
11562
+ });
11563
+ await deployWorker(infrastructureApiToken, accountId, {
11564
+ credentialSource: infrastructureCredentialSource,
11167
11565
  d1DatabaseId: selectedD1DatabaseId,
11168
11566
  d1DatabaseName,
11567
+ nonInteractive,
11169
11568
  r2BucketName: selectedBucketName,
11170
- workerName: existingWorkerName
11569
+ workerName
11171
11570
  });
11172
11571
  const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build));
11173
- await makeEnv({
11174
- HOT_UPDATER_CLOUDFLARE_API_TOKEN: apiToken,
11175
- HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID: accountId,
11176
- HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME: selectedBucketName,
11177
- HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID: r2AccessKeyId,
11178
- HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY: r2SecretAccessKey,
11179
- HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID: selectedD1DatabaseId,
11180
- HOT_UPDATER_CLOUDFLARE_WORKER_NAME: workerName
11181
- });
11182
11572
  p.log.success("Generated '.env.hotupdater' file with Cloudflare settings.");
11183
11573
  if (configWriteResult.status === "created") p.log.success("Generated 'hot-updater.config.ts' file with Cloudflare settings.");
11184
11574
  else if (configWriteResult.status === "merged") p.log.success("Updated 'hot-updater.config.ts' file with Cloudflare settings.");