@hot-updater/cloudflare 1.2.0-rc.4 → 1.2.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -86,7 +86,12 @@ const encode = ({ sql, params }) => ({
86
86
  * time, or a batch D1 runs in one transaction.
87
87
  */
88
88
  const d1RestRunner = (config) => {
89
- const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
89
+ const { cloudflareApiToken } = config;
90
+ let client;
91
+ const connect = () => client ??= (async () => new cloudflare.default({ apiToken: typeof cloudflareApiToken === "function" ? await cloudflareApiToken() : cloudflareApiToken }))().catch((error) => {
92
+ client = void 0;
93
+ throw error;
94
+ });
90
95
  const batch = async (statements) => {
91
96
  const [first] = statements;
92
97
  if (first === void 0) return [];
@@ -97,7 +102,7 @@ const d1RestRunner = (config) => {
97
102
  account_id: config.accountId,
98
103
  batch: statements.map(encode)
99
104
  };
100
- const page = await cloudflare$1.d1.database.query(config.databaseId, body);
105
+ const page = await (await connect()).d1.database.query(config.databaseId, body);
101
106
  const results = [];
102
107
  for await (const resultPage of page.iterPages()) for (const result of resultPage.result) results.push(toSqlResult(result));
103
108
  if (results.length !== statements.length) throw new D1ExecutionError();
package/dist/index.d.cts CHANGED
@@ -4,7 +4,12 @@ import { S3ClientConfig } from "@aws-sdk/client-s3";
4
4
  interface D1RestConfig {
5
5
  readonly databaseId: string;
6
6
  readonly accountId: string;
7
- readonly cloudflareApiToken: string;
7
+ /**
8
+ * An API token with D1 edit permission, or a function that returns it,
9
+ * such as one that reads it with `secretEnv`: called on the first query,
10
+ * so a config loads without the token.
11
+ */
12
+ readonly cloudflareApiToken: string | (() => string | Promise<string>);
8
13
  }
9
14
  //#endregion
10
15
  //#region src/d1Executor.d.ts
package/dist/index.d.mts CHANGED
@@ -4,7 +4,12 @@ import { S3ClientConfig } from "@aws-sdk/client-s3";
4
4
  interface D1RestConfig {
5
5
  readonly databaseId: string;
6
6
  readonly accountId: string;
7
- readonly cloudflareApiToken: string;
7
+ /**
8
+ * An API token with D1 edit permission, or a function that returns it,
9
+ * such as one that reads it with `secretEnv`: called on the first query,
10
+ * so a config loads without the token.
11
+ */
12
+ readonly cloudflareApiToken: string | (() => string | Promise<string>);
8
13
  }
9
14
  //#endregion
10
15
  //#region src/d1Executor.d.ts
package/dist/index.mjs CHANGED
@@ -62,7 +62,12 @@ const encode = ({ sql, params }) => ({
62
62
  * time, or a batch D1 runs in one transaction.
63
63
  */
64
64
  const d1RestRunner = (config) => {
65
- const cloudflare = new Cloudflare({ apiToken: config.cloudflareApiToken });
65
+ const { cloudflareApiToken } = config;
66
+ let client;
67
+ const connect = () => client ??= (async () => new Cloudflare({ apiToken: typeof cloudflareApiToken === "function" ? await cloudflareApiToken() : cloudflareApiToken }))().catch((error) => {
68
+ client = void 0;
69
+ throw error;
70
+ });
66
71
  const batch = async (statements) => {
67
72
  const [first] = statements;
68
73
  if (first === void 0) return [];
@@ -73,7 +78,7 @@ const d1RestRunner = (config) => {
73
78
  account_id: config.accountId,
74
79
  batch: statements.map(encode)
75
80
  };
76
- const page = await cloudflare.d1.database.query(config.databaseId, body);
81
+ const page = await (await connect()).d1.database.query(config.databaseId, body);
77
82
  const results = [];
78
83
  for await (const resultPage of page.iterPages()) for (const result of resultPage.result) results.push(toSqlResult(result));
79
84
  if (results.length !== statements.length) throw new D1ExecutionError();
@@ -113,7 +113,12 @@ const encode = ({ sql, params }) => ({
113
113
  * time, or a batch D1 runs in one transaction.
114
114
  */
115
115
  const d1RestRunner = (config) => {
116
- const cloudflare$1 = new cloudflare.default({ apiToken: config.cloudflareApiToken });
116
+ const { cloudflareApiToken } = config;
117
+ let client;
118
+ const connect = () => client ??= (async () => new cloudflare.default({ apiToken: typeof cloudflareApiToken === "function" ? await cloudflareApiToken() : cloudflareApiToken }))().catch((error) => {
119
+ client = void 0;
120
+ throw error;
121
+ });
117
122
  const batch = async (statements) => {
118
123
  const [first] = statements;
119
124
  if (first === void 0) return [];
@@ -124,7 +129,7 @@ const d1RestRunner = (config) => {
124
129
  account_id: config.accountId,
125
130
  batch: statements.map(encode)
126
131
  };
127
- const page = await cloudflare$1.d1.database.query(config.databaseId, body);
132
+ const page = await (await connect()).d1.database.query(config.databaseId, body);
128
133
  const results = [];
129
134
  for await (const resultPage of page.iterPages()) for (const result of resultPage.result) results.push(toSqlResult(result));
130
135
  if (results.length !== statements.length) throw new D1ExecutionError();
@@ -11320,7 +11325,12 @@ const initProvider = {
11320
11325
  },
11321
11326
  validate: (value) => value === "true" || value === "false"
11322
11327
  }
11323
- }
11328
+ },
11329
+ secrets: [
11330
+ "HOT_UPDATER_CLOUDFLARE_API_TOKEN",
11331
+ "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID",
11332
+ "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"
11333
+ ]
11324
11334
  };
11325
11335
  //#endregion
11326
11336
  //#region iac/cloudflareInitInputs.ts
@@ -11408,8 +11418,26 @@ const inputCloudflareInitSecrets = async ({ accountId, bucketName, apiToken, acc
11408
11418
  };
11409
11419
  //#endregion
11410
11420
  //#region iac/configTemplate.ts
11411
- const getConfigScaffold = (build) => {
11412
- return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage({
11421
+ /**
11422
+ * The config init writes. With the `resources` init set up, it names them,
11423
+ * and reads the API token and R2 keys with `secretEnv`, from the
11424
+ * environment and `.env.hotupdater`, then the config's `secrets` adapter.
11425
+ * Without them, as the agent scaffold writes it, it reads every value from
11426
+ * the environment.
11427
+ */
11428
+ const getConfigScaffold = (build, resources) => {
11429
+ const builder = new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setPlugins({
11430
+ imports: [{
11431
+ pkg: "hot-updater/plugins",
11432
+ named: [
11433
+ "apiKeys",
11434
+ "insights",
11435
+ "remoteConfig"
11436
+ ]
11437
+ }],
11438
+ configString: "[apiKeys(), insights(), remoteConfig()]"
11439
+ });
11440
+ if (resources === void 0) return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder.setStorage({
11413
11441
  imports: [{
11414
11442
  pkg: "@hot-updater/cloudflare",
11415
11443
  named: ["r2Storage"]
@@ -11432,17 +11460,42 @@ const getConfigScaffold = (build) => {
11432
11460
  accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
11433
11461
  cloudflareApiToken: process.env.HOT_UPDATER_CLOUDFLARE_API_TOKEN!,
11434
11462
  })`
11435
- }).setPlugins({
11463
+ }));
11464
+ const accountId = JSON.stringify(resources.accountId);
11465
+ const storageConfig = {
11436
11466
  imports: [{
11437
- pkg: "hot-updater/plugins",
11438
- named: [
11439
- "apiKeys",
11440
- "insights",
11441
- "remoteConfig"
11442
- ]
11467
+ pkg: "@hot-updater/cloudflare",
11468
+ named: ["r2Storage"]
11443
11469
  }],
11444
- configString: "[apiKeys(), insights(), remoteConfig()]"
11445
- }));
11470
+ configString: `r2Storage({
11471
+ bucketName: ${JSON.stringify(resources.bucketName)},
11472
+ accountId: ${accountId},
11473
+ credentials: async () => {
11474
+ const env = await storageSecrets();
11475
+ return {
11476
+ accessKeyId: env.HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID,
11477
+ secretAccessKey: env.HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY,
11478
+ };
11479
+ },
11480
+ })`
11481
+ };
11482
+ const databaseConfig = {
11483
+ imports: [{
11484
+ pkg: "@hot-updater/cloudflare",
11485
+ named: ["d1Database"]
11486
+ }],
11487
+ configString: `d1Database({
11488
+ databaseId: ${JSON.stringify(resources.d1DatabaseId)},
11489
+ accountId: ${accountId},
11490
+ cloudflareApiToken: async () =>
11491
+ (await databaseSecrets()).HOT_UPDATER_CLOUDFLARE_API_TOKEN,
11492
+ })`
11493
+ };
11494
+ const helperStatements = [(0, _hot_updater_cli_tools.secretEnvHelper)("storageSecrets", ["HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID", "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"]), (0, _hot_updater_cli_tools.secretEnvHelper)("databaseSecrets", ["HOT_UPDATER_CLOUDFLARE_API_TOKEN"])];
11495
+ return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder.setStorage(storageConfig).setDatabase(databaseConfig).addImport({
11496
+ pkg: "hot-updater",
11497
+ named: ["secretEnv"]
11498
+ }).setIntermediateCode(helperStatements.map(({ code }) => code).join("\n\n")), { helperStatements });
11446
11499
  };
11447
11500
  //#endregion
11448
11501
  //#region iac/managedWorker.ts
@@ -11514,10 +11567,10 @@ const deployWorker = async (apiToken, accountId, { credentialSource, d1DatabaseI
11514
11567
  await removeTmpDir();
11515
11568
  }
11516
11569
  };
11517
- const runInit = async ({ build, envFile }) => {
11570
+ const runInit = async ({ build, envFile, secretsEnv }) => {
11518
11571
  const cwd = (0, _hot_updater_cli_tools.getCwd)();
11519
11572
  const nonInteractive = envFile !== void 0;
11520
- const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(cwd, envFile);
11573
+ const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(cwd, envFile, secretsEnv);
11521
11574
  const { managedEnv } = initEnvSources;
11522
11575
  const initInputEnv = (0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive);
11523
11576
  const existingInputs = resolveCloudflareInitInputs(initInputEnv);
@@ -11867,7 +11920,11 @@ const runInit = async ({ build, envFile }) => {
11867
11920
  await database.dispose?.();
11868
11921
  }
11869
11922
  _hot_updater_cli_tools.p.log.success("Generated '.env.hotupdater' file with Cloudflare settings.");
11870
- await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build), {
11923
+ await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build, {
11924
+ accountId,
11925
+ bucketName: selectedBucketName,
11926
+ d1DatabaseId: selectedD1DatabaseId
11927
+ }), {
11871
11928
  cwd,
11872
11929
  settings: "Cloudflare"
11873
11930
  });
@@ -82,9 +82,10 @@ declare const initProvider: {
82
82
  readonly validate: (value: string | undefined) => value is "true" | "false";
83
83
  };
84
84
  };
85
+ readonly secrets: readonly ["HOT_UPDATER_CLOUDFLARE_API_TOKEN", "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID", "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"];
85
86
  };
86
87
  //#endregion
87
88
  //#region iac/index.d.ts
88
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
89
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
89
90
  //#endregion
90
91
  export { initProvider, runInit };
@@ -82,9 +82,10 @@ declare const initProvider: {
82
82
  readonly validate: (value: string | undefined) => value is "true" | "false";
83
83
  };
84
84
  };
85
+ readonly secrets: readonly ["HOT_UPDATER_CLOUDFLARE_API_TOKEN", "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID", "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"];
85
86
  };
86
87
  //#endregion
87
88
  //#region iac/index.d.ts
88
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
89
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
89
90
  //#endregion
90
91
  export { initProvider, runInit };
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import path from "path";
3
- import { ConfigBuilder, InitError, LegacyInfrastructureError, assembleServer, assertInfrastructureGenerationAtUrl, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, resolveInitProviderInput, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
3
+ import { ConfigBuilder, InitError, LegacyInfrastructureError, assembleServer, assertInfrastructureGenerationAtUrl, assertInitInputs, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, resolveInitProviderInput, secretEnvHelper, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
4
4
  import { apiKeys, insights, remoteConfig } from "@hot-updater/server/plugins";
5
5
  import Cloudflare$1, { Cloudflare } from "cloudflare";
6
6
  import { WRITE_GUARD_TABLE, coreSettings, coreTarget, createEngineDatabase, createSqlAdapter, createTableStatements, generateEngineSql } from "@hot-updater/plugin-core";
@@ -106,7 +106,12 @@ const encode = ({ sql, params }) => ({
106
106
  * time, or a batch D1 runs in one transaction.
107
107
  */
108
108
  const d1RestRunner = (config) => {
109
- const cloudflare = new Cloudflare$1({ apiToken: config.cloudflareApiToken });
109
+ const { cloudflareApiToken } = config;
110
+ let client;
111
+ const connect = () => client ??= (async () => new Cloudflare$1({ apiToken: typeof cloudflareApiToken === "function" ? await cloudflareApiToken() : cloudflareApiToken }))().catch((error) => {
112
+ client = void 0;
113
+ throw error;
114
+ });
110
115
  const batch = async (statements) => {
111
116
  const [first] = statements;
112
117
  if (first === void 0) return [];
@@ -117,7 +122,7 @@ const d1RestRunner = (config) => {
117
122
  account_id: config.accountId,
118
123
  batch: statements.map(encode)
119
124
  };
120
- const page = await cloudflare.d1.database.query(config.databaseId, body);
125
+ const page = await (await connect()).d1.database.query(config.databaseId, body);
121
126
  const results = [];
122
127
  for await (const resultPage of page.iterPages()) for (const result of resultPage.result) results.push(toSqlResult(result));
123
128
  if (results.length !== statements.length) throw new D1ExecutionError();
@@ -11313,7 +11318,12 @@ const initProvider = {
11313
11318
  },
11314
11319
  validate: (value) => value === "true" || value === "false"
11315
11320
  }
11316
- }
11321
+ },
11322
+ secrets: [
11323
+ "HOT_UPDATER_CLOUDFLARE_API_TOKEN",
11324
+ "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID",
11325
+ "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"
11326
+ ]
11317
11327
  };
11318
11328
  //#endregion
11319
11329
  //#region iac/cloudflareInitInputs.ts
@@ -11401,8 +11411,26 @@ const inputCloudflareInitSecrets = async ({ accountId, bucketName, apiToken, acc
11401
11411
  };
11402
11412
  //#endregion
11403
11413
  //#region iac/configTemplate.ts
11404
- const getConfigScaffold = (build) => {
11405
- return createHotUpdaterConfigScaffoldFromBuilder(new ConfigBuilder().setBuildType(build).setStorage({
11414
+ /**
11415
+ * The config init writes. With the `resources` init set up, it names them,
11416
+ * and reads the API token and R2 keys with `secretEnv`, from the
11417
+ * environment and `.env.hotupdater`, then the config's `secrets` adapter.
11418
+ * Without them, as the agent scaffold writes it, it reads every value from
11419
+ * the environment.
11420
+ */
11421
+ const getConfigScaffold = (build, resources) => {
11422
+ const builder = new ConfigBuilder().setBuildType(build).setPlugins({
11423
+ imports: [{
11424
+ pkg: "hot-updater/plugins",
11425
+ named: [
11426
+ "apiKeys",
11427
+ "insights",
11428
+ "remoteConfig"
11429
+ ]
11430
+ }],
11431
+ configString: "[apiKeys(), insights(), remoteConfig()]"
11432
+ });
11433
+ if (resources === void 0) return createHotUpdaterConfigScaffoldFromBuilder(builder.setStorage({
11406
11434
  imports: [{
11407
11435
  pkg: "@hot-updater/cloudflare",
11408
11436
  named: ["r2Storage"]
@@ -11425,17 +11453,42 @@ const getConfigScaffold = (build) => {
11425
11453
  accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
11426
11454
  cloudflareApiToken: process.env.HOT_UPDATER_CLOUDFLARE_API_TOKEN!,
11427
11455
  })`
11428
- }).setPlugins({
11456
+ }));
11457
+ const accountId = JSON.stringify(resources.accountId);
11458
+ const storageConfig = {
11429
11459
  imports: [{
11430
- pkg: "hot-updater/plugins",
11431
- named: [
11432
- "apiKeys",
11433
- "insights",
11434
- "remoteConfig"
11435
- ]
11460
+ pkg: "@hot-updater/cloudflare",
11461
+ named: ["r2Storage"]
11436
11462
  }],
11437
- configString: "[apiKeys(), insights(), remoteConfig()]"
11438
- }));
11463
+ configString: `r2Storage({
11464
+ bucketName: ${JSON.stringify(resources.bucketName)},
11465
+ accountId: ${accountId},
11466
+ credentials: async () => {
11467
+ const env = await storageSecrets();
11468
+ return {
11469
+ accessKeyId: env.HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID,
11470
+ secretAccessKey: env.HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY,
11471
+ };
11472
+ },
11473
+ })`
11474
+ };
11475
+ const databaseConfig = {
11476
+ imports: [{
11477
+ pkg: "@hot-updater/cloudflare",
11478
+ named: ["d1Database"]
11479
+ }],
11480
+ configString: `d1Database({
11481
+ databaseId: ${JSON.stringify(resources.d1DatabaseId)},
11482
+ accountId: ${accountId},
11483
+ cloudflareApiToken: async () =>
11484
+ (await databaseSecrets()).HOT_UPDATER_CLOUDFLARE_API_TOKEN,
11485
+ })`
11486
+ };
11487
+ const helperStatements = [secretEnvHelper("storageSecrets", ["HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID", "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"]), secretEnvHelper("databaseSecrets", ["HOT_UPDATER_CLOUDFLARE_API_TOKEN"])];
11488
+ return createHotUpdaterConfigScaffoldFromBuilder(builder.setStorage(storageConfig).setDatabase(databaseConfig).addImport({
11489
+ pkg: "hot-updater",
11490
+ named: ["secretEnv"]
11491
+ }).setIntermediateCode(helperStatements.map(({ code }) => code).join("\n\n")), { helperStatements });
11439
11492
  };
11440
11493
  //#endregion
11441
11494
  //#region iac/managedWorker.ts
@@ -11507,10 +11560,10 @@ const deployWorker = async (apiToken, accountId, { credentialSource, d1DatabaseI
11507
11560
  await removeTmpDir();
11508
11561
  }
11509
11562
  };
11510
- const runInit = async ({ build, envFile }) => {
11563
+ const runInit = async ({ build, envFile, secretsEnv }) => {
11511
11564
  const cwd = getCwd();
11512
11565
  const nonInteractive = envFile !== void 0;
11513
- const initEnvSources = await readHotUpdaterInitEnv(cwd, envFile);
11566
+ const initEnvSources = await readHotUpdaterInitEnv(cwd, envFile, secretsEnv);
11514
11567
  const { managedEnv } = initEnvSources;
11515
11568
  const initInputEnv = getHotUpdaterInitInputEnv(initEnvSources, nonInteractive);
11516
11569
  const existingInputs = resolveCloudflareInitInputs(initInputEnv);
@@ -11860,7 +11913,11 @@ const runInit = async ({ build, envFile }) => {
11860
11913
  await database.dispose?.();
11861
11914
  }
11862
11915
  p.log.success("Generated '.env.hotupdater' file with Cloudflare settings.");
11863
- await writeHotUpdaterFiles(getConfigScaffold(build), {
11916
+ await writeHotUpdaterFiles(getConfigScaffold(build, {
11917
+ accountId,
11918
+ bucketName: selectedBucketName,
11919
+ d1DatabaseId: selectedD1DatabaseId
11920
+ }), {
11864
11921
  cwd,
11865
11922
  settings: "Cloudflare"
11866
11923
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/cloudflare",
3
3
  "type": "module",
4
- "version": "1.2.0-rc.4",
4
+ "version": "1.2.0-rc.5",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -55,10 +55,10 @@
55
55
  "cloudflare": "4.2.0",
56
56
  "hono": "4.12.34",
57
57
  "uuidv7": "^1.0.2",
58
- "@hot-updater/protocol": "1.2.0-rc.4",
59
- "@hot-updater/plugin-core": "1.2.0-rc.4",
60
- "@hot-updater/cli-tools": "1.2.0-rc.4",
61
- "@hot-updater/server": "1.2.0-rc.4"
58
+ "@hot-updater/cli-tools": "1.2.0-rc.5",
59
+ "@hot-updater/protocol": "1.2.0-rc.5",
60
+ "@hot-updater/plugin-core": "1.2.0-rc.5",
61
+ "@hot-updater/server": "1.2.0-rc.5"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@cloudflare/vitest-pool-workers": "0.13.0",
@@ -74,7 +74,7 @@
74
74
  "vitest": "4.1.4",
75
75
  "wrangler": "^4.69.0",
76
76
  "xdg-app-paths": "^8.3.0",
77
- "@hot-updater/test-utils": "1.2.0-rc.4"
77
+ "@hot-updater/test-utils": "1.2.0-rc.5"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "tsdown && pnpm build:worker",
package/src/d1Rest.ts CHANGED
@@ -6,7 +6,12 @@ import { D1ExecutionError, type D1ResultLike, toSqlResult } from "./d1Executor";
6
6
  export interface D1RestConfig {
7
7
  readonly databaseId: string;
8
8
  readonly accountId: string;
9
- readonly cloudflareApiToken: string;
9
+ /**
10
+ * An API token with D1 edit permission, or a function that returns it,
11
+ * such as one that reads it with `secretEnv`: called on the first query,
12
+ * so a config loads without the token.
13
+ */
14
+ readonly cloudflareApiToken: string | (() => string | Promise<string>);
10
15
  }
11
16
 
12
17
  /** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
@@ -21,9 +26,19 @@ const encode = ({ sql, params }: SqlStatement) => ({
21
26
  * time, or a batch D1 runs in one transaction.
22
27
  */
23
28
  export const d1RestRunner = (config: D1RestConfig) => {
24
- const cloudflare = new Cloudflare({
25
- apiToken: config.cloudflareApiToken,
26
- });
29
+ const { cloudflareApiToken } = config;
30
+ let client: Promise<Cloudflare> | undefined;
31
+ const connect = () =>
32
+ (client ??= (async () =>
33
+ new Cloudflare({
34
+ apiToken:
35
+ typeof cloudflareApiToken === "function"
36
+ ? await cloudflareApiToken()
37
+ : cloudflareApiToken,
38
+ }))().catch((error: unknown) => {
39
+ client = undefined;
40
+ throw error;
41
+ }));
27
42
  const batch = async (
28
43
  statements: readonly SqlStatement[],
29
44
  ): Promise<SqlResult[]> => {
@@ -33,6 +48,7 @@ export const d1RestRunner = (config: D1RestConfig) => {
33
48
  statements.length === 1
34
49
  ? { account_id: config.accountId, ...encode(first) }
35
50
  : { account_id: config.accountId, batch: statements.map(encode) };
51
+ const cloudflare = await connect();
36
52
  // cloudflare@4 predates the D1 REST API's parameterized batch body type.
37
53
  const page = await cloudflare.d1.database.query(
38
54
  config.databaseId,
@@ -1 +1 @@
1
- This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-28T12:42:02.480Z.
1
+ This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-28T17:24:58.490Z.
@@ -4632,7 +4632,7 @@ var createSqlAdapter = /* @__PURE__ */ __name(({ executor, tablePrefix = "", max
4632
4632
  }, "createSqlAdapter");
4633
4633
 
4634
4634
  // ../../packages/server/dist/package.mjs
4635
- var version = "1.2.0-rc.4";
4635
+ var version = "1.2.0-rc.5";
4636
4636
 
4637
4637
  // ../../packages/server/dist/version.mjs
4638
4638
  var HOT_UPDATER_SERVER_VERSION = version;