@hot-updater/cloudflare 0.31.4 → 0.32.0
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/iac/index.cjs +206 -92
- package/dist/iac/index.mjs +198 -84
- package/dist/index.cjs +214 -51
- package/dist/index.d.cts +46 -6
- package/dist/index.d.mts +46 -6
- package/dist/index.mjs +209 -47
- package/dist/worker/index.d.cts +4 -3
- package/dist/worker/index.d.mts +3 -2
- package/package.json +10 -7
- package/src/r2S3Storage.ts +197 -0
- package/src/r2Storage.spec.ts +316 -2
- package/src/r2Storage.ts +50 -110
- package/src/r2WranglerStorage.ts +193 -0
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +85 -24
- package/worker/dist/index.js.map +4 -4
- package/worker/src/index.ts +0 -1
package/dist/iac/index.mjs
CHANGED
|
@@ -28,7 +28,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
28
28
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
29
29
|
var __getProtoOf = Object.getPrototypeOf;
|
|
30
30
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
31
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
31
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
32
32
|
var __copyProps = (to, from, except, desc) => {
|
|
33
33
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
34
34
|
key = keys[i];
|
|
@@ -10838,7 +10838,10 @@ const getConfigScaffold = (build) => {
|
|
|
10838
10838
|
configString: `r2Storage({
|
|
10839
10839
|
bucketName: process.env.HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME!,
|
|
10840
10840
|
accountId: process.env.HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID!,
|
|
10841
|
-
|
|
10841
|
+
credentials: {
|
|
10842
|
+
accessKeyId: process.env.HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID!,
|
|
10843
|
+
secretAccessKey: process.env.HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY!,
|
|
10844
|
+
},
|
|
10842
10845
|
})`
|
|
10843
10846
|
}).setDatabase({
|
|
10844
10847
|
imports: [{
|
|
@@ -10863,7 +10866,50 @@ export default HotUpdater.wrap({
|
|
|
10863
10866
|
baseURL: "%%source%%",
|
|
10864
10867
|
updateStrategy: "appVersion", // or "fingerprint"
|
|
10865
10868
|
})(App);`;
|
|
10866
|
-
const
|
|
10869
|
+
const HOT_UPDATER_ENV_PATH = ".env.hotupdater";
|
|
10870
|
+
const unquoteEnvValue = (value) => {
|
|
10871
|
+
const trimmed = value.trim();
|
|
10872
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
|
|
10873
|
+
return trimmed;
|
|
10874
|
+
};
|
|
10875
|
+
const readHotUpdaterEnv = async (cwd) => {
|
|
10876
|
+
const envPath = path.join(cwd, HOT_UPDATER_ENV_PATH);
|
|
10877
|
+
const content = await fs.readFile(envPath, "utf-8").catch(() => "");
|
|
10878
|
+
const env = {};
|
|
10879
|
+
for (const line of content.split("\n")) {
|
|
10880
|
+
const trimmed = line.trim();
|
|
10881
|
+
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
10882
|
+
const [key, ...valueParts] = trimmed.split("=");
|
|
10883
|
+
if (!key) continue;
|
|
10884
|
+
env[key.trim()] = unquoteEnvValue(valueParts.join("="));
|
|
10885
|
+
}
|
|
10886
|
+
return env;
|
|
10887
|
+
};
|
|
10888
|
+
const getEnvValue = (env, key) => {
|
|
10889
|
+
return process.env[key]?.trim() || env[key]?.trim() || void 0;
|
|
10890
|
+
};
|
|
10891
|
+
const inputR2ApiCredentials = async ({ accountId, bucketName, accessKeyId, secretAccessKey }) => {
|
|
10892
|
+
p.log.step(`R2 API Tokens dashboard: ${link(`https://dash.cloudflare.com/${accountId}/r2/api-tokens`)}`);
|
|
10893
|
+
p.log.step("Required permission: Object Read & Write");
|
|
10894
|
+
p.log.step(`Target bucket: ${bucketName}`);
|
|
10895
|
+
let resolvedAccessKeyId = accessKeyId;
|
|
10896
|
+
if (!resolvedAccessKeyId) {
|
|
10897
|
+
const inputR2AccessKeyId = await p.password({ message: "Enter the R2 Access Key ID" });
|
|
10898
|
+
if (p.isCancel(inputR2AccessKeyId)) process.exit(1);
|
|
10899
|
+
resolvedAccessKeyId = inputR2AccessKeyId;
|
|
10900
|
+
}
|
|
10901
|
+
let resolvedSecretAccessKey = secretAccessKey;
|
|
10902
|
+
if (!resolvedSecretAccessKey) {
|
|
10903
|
+
const inputR2SecretAccessKey = await p.password({ message: "Enter the R2 Secret Access Key" });
|
|
10904
|
+
if (p.isCancel(inputR2SecretAccessKey)) process.exit(1);
|
|
10905
|
+
resolvedSecretAccessKey = inputR2SecretAccessKey;
|
|
10906
|
+
}
|
|
10907
|
+
return {
|
|
10908
|
+
accessKeyId: resolvedAccessKeyId,
|
|
10909
|
+
secretAccessKey: resolvedSecretAccessKey
|
|
10910
|
+
};
|
|
10911
|
+
};
|
|
10912
|
+
const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseName, r2BucketName, workerName }) => {
|
|
10867
10913
|
const cwd = getCwd();
|
|
10868
10914
|
const cloudflarePackagePath = __require.resolve("@hot-updater/cloudflare/package.json", { paths: [cwd] });
|
|
10869
10915
|
const { tmpDir, removeTmpDir } = await copyDirToTmp(path.dirname(cloudflarePackagePath));
|
|
@@ -10895,14 +10941,19 @@ const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseNa
|
|
|
10895
10941
|
await fs.writeFile(filePath, transformTemplate(content, { BUCKET_NAME: r2BucketName }));
|
|
10896
10942
|
}
|
|
10897
10943
|
await wrangler("d1", "migrations", "apply", d1DatabaseName, "--remote");
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10944
|
+
let resolvedWorkerName = workerName;
|
|
10945
|
+
if (resolvedWorkerName) p.log.info("Using existing Cloudflare Worker name.");
|
|
10946
|
+
else {
|
|
10947
|
+
const inputWorkerName = await p.text({
|
|
10948
|
+
message: "Enter the name of the worker",
|
|
10949
|
+
defaultValue: "hot-updater",
|
|
10950
|
+
placeholder: "hot-updater"
|
|
10951
|
+
});
|
|
10952
|
+
if (p.isCancel(inputWorkerName)) process.exit(1);
|
|
10953
|
+
resolvedWorkerName = inputWorkerName;
|
|
10954
|
+
}
|
|
10955
|
+
await wrangler("deploy", "--name", resolvedWorkerName);
|
|
10956
|
+
return resolvedWorkerName;
|
|
10906
10957
|
} catch (error) {
|
|
10907
10958
|
throw new Error("Failed to deploy worker", { cause: error });
|
|
10908
10959
|
} finally {
|
|
@@ -10911,6 +10962,7 @@ const deployWorker = async (oauth_token, accountId, { d1DatabaseId, d1DatabaseNa
|
|
|
10911
10962
|
};
|
|
10912
10963
|
const runInit = async ({ build }) => {
|
|
10913
10964
|
const cwd = getCwd();
|
|
10965
|
+
const existingEnv = await readHotUpdaterEnv(cwd);
|
|
10914
10966
|
let auth = getWranglerLoginAuthToken();
|
|
10915
10967
|
if (!auth || (0, import_dayjs_min.default)(auth?.expiration_time).isBefore((0, import_dayjs_min.default)())) {
|
|
10916
10968
|
await execa("npx", [
|
|
@@ -10928,70 +10980,117 @@ const runInit = async ({ build }) => {
|
|
|
10928
10980
|
if (!auth) throw new Error("'npx wrangler login' is required to use this command");
|
|
10929
10981
|
const cf = new Cloudflare({ apiToken: auth.oauth_token });
|
|
10930
10982
|
const createKey = `create/${Math.random().toString(36).substring(2, 15)}`;
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10983
|
+
let accountId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID");
|
|
10984
|
+
if (accountId) p.log.info("Using existing Cloudflare account ID.");
|
|
10985
|
+
else {
|
|
10986
|
+
const accounts = [];
|
|
10987
|
+
try {
|
|
10988
|
+
await p.tasks([{
|
|
10989
|
+
title: "Checking Account List...",
|
|
10990
|
+
task: async () => {
|
|
10991
|
+
accounts.push(...(await cf.accounts.list()).result.map((account) => ({
|
|
10992
|
+
id: account.id,
|
|
10993
|
+
name: account.name
|
|
10994
|
+
})));
|
|
10995
|
+
}
|
|
10996
|
+
}]);
|
|
10997
|
+
} catch (e) {
|
|
10998
|
+
if (e instanceof Error) p.log.error(e.message);
|
|
10999
|
+
throw e;
|
|
11000
|
+
}
|
|
11001
|
+
const selectedAccountId = await p.select({
|
|
11002
|
+
message: "Account List",
|
|
11003
|
+
options: accounts.map((account) => ({
|
|
11004
|
+
value: account.id,
|
|
11005
|
+
label: `${account.name} (${account.id})`
|
|
11006
|
+
}))
|
|
11007
|
+
});
|
|
11008
|
+
if (p.isCancel(selectedAccountId)) process.exit(1);
|
|
11009
|
+
accountId = selectedAccountId;
|
|
10945
11010
|
}
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
11011
|
+
let apiToken = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_API_TOKEN");
|
|
11012
|
+
if (apiToken) p.log.info("Using existing Cloudflare API token.");
|
|
11013
|
+
else {
|
|
11014
|
+
p.log.step(`D1 API Token dashboard: ${link(`https://dash.cloudflare.com/${accountId}/api-tokens`)}`);
|
|
11015
|
+
p.log.step("Required permission: D1 Edit");
|
|
11016
|
+
p.log.step("Used for bundle metadata writes after init.");
|
|
11017
|
+
const inputApiToken = await p.password({ message: "Enter the D1 API Token" });
|
|
11018
|
+
if (p.isCancel(inputApiToken)) process.exit(1);
|
|
11019
|
+
apiToken = inputApiToken;
|
|
11020
|
+
if (!apiToken) p.log.warn("Skipping API Token. You can set it later in .env HOT_UPDATER_CLOUDFLARE_API_TOKEN file.");
|
|
11021
|
+
}
|
|
11022
|
+
const existingBucketName = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME");
|
|
11023
|
+
let selectedBucketName;
|
|
11024
|
+
if (existingBucketName) {
|
|
11025
|
+
selectedBucketName = existingBucketName;
|
|
11026
|
+
p.log.info("Using existing Cloudflare R2 bucket name.");
|
|
11027
|
+
} else {
|
|
11028
|
+
const availableBuckets = [];
|
|
11029
|
+
try {
|
|
11030
|
+
await p.tasks([{
|
|
11031
|
+
title: "Checking R2 Buckets...",
|
|
11032
|
+
task: async () => {
|
|
11033
|
+
const buckets = (await cf.r2.buckets.list({ account_id: accountId })).buckets ?? [];
|
|
11034
|
+
availableBuckets.push(...buckets.filter((bucket) => bucket.name).map((bucket) => ({ name: bucket.name })));
|
|
11035
|
+
}
|
|
11036
|
+
}]);
|
|
11037
|
+
} catch (e) {
|
|
11038
|
+
if (e instanceof Error) p.log.error(e.message);
|
|
11039
|
+
throw e;
|
|
11040
|
+
}
|
|
11041
|
+
if (availableBuckets.length === 1) {
|
|
11042
|
+
selectedBucketName = availableBuckets[0].name;
|
|
11043
|
+
p.log.info("Using the only Cloudflare R2 bucket.");
|
|
11044
|
+
} else {
|
|
11045
|
+
const selectedR2BucketName = await p.select({
|
|
11046
|
+
message: "R2 List",
|
|
11047
|
+
options: [...availableBuckets.map((bucket) => ({
|
|
11048
|
+
value: bucket.name,
|
|
11049
|
+
label: bucket.name
|
|
11050
|
+
})), {
|
|
11051
|
+
value: createKey,
|
|
11052
|
+
label: "Create New R2 Bucket"
|
|
11053
|
+
}]
|
|
11054
|
+
});
|
|
11055
|
+
if (p.isCancel(selectedR2BucketName)) process.exit(1);
|
|
11056
|
+
selectedBucketName = selectedR2BucketName;
|
|
11057
|
+
}
|
|
11058
|
+
if (selectedBucketName === createKey) {
|
|
11059
|
+
const name = await p.text({ message: "Enter the name of the new R2 Bucket" });
|
|
11060
|
+
if (p.isCancel(name)) process.exit(1);
|
|
11061
|
+
const newR2 = await cf.r2.buckets.create({
|
|
11062
|
+
account_id: accountId,
|
|
11063
|
+
name
|
|
11064
|
+
});
|
|
11065
|
+
if (!newR2.name) throw new Error("Failed to create new R2 Bucket");
|
|
11066
|
+
selectedBucketName = newR2.name;
|
|
11067
|
+
}
|
|
10971
11068
|
}
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
11069
|
+
p.log.info(`Selected R2: ${selectedBucketName}`);
|
|
11070
|
+
const existingR2AccessKeyId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID");
|
|
11071
|
+
const existingR2SecretAccessKey = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY");
|
|
11072
|
+
let r2AccessKeyId = existingR2AccessKeyId;
|
|
11073
|
+
let r2SecretAccessKey = existingR2SecretAccessKey;
|
|
11074
|
+
if (r2AccessKeyId && r2SecretAccessKey) p.log.info("Using existing Cloudflare R2 API credentials.");
|
|
11075
|
+
else if (r2AccessKeyId || r2SecretAccessKey) {
|
|
11076
|
+
p.log.warn("Existing Cloudflare R2 API credentials are incomplete.");
|
|
11077
|
+
const credentials = await inputR2ApiCredentials({
|
|
11078
|
+
accountId,
|
|
11079
|
+
bucketName: selectedBucketName,
|
|
11080
|
+
accessKeyId: r2AccessKeyId,
|
|
11081
|
+
secretAccessKey: r2SecretAccessKey
|
|
11082
|
+
});
|
|
11083
|
+
r2AccessKeyId = credentials.accessKeyId;
|
|
11084
|
+
r2SecretAccessKey = credentials.secretAccessKey;
|
|
11085
|
+
} else {
|
|
11086
|
+
const credentials = await inputR2ApiCredentials({
|
|
11087
|
+
accountId,
|
|
11088
|
+
bucketName: selectedBucketName
|
|
10989
11089
|
});
|
|
10990
|
-
|
|
10991
|
-
|
|
11090
|
+
r2AccessKeyId = credentials.accessKeyId;
|
|
11091
|
+
r2SecretAccessKey = credentials.secretAccessKey;
|
|
10992
11092
|
}
|
|
10993
|
-
|
|
10994
|
-
if ((await cf.r2.buckets.domains.managed.list(selectedBucketName, { account_id: accountId })).enabled) {
|
|
11093
|
+
if ((existingBucketName ? { enabled: false } : await cf.r2.buckets.domains.managed.list(selectedBucketName, { account_id: accountId })).enabled) {
|
|
10995
11094
|
if (await p.confirm({ message: "Make R2 bucket private?" })) try {
|
|
10996
11095
|
await p.tasks([{
|
|
10997
11096
|
title: "Making R2 bucket private...",
|
|
@@ -11023,17 +11122,27 @@ const runInit = async ({ build }) => {
|
|
|
11023
11122
|
if (e instanceof Error) p.log.error(e.message);
|
|
11024
11123
|
throw e;
|
|
11025
11124
|
}
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11125
|
+
const existingD1DatabaseId = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID");
|
|
11126
|
+
const hasExistingD1Database = availableD1List.some((d1) => d1.uuid === existingD1DatabaseId);
|
|
11127
|
+
let selectedD1DatabaseId;
|
|
11128
|
+
if (existingD1DatabaseId && hasExistingD1Database) {
|
|
11129
|
+
selectedD1DatabaseId = existingD1DatabaseId;
|
|
11130
|
+
p.log.info("Using existing Cloudflare D1 database ID.");
|
|
11131
|
+
} else {
|
|
11132
|
+
if (existingD1DatabaseId) p.log.warn("Existing Cloudflare D1 database ID was not found. Select a database again.");
|
|
11133
|
+
const selectedD1 = await p.select({
|
|
11134
|
+
message: "D1 List",
|
|
11135
|
+
options: [...availableD1List.map((d1) => ({
|
|
11136
|
+
value: d1.uuid,
|
|
11137
|
+
label: `${d1.name} (${d1.uuid})`
|
|
11138
|
+
})), {
|
|
11139
|
+
value: createKey,
|
|
11140
|
+
label: "Create New D1 Database"
|
|
11141
|
+
}]
|
|
11142
|
+
});
|
|
11143
|
+
if (p.isCancel(selectedD1)) process.exit(1);
|
|
11144
|
+
selectedD1DatabaseId = selectedD1;
|
|
11145
|
+
}
|
|
11037
11146
|
if (selectedD1DatabaseId === createKey) {
|
|
11038
11147
|
const name = await p.text({ message: "Enter the name of the new D1 Database" });
|
|
11039
11148
|
if (p.isCancel(name)) process.exit(1);
|
|
@@ -11052,17 +11161,22 @@ const runInit = async ({ build }) => {
|
|
|
11052
11161
|
const d1DatabaseName = availableD1List.find((d1) => d1.uuid === selectedD1DatabaseId)?.name;
|
|
11053
11162
|
if (!d1DatabaseName) throw new Error("Failed to get D1 Database name");
|
|
11054
11163
|
const subdomains = await cf.workers.subdomains.get({ account_id: accountId });
|
|
11164
|
+
const existingWorkerName = getEnvValue(existingEnv, "HOT_UPDATER_CLOUDFLARE_WORKER_NAME");
|
|
11055
11165
|
const workerName = await deployWorker(auth.oauth_token, accountId, {
|
|
11056
11166
|
d1DatabaseId: selectedD1DatabaseId,
|
|
11057
11167
|
d1DatabaseName,
|
|
11058
|
-
r2BucketName: selectedBucketName
|
|
11168
|
+
r2BucketName: selectedBucketName,
|
|
11169
|
+
workerName: existingWorkerName
|
|
11059
11170
|
});
|
|
11060
11171
|
const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build));
|
|
11061
11172
|
await makeEnv({
|
|
11062
11173
|
HOT_UPDATER_CLOUDFLARE_API_TOKEN: apiToken,
|
|
11063
11174
|
HOT_UPDATER_CLOUDFLARE_ACCOUNT_ID: accountId,
|
|
11064
11175
|
HOT_UPDATER_CLOUDFLARE_R2_BUCKET_NAME: selectedBucketName,
|
|
11065
|
-
|
|
11176
|
+
HOT_UPDATER_CLOUDFLARE_R2_ACCESS_KEY_ID: r2AccessKeyId,
|
|
11177
|
+
HOT_UPDATER_CLOUDFLARE_R2_SECRET_ACCESS_KEY: r2SecretAccessKey,
|
|
11178
|
+
HOT_UPDATER_CLOUDFLARE_D1_DATABASE_ID: selectedD1DatabaseId,
|
|
11179
|
+
HOT_UPDATER_CLOUDFLARE_WORKER_NAME: workerName
|
|
11066
11180
|
});
|
|
11067
11181
|
p.log.success("Generated '.env.hotupdater' file with Cloudflare settings.");
|
|
11068
11182
|
if (configWriteResult.status === "created") p.log.success("Generated 'hot-updater.config.ts' file with Cloudflare settings.");
|
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
9
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
10
10
|
var __copyProps = (to, from, except, desc) => {
|
|
11
11
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
12
|
key = keys[i];
|
|
@@ -25,21 +25,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
let _hot_updater_core = require("@hot-updater/core");
|
|
26
26
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
27
27
|
let cloudflare = require("cloudflare");
|
|
28
|
-
cloudflare = __toESM(cloudflare
|
|
28
|
+
cloudflare = __toESM(cloudflare);
|
|
29
29
|
let node_fs_promises = require("node:fs/promises");
|
|
30
|
-
node_fs_promises = __toESM(node_fs_promises
|
|
30
|
+
node_fs_promises = __toESM(node_fs_promises);
|
|
31
31
|
let node_path = require("node:path");
|
|
32
|
-
node_path = __toESM(node_path
|
|
32
|
+
node_path = __toESM(node_path);
|
|
33
|
+
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
34
|
+
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
35
|
+
let _aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
36
|
+
let node_os = require("node:os");
|
|
37
|
+
node_os = __toESM(node_os);
|
|
33
38
|
let node_url = require("node:url");
|
|
34
39
|
let node_child_process = require("node:child_process");
|
|
35
40
|
let node_string_decoder = require("node:string_decoder");
|
|
36
41
|
let node_util = require("node:util");
|
|
37
42
|
let node_process = require("node:process");
|
|
38
|
-
node_process = __toESM(node_process
|
|
43
|
+
node_process = __toESM(node_process);
|
|
39
44
|
let node_tty = require("node:tty");
|
|
40
|
-
node_tty = __toESM(node_tty
|
|
45
|
+
node_tty = __toESM(node_tty);
|
|
41
46
|
let node_timers_promises = require("node:timers/promises");
|
|
42
|
-
let node_os = require("node:os");
|
|
43
47
|
let node_events = require("node:events");
|
|
44
48
|
let node_v8 = require("node:v8");
|
|
45
49
|
let node_fs = require("node:fs");
|
|
@@ -669,6 +673,112 @@ const d1Database = (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
|
669
673
|
}
|
|
670
674
|
});
|
|
671
675
|
//#endregion
|
|
676
|
+
//#region src/r2S3Storage.ts
|
|
677
|
+
const ensureExpectedR2Bucket$1 = (bucket, bucketName) => {
|
|
678
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
679
|
+
};
|
|
680
|
+
const isS3ObjectNotFoundError = (error) => {
|
|
681
|
+
if (error instanceof Error) return error.name === "NotFound" || error.name === "NoSuchKey";
|
|
682
|
+
if (typeof error === "object" && error !== null && "$metadata" in error) return error.$metadata?.httpStatusCode === 404;
|
|
683
|
+
return false;
|
|
684
|
+
};
|
|
685
|
+
const createS3Client = (config) => {
|
|
686
|
+
const { accountId, basePath: _basePath, bucketName: _bucketName, endpoint, forcePathStyle, region, ...s3Config } = config;
|
|
687
|
+
return new _aws_sdk_client_s3.S3Client({
|
|
688
|
+
...s3Config,
|
|
689
|
+
endpoint: endpoint ?? `https://${accountId}.r2.cloudflarestorage.com`,
|
|
690
|
+
forcePathStyle: forcePathStyle ?? true,
|
|
691
|
+
region: region ?? "auto"
|
|
692
|
+
});
|
|
693
|
+
};
|
|
694
|
+
const createS3StorageProfile = (config) => {
|
|
695
|
+
const { bucketName } = config;
|
|
696
|
+
const client = createS3Client(config);
|
|
697
|
+
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
698
|
+
return {
|
|
699
|
+
async delete(storageUri) {
|
|
700
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
701
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
702
|
+
await client.send(new _aws_sdk_client_s3.DeleteObjectCommand({
|
|
703
|
+
Bucket: bucketName,
|
|
704
|
+
Key: key
|
|
705
|
+
}));
|
|
706
|
+
},
|
|
707
|
+
async upload(key, filePath) {
|
|
708
|
+
const Body = await node_fs_promises.default.readFile(filePath);
|
|
709
|
+
const ContentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
|
|
710
|
+
const Key = getStorageKey(key, node_path.default.basename(filePath));
|
|
711
|
+
await new _aws_sdk_lib_storage.Upload({
|
|
712
|
+
client,
|
|
713
|
+
params: {
|
|
714
|
+
Body,
|
|
715
|
+
Bucket: bucketName,
|
|
716
|
+
CacheControl: "max-age=31536000",
|
|
717
|
+
ContentType,
|
|
718
|
+
Key
|
|
719
|
+
}
|
|
720
|
+
}).done();
|
|
721
|
+
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
722
|
+
},
|
|
723
|
+
async exists(storageUri) {
|
|
724
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
725
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
726
|
+
try {
|
|
727
|
+
await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
728
|
+
Bucket: bucketName,
|
|
729
|
+
Key: key
|
|
730
|
+
}));
|
|
731
|
+
return true;
|
|
732
|
+
} catch (error) {
|
|
733
|
+
if (isS3ObjectNotFoundError(error)) return false;
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
async downloadFile(storageUri, filePath) {
|
|
738
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
739
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
740
|
+
const response = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
741
|
+
Bucket: bucketName,
|
|
742
|
+
Key: key
|
|
743
|
+
}));
|
|
744
|
+
if (!response.Body) throw new Error("R2 object body is empty");
|
|
745
|
+
await node_fs_promises.default.mkdir(node_path.default.dirname(filePath), { recursive: true });
|
|
746
|
+
await node_fs_promises.default.writeFile(filePath, await response.Body.transformToByteArray());
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
const createS3RuntimeStorageProfile = (config) => {
|
|
751
|
+
const { bucketName } = config;
|
|
752
|
+
const client = createS3Client(config);
|
|
753
|
+
return {
|
|
754
|
+
async readText(storageUri) {
|
|
755
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
756
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
757
|
+
try {
|
|
758
|
+
const response = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
759
|
+
Bucket: bucketName,
|
|
760
|
+
Key: key
|
|
761
|
+
}));
|
|
762
|
+
if (!response.Body) return null;
|
|
763
|
+
return response.Body.transformToString();
|
|
764
|
+
} catch (error) {
|
|
765
|
+
if (isS3ObjectNotFoundError(error)) return null;
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
async getDownloadUrl(storageUri) {
|
|
770
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
771
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
772
|
+
const signedUrl = await (0, _aws_sdk_s3_request_presigner.getSignedUrl)(client, new _aws_sdk_client_s3.GetObjectCommand({
|
|
773
|
+
Bucket: bucketName,
|
|
774
|
+
Key: key
|
|
775
|
+
}), { expiresIn: 3600 });
|
|
776
|
+
if (!signedUrl) throw new Error("Failed to presign R2 URL");
|
|
777
|
+
return { fileUrl: signedUrl };
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
};
|
|
781
|
+
//#endregion
|
|
672
782
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
673
783
|
function isPlainObject(value) {
|
|
674
784
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -6842,55 +6952,108 @@ const createWrangler = ({ stdio, accountId, cloudflareApiToken, cwd }) => {
|
|
|
6842
6952
|
return (...command) => $("npx", ["wrangler", ...command]);
|
|
6843
6953
|
};
|
|
6844
6954
|
//#endregion
|
|
6955
|
+
//#region src/r2WranglerStorage.ts
|
|
6956
|
+
const ensureExpectedR2Bucket = (bucket, bucketName) => {
|
|
6957
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6958
|
+
};
|
|
6959
|
+
const isR2ObjectNotFoundError = (error) => {
|
|
6960
|
+
const output = [
|
|
6961
|
+
error.stderr,
|
|
6962
|
+
error.stdout,
|
|
6963
|
+
error.shortMessage,
|
|
6964
|
+
error.message
|
|
6965
|
+
].filter(Boolean).join("\n").toLowerCase();
|
|
6966
|
+
return output.includes("not found") || output.includes("no such object") || output.includes("does not exist");
|
|
6967
|
+
};
|
|
6968
|
+
const createWranglerStorageProfile = (config) => {
|
|
6969
|
+
const { bucketName, cloudflareApiToken, accountId } = config;
|
|
6970
|
+
const wrangler = createWrangler({
|
|
6971
|
+
accountId,
|
|
6972
|
+
cloudflareApiToken,
|
|
6973
|
+
cwd: process.cwd()
|
|
6974
|
+
});
|
|
6975
|
+
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
6976
|
+
return {
|
|
6977
|
+
async delete(storageUri) {
|
|
6978
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
6979
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
6980
|
+
try {
|
|
6981
|
+
await wrangler("r2", "object", "delete", [bucketName, key].join("/"), "--remote");
|
|
6982
|
+
} catch {
|
|
6983
|
+
throw new Error("Can not delete bundle");
|
|
6984
|
+
}
|
|
6985
|
+
},
|
|
6986
|
+
async upload(key, filePath) {
|
|
6987
|
+
const contentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
|
|
6988
|
+
const Key = getStorageKey(key, node_path.default.basename(filePath));
|
|
6989
|
+
try {
|
|
6990
|
+
const { stderr, exitCode } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", filePath, "--content-type", contentType, "--remote");
|
|
6991
|
+
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6992
|
+
} catch (error) {
|
|
6993
|
+
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6994
|
+
throw error;
|
|
6995
|
+
}
|
|
6996
|
+
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
6997
|
+
},
|
|
6998
|
+
async exists(storageUri) {
|
|
6999
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
7000
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
7001
|
+
const tempDir = await node_fs_promises.default.mkdtemp(node_path.default.join(node_os.default.tmpdir(), "hot-updater-r2-exists-"));
|
|
7002
|
+
const tempFilePath = node_path.default.join(tempDir, "object");
|
|
7003
|
+
try {
|
|
7004
|
+
await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", tempFilePath, "--remote");
|
|
7005
|
+
return true;
|
|
7006
|
+
} catch (error) {
|
|
7007
|
+
if (error instanceof ExecaError && isR2ObjectNotFoundError(error)) return false;
|
|
7008
|
+
throw error;
|
|
7009
|
+
} finally {
|
|
7010
|
+
await node_fs_promises.default.rm(tempDir, {
|
|
7011
|
+
force: true,
|
|
7012
|
+
recursive: true
|
|
7013
|
+
});
|
|
7014
|
+
}
|
|
7015
|
+
},
|
|
7016
|
+
async downloadFile(storageUri, filePath) {
|
|
7017
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
7018
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
7019
|
+
try {
|
|
7020
|
+
await node_fs_promises.default.mkdir(node_path.default.dirname(filePath), { recursive: true });
|
|
7021
|
+
const { stderr, exitCode } = await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", filePath, "--remote");
|
|
7022
|
+
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
7023
|
+
} catch (error) {
|
|
7024
|
+
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
7025
|
+
throw error;
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
};
|
|
7029
|
+
};
|
|
7030
|
+
const createWranglerRuntimeStorageProfile = () => {
|
|
7031
|
+
const error = /* @__PURE__ */ new Error("r2Storage runtime profile requires R2 S3 credentials. Wrangler-based R2 access is only supported by the node profile.");
|
|
7032
|
+
return {
|
|
7033
|
+
async readText() {
|
|
7034
|
+
throw error;
|
|
7035
|
+
},
|
|
7036
|
+
async getDownloadUrl() {
|
|
7037
|
+
throw error;
|
|
7038
|
+
}
|
|
7039
|
+
};
|
|
7040
|
+
};
|
|
7041
|
+
//#endregion
|
|
6845
7042
|
//#region src/r2Storage.ts
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
const r2Storage = (0, _hot_updater_plugin_core.
|
|
7043
|
+
const hasS3Credentials = (config) => {
|
|
7044
|
+
return Boolean(config.credentials);
|
|
7045
|
+
};
|
|
7046
|
+
const r2Storage = (0, _hot_updater_plugin_core.createUniversalStoragePlugin)({
|
|
6850
7047
|
name: "r2Storage",
|
|
6851
7048
|
supportedProtocol: "r2",
|
|
6852
7049
|
factory: (config) => {
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
cwd: process.cwd()
|
|
6858
|
-
});
|
|
6859
|
-
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
7050
|
+
if (hasS3Credentials(config)) return {
|
|
7051
|
+
node: createS3StorageProfile(config),
|
|
7052
|
+
runtime: createS3RuntimeStorageProfile(config)
|
|
7053
|
+
};
|
|
6860
7054
|
return {
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6864
|
-
try {
|
|
6865
|
-
await wrangler("r2", "object", "delete", [bucketName, key].join("/"), "--remote");
|
|
6866
|
-
} catch {
|
|
6867
|
-
throw new Error("Can not delete bundle");
|
|
6868
|
-
}
|
|
6869
|
-
},
|
|
6870
|
-
async upload(key, filePath) {
|
|
6871
|
-
const contentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
|
|
6872
|
-
const Key = getStorageKey(key, node_path.default.basename(filePath));
|
|
6873
|
-
try {
|
|
6874
|
-
const { stderr, exitCode } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", filePath, "--content-type", contentType, "--remote");
|
|
6875
|
-
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6876
|
-
} catch (error) {
|
|
6877
|
-
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6878
|
-
throw error;
|
|
6879
|
-
}
|
|
6880
|
-
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
6881
|
-
},
|
|
6882
|
-
async downloadFile(storageUri, filePath) {
|
|
6883
|
-
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "r2");
|
|
6884
|
-
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6885
|
-
try {
|
|
6886
|
-
await node_fs_promises.default.mkdir(node_path.default.dirname(filePath), { recursive: true });
|
|
6887
|
-
const { stderr, exitCode } = await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", filePath, "--remote");
|
|
6888
|
-
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6889
|
-
} catch (error) {
|
|
6890
|
-
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6891
|
-
throw error;
|
|
6892
|
-
}
|
|
6893
|
-
}
|
|
7055
|
+
node: createWranglerStorageProfile(config),
|
|
7056
|
+
runtime: createWranglerRuntimeStorageProfile()
|
|
6894
7057
|
};
|
|
6895
7058
|
}
|
|
6896
7059
|
});
|