@hot-updater/cloudflare 1.2.0-rc.6 → 1.2.0-rc.8
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 +19 -1
- package/dist/index.d.cts +21 -6
- package/dist/index.d.mts +21 -6
- package/dist/index.mjs +20 -2
- package/dist/init/index.cjs +16 -17
- package/dist/init/index.mjs +17 -18
- package/package.json +6 -6
- package/src/d1Rest.ts +4 -4
- package/src/lazyCredentials.ts +45 -0
- package/src/r2S3Storage.ts +7 -2
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +1 -1
- package/worker/dist/index.js.map +1 -1
package/dist/index.cjs
CHANGED
|
@@ -156,6 +156,23 @@ const d1Database = (config) => ({
|
|
|
156
156
|
})
|
|
157
157
|
});
|
|
158
158
|
//#endregion
|
|
159
|
+
//#region src/lazyCredentials.ts
|
|
160
|
+
/**
|
|
161
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
162
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
163
|
+
*/
|
|
164
|
+
const toSdkCredentials = (credentials) => {
|
|
165
|
+
if (credentials === void 0 || typeof credentials === "function") return credentials;
|
|
166
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
167
|
+
if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
|
|
168
|
+
return async () => ({
|
|
169
|
+
...rest,
|
|
170
|
+
accessKeyId: await (0, _hot_updater_plugin_core.resolveLazy)(accessKeyId),
|
|
171
|
+
secretAccessKey: await (0, _hot_updater_plugin_core.resolveLazy)(secretAccessKey),
|
|
172
|
+
...sessionToken === void 0 ? {} : { sessionToken: await (0, _hot_updater_plugin_core.resolveLazy)(sessionToken) }
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
//#endregion
|
|
159
176
|
//#region src/r2S3Storage.ts
|
|
160
177
|
/** Devices download right after the update check that returns the URL. */
|
|
161
178
|
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
@@ -164,9 +181,10 @@ const isObjectNotFoundError = (error) => {
|
|
|
164
181
|
return typeof error === "object" && error !== null && "$metadata" in error && error.$metadata?.httpStatusCode === 404;
|
|
165
182
|
};
|
|
166
183
|
const createR2S3Storage = (config) => {
|
|
167
|
-
const { accountId, basePath, bucketName, endpoint, forcePathStyle, region, ...s3Config } = config;
|
|
184
|
+
const { accountId, basePath, bucketName, endpoint, forcePathStyle, region, credentials, ...s3Config } = config;
|
|
168
185
|
const client = new _aws_sdk_client_s3.S3Client({
|
|
169
186
|
...s3Config,
|
|
187
|
+
credentials: toSdkCredentials(credentials),
|
|
170
188
|
endpoint: endpoint ?? `https://${accountId}.r2.cloudflarestorage.com`,
|
|
171
189
|
forcePathStyle: forcePathStyle ?? true,
|
|
172
190
|
region: region ?? "auto"
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
1
|
+
import { Lazy, StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
2
2
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
3
|
//#region src/d1Rest.d.ts
|
|
4
4
|
interface D1RestConfig {
|
|
@@ -6,10 +6,10 @@ interface D1RestConfig {
|
|
|
6
6
|
readonly accountId: string;
|
|
7
7
|
/**
|
|
8
8
|
* An API token with D1 edit permission, or a function that returns it,
|
|
9
|
-
* such as
|
|
10
|
-
* so a config loads without the token.
|
|
9
|
+
* such as `secret("<name>")` in `hot-updater.config.ts`: called on the
|
|
10
|
+
* first query, so a config loads without the token.
|
|
11
11
|
*/
|
|
12
|
-
readonly cloudflareApiToken:
|
|
12
|
+
readonly cloudflareApiToken: Lazy<string>;
|
|
13
13
|
}
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/d1Executor.d.ts
|
|
@@ -30,11 +30,26 @@ type D1DatabaseConfig = D1RestConfig;
|
|
|
30
30
|
*/
|
|
31
31
|
declare const d1Database: (config: D1DatabaseConfig) => ToolingDatabase;
|
|
32
32
|
//#endregion
|
|
33
|
+
//#region src/lazyCredentials.d.ts
|
|
34
|
+
type SdkCredentials = S3ClientConfig["credentials"];
|
|
35
|
+
/**
|
|
36
|
+
* Access keys whose fields may each be a function that returns them, such
|
|
37
|
+
* as `secret("<name>")` in `hot-updater.config.ts`, called on first use.
|
|
38
|
+
*/
|
|
39
|
+
interface LazyAwsCredentials {
|
|
40
|
+
readonly accessKeyId: Lazy<string>;
|
|
41
|
+
readonly secretAccessKey: Lazy<string>;
|
|
42
|
+
readonly sessionToken?: Lazy<string>;
|
|
43
|
+
}
|
|
44
|
+
/** The credentials the AWS SDK takes, or access keys with lazy fields. */
|
|
45
|
+
type AwsCredentialsOption = SdkCredentials | LazyAwsCredentials;
|
|
46
|
+
//#endregion
|
|
33
47
|
//#region src/r2S3Storage.d.ts
|
|
34
|
-
interface R2S3StorageConfig extends S3ClientConfig {
|
|
48
|
+
interface R2S3StorageConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
35
49
|
accountId: string;
|
|
36
50
|
bucketName: string;
|
|
37
|
-
credentials
|
|
51
|
+
/** The SDK's credentials, or an R2 access key whose fields are lazy. */
|
|
52
|
+
credentials: NonNullable<AwsCredentialsOption>;
|
|
38
53
|
/** Base path where bundles will be stored in the bucket. */
|
|
39
54
|
basePath?: string;
|
|
40
55
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
1
|
+
import { Lazy, StorageAdapterWith, ToolingDatabase } from "@hot-updater/plugin-core";
|
|
2
2
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
3
|
//#region src/d1Rest.d.ts
|
|
4
4
|
interface D1RestConfig {
|
|
@@ -6,10 +6,10 @@ interface D1RestConfig {
|
|
|
6
6
|
readonly accountId: string;
|
|
7
7
|
/**
|
|
8
8
|
* An API token with D1 edit permission, or a function that returns it,
|
|
9
|
-
* such as
|
|
10
|
-
* so a config loads without the token.
|
|
9
|
+
* such as `secret("<name>")` in `hot-updater.config.ts`: called on the
|
|
10
|
+
* first query, so a config loads without the token.
|
|
11
11
|
*/
|
|
12
|
-
readonly cloudflareApiToken:
|
|
12
|
+
readonly cloudflareApiToken: Lazy<string>;
|
|
13
13
|
}
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/d1Executor.d.ts
|
|
@@ -30,11 +30,26 @@ type D1DatabaseConfig = D1RestConfig;
|
|
|
30
30
|
*/
|
|
31
31
|
declare const d1Database: (config: D1DatabaseConfig) => ToolingDatabase;
|
|
32
32
|
//#endregion
|
|
33
|
+
//#region src/lazyCredentials.d.ts
|
|
34
|
+
type SdkCredentials = S3ClientConfig["credentials"];
|
|
35
|
+
/**
|
|
36
|
+
* Access keys whose fields may each be a function that returns them, such
|
|
37
|
+
* as `secret("<name>")` in `hot-updater.config.ts`, called on first use.
|
|
38
|
+
*/
|
|
39
|
+
interface LazyAwsCredentials {
|
|
40
|
+
readonly accessKeyId: Lazy<string>;
|
|
41
|
+
readonly secretAccessKey: Lazy<string>;
|
|
42
|
+
readonly sessionToken?: Lazy<string>;
|
|
43
|
+
}
|
|
44
|
+
/** The credentials the AWS SDK takes, or access keys with lazy fields. */
|
|
45
|
+
type AwsCredentialsOption = SdkCredentials | LazyAwsCredentials;
|
|
46
|
+
//#endregion
|
|
33
47
|
//#region src/r2S3Storage.d.ts
|
|
34
|
-
interface R2S3StorageConfig extends S3ClientConfig {
|
|
48
|
+
interface R2S3StorageConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
35
49
|
accountId: string;
|
|
36
50
|
bucketName: string;
|
|
37
|
-
credentials
|
|
51
|
+
/** The SDK's credentials, or an R2 access key whose fields are lazy. */
|
|
52
|
+
credentials: NonNullable<AwsCredentialsOption>;
|
|
38
53
|
/** Base path where bundles will be stored in the bucket. */
|
|
39
54
|
basePath?: string;
|
|
40
55
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WRITE_GUARD_TABLE, coreSettings, coreTarget, createEngineDatabase, createSqlAdapter, createStorageAdapter, createStorageKeyBuilder, createStorageUri, createTableStatements, generateEngineSql, parseStorageUri } from "@hot-updater/plugin-core";
|
|
1
|
+
import { WRITE_GUARD_TABLE, coreSettings, coreTarget, createEngineDatabase, createSqlAdapter, createStorageAdapter, createStorageKeyBuilder, createStorageUri, createTableStatements, generateEngineSql, parseStorageUri, resolveLazy } from "@hot-updater/plugin-core";
|
|
2
2
|
import Cloudflare from "cloudflare";
|
|
3
3
|
import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
4
4
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
@@ -132,6 +132,23 @@ const d1Database = (config) => ({
|
|
|
132
132
|
})
|
|
133
133
|
});
|
|
134
134
|
//#endregion
|
|
135
|
+
//#region src/lazyCredentials.ts
|
|
136
|
+
/**
|
|
137
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
138
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
139
|
+
*/
|
|
140
|
+
const toSdkCredentials = (credentials) => {
|
|
141
|
+
if (credentials === void 0 || typeof credentials === "function") return credentials;
|
|
142
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
143
|
+
if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
|
|
144
|
+
return async () => ({
|
|
145
|
+
...rest,
|
|
146
|
+
accessKeyId: await resolveLazy(accessKeyId),
|
|
147
|
+
secretAccessKey: await resolveLazy(secretAccessKey),
|
|
148
|
+
...sessionToken === void 0 ? {} : { sessionToken: await resolveLazy(sessionToken) }
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
//#endregion
|
|
135
152
|
//#region src/r2S3Storage.ts
|
|
136
153
|
/** Devices download right after the update check that returns the URL. */
|
|
137
154
|
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
@@ -140,9 +157,10 @@ const isObjectNotFoundError = (error) => {
|
|
|
140
157
|
return typeof error === "object" && error !== null && "$metadata" in error && error.$metadata?.httpStatusCode === 404;
|
|
141
158
|
};
|
|
142
159
|
const createR2S3Storage = (config) => {
|
|
143
|
-
const { accountId, basePath, bucketName, endpoint, forcePathStyle, region, ...s3Config } = config;
|
|
160
|
+
const { accountId, basePath, bucketName, endpoint, forcePathStyle, region, credentials, ...s3Config } = config;
|
|
144
161
|
const client = new S3Client({
|
|
145
162
|
...s3Config,
|
|
163
|
+
credentials: toSdkCredentials(credentials),
|
|
146
164
|
endpoint: endpoint ?? `https://${accountId}.r2.cloudflarestorage.com`,
|
|
147
165
|
forcePathStyle: forcePathStyle ?? true,
|
|
148
166
|
region: region ?? "auto"
|
package/dist/init/index.cjs
CHANGED
|
@@ -11420,10 +11420,11 @@ const inputCloudflareInitSecrets = async ({ accountId, bucketName, apiToken, acc
|
|
|
11420
11420
|
//#region iac/configTemplate.ts
|
|
11421
11421
|
/**
|
|
11422
11422
|
* The config init writes. With the `resources` init set up, it names them,
|
|
11423
|
-
* and reads the API token and R2 keys with `
|
|
11423
|
+
* and reads the API token and R2 keys with `secret()`, from the
|
|
11424
11424
|
* environment and `.env.hotupdater`, then the config's `secrets` adapter.
|
|
11425
|
-
* Without them, as the agent scaffold writes it
|
|
11426
|
-
* the environment
|
|
11425
|
+
* Without them, as the agent scaffold writes it before the agent creates the
|
|
11426
|
+
* resources, it reads their IDs from the environment, where the agent
|
|
11427
|
+
* records them, and the credentials with `secret()` alike.
|
|
11427
11428
|
*/
|
|
11428
11429
|
const getConfigScaffold = (build, resources) => {
|
|
11429
11430
|
const builder = new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setPlugins({
|
|
@@ -11446,8 +11447,8 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11446
11447
|
bucketName: process.env.HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME!,
|
|
11447
11448
|
accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
|
|
11448
11449
|
credentials: {
|
|
11449
|
-
accessKeyId:
|
|
11450
|
-
secretAccessKey:
|
|
11450
|
+
accessKeyId: secret("HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID"),
|
|
11451
|
+
secretAccessKey: secret("HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"),
|
|
11451
11452
|
},
|
|
11452
11453
|
})`
|
|
11453
11454
|
}).setDatabase({
|
|
@@ -11458,8 +11459,11 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11458
11459
|
configString: `d1Database({
|
|
11459
11460
|
databaseId: process.env.HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID!,
|
|
11460
11461
|
accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
|
|
11461
|
-
cloudflareApiToken:
|
|
11462
|
+
cloudflareApiToken: secret("HOT_UPDATER_CLOUDFLARE_API_TOKEN"),
|
|
11462
11463
|
})`
|
|
11464
|
+
}).addImport({
|
|
11465
|
+
pkg: "hot-updater",
|
|
11466
|
+
named: ["secret"]
|
|
11463
11467
|
}));
|
|
11464
11468
|
const accountId = JSON.stringify(resources.accountId);
|
|
11465
11469
|
const storageConfig = {
|
|
@@ -11470,12 +11474,9 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11470
11474
|
configString: `r2Storage({
|
|
11471
11475
|
bucketName: ${JSON.stringify(resources.bucketName)},
|
|
11472
11476
|
accountId: ${accountId},
|
|
11473
|
-
credentials:
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
accessKeyId: env.HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID,
|
|
11477
|
-
secretAccessKey: env.HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY,
|
|
11478
|
-
};
|
|
11477
|
+
credentials: {
|
|
11478
|
+
accessKeyId: secret("HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID"),
|
|
11479
|
+
secretAccessKey: secret("HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"),
|
|
11479
11480
|
},
|
|
11480
11481
|
})`
|
|
11481
11482
|
};
|
|
@@ -11487,15 +11488,13 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11487
11488
|
configString: `d1Database({
|
|
11488
11489
|
databaseId: ${JSON.stringify(resources.d1DatabaseId)},
|
|
11489
11490
|
accountId: ${accountId},
|
|
11490
|
-
cloudflareApiToken:
|
|
11491
|
-
(await databaseSecrets()).HOT_UPDATER_CLOUDFLARE_API_TOKEN,
|
|
11491
|
+
cloudflareApiToken: secret("HOT_UPDATER_CLOUDFLARE_API_TOKEN"),
|
|
11492
11492
|
})`
|
|
11493
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
11494
|
return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder.setStorage(storageConfig).setDatabase(databaseConfig).addImport({
|
|
11496
11495
|
pkg: "hot-updater",
|
|
11497
|
-
named: ["
|
|
11498
|
-
})
|
|
11496
|
+
named: ["secret"]
|
|
11497
|
+
}));
|
|
11499
11498
|
};
|
|
11500
11499
|
//#endregion
|
|
11501
11500
|
//#region iac/managedWorker.ts
|
package/dist/init/index.mjs
CHANGED
|
@@ -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,
|
|
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";
|
|
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";
|
|
@@ -11413,10 +11413,11 @@ const inputCloudflareInitSecrets = async ({ accountId, bucketName, apiToken, acc
|
|
|
11413
11413
|
//#region iac/configTemplate.ts
|
|
11414
11414
|
/**
|
|
11415
11415
|
* The config init writes. With the `resources` init set up, it names them,
|
|
11416
|
-
* and reads the API token and R2 keys with `
|
|
11416
|
+
* and reads the API token and R2 keys with `secret()`, from the
|
|
11417
11417
|
* environment and `.env.hotupdater`, then the config's `secrets` adapter.
|
|
11418
|
-
* Without them, as the agent scaffold writes it
|
|
11419
|
-
* the environment
|
|
11418
|
+
* Without them, as the agent scaffold writes it before the agent creates the
|
|
11419
|
+
* resources, it reads their IDs from the environment, where the agent
|
|
11420
|
+
* records them, and the credentials with `secret()` alike.
|
|
11420
11421
|
*/
|
|
11421
11422
|
const getConfigScaffold = (build, resources) => {
|
|
11422
11423
|
const builder = new ConfigBuilder().setBuildType(build).setPlugins({
|
|
@@ -11439,8 +11440,8 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11439
11440
|
bucketName: process.env.HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME!,
|
|
11440
11441
|
accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
|
|
11441
11442
|
credentials: {
|
|
11442
|
-
accessKeyId:
|
|
11443
|
-
secretAccessKey:
|
|
11443
|
+
accessKeyId: secret("HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID"),
|
|
11444
|
+
secretAccessKey: secret("HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"),
|
|
11444
11445
|
},
|
|
11445
11446
|
})`
|
|
11446
11447
|
}).setDatabase({
|
|
@@ -11451,8 +11452,11 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11451
11452
|
configString: `d1Database({
|
|
11452
11453
|
databaseId: process.env.HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID!,
|
|
11453
11454
|
accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
|
|
11454
|
-
cloudflareApiToken:
|
|
11455
|
+
cloudflareApiToken: secret("HOT_UPDATER_CLOUDFLARE_API_TOKEN"),
|
|
11455
11456
|
})`
|
|
11457
|
+
}).addImport({
|
|
11458
|
+
pkg: "hot-updater",
|
|
11459
|
+
named: ["secret"]
|
|
11456
11460
|
}));
|
|
11457
11461
|
const accountId = JSON.stringify(resources.accountId);
|
|
11458
11462
|
const storageConfig = {
|
|
@@ -11463,12 +11467,9 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11463
11467
|
configString: `r2Storage({
|
|
11464
11468
|
bucketName: ${JSON.stringify(resources.bucketName)},
|
|
11465
11469
|
accountId: ${accountId},
|
|
11466
|
-
credentials:
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
accessKeyId: env.HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID,
|
|
11470
|
-
secretAccessKey: env.HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY,
|
|
11471
|
-
};
|
|
11470
|
+
credentials: {
|
|
11471
|
+
accessKeyId: secret("HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID"),
|
|
11472
|
+
secretAccessKey: secret("HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY"),
|
|
11472
11473
|
},
|
|
11473
11474
|
})`
|
|
11474
11475
|
};
|
|
@@ -11480,15 +11481,13 @@ const getConfigScaffold = (build, resources) => {
|
|
|
11480
11481
|
configString: `d1Database({
|
|
11481
11482
|
databaseId: ${JSON.stringify(resources.d1DatabaseId)},
|
|
11482
11483
|
accountId: ${accountId},
|
|
11483
|
-
cloudflareApiToken:
|
|
11484
|
-
(await databaseSecrets()).HOT_UPDATER_CLOUDFLARE_API_TOKEN,
|
|
11484
|
+
cloudflareApiToken: secret("HOT_UPDATER_CLOUDFLARE_API_TOKEN"),
|
|
11485
11485
|
})`
|
|
11486
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
11487
|
return createHotUpdaterConfigScaffoldFromBuilder(builder.setStorage(storageConfig).setDatabase(databaseConfig).addImport({
|
|
11489
11488
|
pkg: "hot-updater",
|
|
11490
|
-
named: ["
|
|
11491
|
-
})
|
|
11489
|
+
named: ["secret"]
|
|
11490
|
+
}));
|
|
11492
11491
|
};
|
|
11493
11492
|
//#endregion
|
|
11494
11493
|
//#region iac/managedWorker.ts
|
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
|
+
"version": "1.2.0-rc.8",
|
|
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/
|
|
59
|
-
"@hot-updater/
|
|
60
|
-
"@hot-updater/
|
|
61
|
-
"@hot-updater/
|
|
58
|
+
"@hot-updater/cli-tools": "1.2.0-rc.8",
|
|
59
|
+
"@hot-updater/protocol": "1.2.0-rc.8",
|
|
60
|
+
"@hot-updater/server": "1.2.0-rc.8",
|
|
61
|
+
"@hot-updater/plugin-core": "1.2.0-rc.8"
|
|
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.
|
|
77
|
+
"@hot-updater/test-utils": "1.2.0-rc.8"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build": "tsdown && pnpm build:worker",
|
package/src/d1Rest.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SqlResult, SqlStatement } from "@hot-updater/plugin-core";
|
|
1
|
+
import type { Lazy, SqlResult, SqlStatement } from "@hot-updater/plugin-core";
|
|
2
2
|
import Cloudflare from "cloudflare";
|
|
3
3
|
|
|
4
4
|
import { D1ExecutionError, type D1ResultLike, toSqlResult } from "./d1Executor";
|
|
@@ -8,10 +8,10 @@ export interface D1RestConfig {
|
|
|
8
8
|
readonly accountId: string;
|
|
9
9
|
/**
|
|
10
10
|
* An API token with D1 edit permission, or a function that returns it,
|
|
11
|
-
* such as
|
|
12
|
-
* so a config loads without the token.
|
|
11
|
+
* such as `secret("<name>")` in `hot-updater.config.ts`: called on the
|
|
12
|
+
* first query, so a config loads without the token.
|
|
13
13
|
*/
|
|
14
|
-
readonly cloudflareApiToken:
|
|
14
|
+
readonly cloudflareApiToken: Lazy<string>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/** The REST API binds text: each value goes as JSON and is read back with `json_extract`. */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
2
|
+
import { type Lazy, resolveLazy } from "@hot-updater/plugin-core";
|
|
3
|
+
|
|
4
|
+
type SdkCredentials = S3ClientConfig["credentials"];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Access keys whose fields may each be a function that returns them, such
|
|
8
|
+
* as `secret("<name>")` in `hot-updater.config.ts`, called on first use.
|
|
9
|
+
*/
|
|
10
|
+
export interface LazyAwsCredentials {
|
|
11
|
+
readonly accessKeyId: Lazy<string>;
|
|
12
|
+
readonly secretAccessKey: Lazy<string>;
|
|
13
|
+
readonly sessionToken?: Lazy<string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The credentials the AWS SDK takes, or access keys with lazy fields. */
|
|
17
|
+
export type AwsCredentialsOption = SdkCredentials | LazyAwsCredentials;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
21
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
22
|
+
*/
|
|
23
|
+
export const toSdkCredentials = (
|
|
24
|
+
credentials: AwsCredentialsOption | undefined,
|
|
25
|
+
): SdkCredentials => {
|
|
26
|
+
if (credentials === undefined || typeof credentials === "function") {
|
|
27
|
+
return credentials;
|
|
28
|
+
}
|
|
29
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
30
|
+
if (
|
|
31
|
+
typeof accessKeyId !== "function" &&
|
|
32
|
+
typeof secretAccessKey !== "function" &&
|
|
33
|
+
typeof sessionToken !== "function"
|
|
34
|
+
) {
|
|
35
|
+
return credentials as SdkCredentials;
|
|
36
|
+
}
|
|
37
|
+
return async () => ({
|
|
38
|
+
...rest,
|
|
39
|
+
accessKeyId: await resolveLazy(accessKeyId),
|
|
40
|
+
secretAccessKey: await resolveLazy(secretAccessKey),
|
|
41
|
+
...(sessionToken === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { sessionToken: await resolveLazy(sessionToken) }),
|
|
44
|
+
});
|
|
45
|
+
};
|
package/src/r2S3Storage.ts
CHANGED
|
@@ -15,10 +15,13 @@ import {
|
|
|
15
15
|
type StorageAdapterWith,
|
|
16
16
|
} from "@hot-updater/plugin-core";
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
import { type AwsCredentialsOption, toSdkCredentials } from "./lazyCredentials";
|
|
19
|
+
|
|
20
|
+
export interface R2S3StorageConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
19
21
|
accountId: string;
|
|
20
22
|
bucketName: string;
|
|
21
|
-
credentials
|
|
23
|
+
/** The SDK's credentials, or an R2 access key whose fields are lazy. */
|
|
24
|
+
credentials: NonNullable<AwsCredentialsOption>;
|
|
22
25
|
/** Base path where bundles will be stored in the bucket. */
|
|
23
26
|
basePath?: string;
|
|
24
27
|
}
|
|
@@ -54,10 +57,12 @@ export const createR2S3Storage = (
|
|
|
54
57
|
endpoint,
|
|
55
58
|
forcePathStyle,
|
|
56
59
|
region,
|
|
60
|
+
credentials,
|
|
57
61
|
...s3Config
|
|
58
62
|
} = config;
|
|
59
63
|
const client = new S3Client({
|
|
60
64
|
...s3Config,
|
|
65
|
+
credentials: toSdkCredentials(credentials),
|
|
61
66
|
endpoint: endpoint ?? `https://${accountId}.r2.cloudflarestorage.com`,
|
|
62
67
|
forcePathStyle: forcePathStyle ?? true,
|
|
63
68
|
region: region ?? "auto",
|
package/worker/dist/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-
|
|
1
|
+
This folder contains the built output assets for the worker "hot-updater" generated at 2026-10-29T09:59:26.122Z.
|
package/worker/dist/index.js
CHANGED
|
@@ -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.
|
|
4635
|
+
var version = "1.2.0-rc.8";
|
|
4636
4636
|
|
|
4637
4637
|
// ../../packages/server/dist/version.mjs
|
|
4638
4638
|
var HOT_UPDATER_SERVER_VERSION = version;
|