@hot-updater/cloudflare 0.31.4 → 0.33.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 +223 -63
- package/dist/index.d.cts +46 -6
- package/dist/index.d.mts +46 -6
- package/dist/index.mjs +218 -59
- package/dist/worker/index.cjs +9 -12
- package/dist/worker/index.d.cts +4 -3
- package/dist/worker/index.d.mts +3 -2
- package/dist/worker/index.mjs +9 -12
- package/package.json +10 -7
- package/src/cloudflareWorkerDatabase.spec.ts +260 -0
- package/src/cloudflareWorkerDatabase.ts +23 -19
- package/src/d1Database.spec.ts +16 -2
- package/src/d1Database.ts +23 -19
- 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 +249 -58
- package/worker/dist/index.js.map +4 -4
- package/worker/src/index.ts +0 -1
- package/worker/src/getUpdateInfo.ts +0 -194
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { DEFAULT_ROLLOUT_COHORT_COUNT, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
|
|
3
|
-
import { calculatePagination, createDatabasePlugin, createDatabasePluginGetUpdateInfo,
|
|
3
|
+
import { calculatePagination, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createStorageKeyBuilder, createUniversalStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
|
|
4
4
|
import Cloudflare from "cloudflare";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
8
|
+
import { Upload } from "@aws-sdk/lib-storage";
|
|
9
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
10
|
+
import os, { constants } from "node:os";
|
|
7
11
|
import { fileURLToPath } from "node:url";
|
|
8
12
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
9
13
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -11,7 +15,6 @@ import { aborted, callbackify, debuglog, inspect, promisify, stripVTControlChara
|
|
|
11
15
|
import process$1, { execArgv, execPath, hrtime, platform } from "node:process";
|
|
12
16
|
import tty from "node:tty";
|
|
13
17
|
import { scheduler, setImmediate, setTimeout } from "node:timers/promises";
|
|
14
|
-
import { constants } from "node:os";
|
|
15
18
|
import { EventEmitter, addAbortListener, on, once, setMaxListeners } from "node:events";
|
|
16
19
|
import { serialize } from "node:v8";
|
|
17
20
|
import { appendFileSync, createReadStream, createWriteStream, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -25,7 +28,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
25
28
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
26
29
|
var __getProtoOf = Object.getPrototypeOf;
|
|
27
30
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
28
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
31
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
29
32
|
var __copyProps = (to, from, except, desc) => {
|
|
30
33
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
31
34
|
key = keys[i];
|
|
@@ -273,6 +276,11 @@ var import_lib = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
|
|
|
273
276
|
parser.parsingErrorCode = error.parsingErrorCode;
|
|
274
277
|
module.exports = parser;
|
|
275
278
|
})))(), 1);
|
|
279
|
+
const buildJsonEachInClause = (columnName, values, params) => {
|
|
280
|
+
if (values.length === 0) return "1 = 0";
|
|
281
|
+
params.push(JSON.stringify(values));
|
|
282
|
+
return `${columnName} IN (SELECT value FROM json_each(?))`;
|
|
283
|
+
};
|
|
276
284
|
async function resolvePage(singlePage) {
|
|
277
285
|
const results = [];
|
|
278
286
|
for await (const page of singlePage.iterPages()) {
|
|
@@ -296,11 +304,7 @@ function buildWhereClause(conditions) {
|
|
|
296
304
|
clauses.push("enabled = ?");
|
|
297
305
|
params.push(conditions.enabled ? 1 : 0);
|
|
298
306
|
}
|
|
299
|
-
if (conditions.id?.in)
|
|
300
|
-
else {
|
|
301
|
-
clauses.push(`id IN (${conditions.id.in.map(() => "?").join(", ")})`);
|
|
302
|
-
params.push(...conditions.id.in);
|
|
303
|
-
}
|
|
307
|
+
if (conditions.id?.in) clauses.push(buildJsonEachInClause("id", conditions.id.in, params));
|
|
304
308
|
if (conditions.id?.eq) {
|
|
305
309
|
clauses.push("id = ?");
|
|
306
310
|
params.push(conditions.id.eq);
|
|
@@ -327,11 +331,7 @@ function buildWhereClause(conditions) {
|
|
|
327
331
|
clauses.push("target_app_version = ?");
|
|
328
332
|
params.push(conditions.targetAppVersion);
|
|
329
333
|
}
|
|
330
|
-
if (conditions.targetAppVersionIn)
|
|
331
|
-
else {
|
|
332
|
-
clauses.push(`target_app_version IN (${conditions.targetAppVersionIn.map(() => "?").join(", ")})`);
|
|
333
|
-
params.push(...conditions.targetAppVersionIn);
|
|
334
|
-
}
|
|
334
|
+
if (conditions.targetAppVersionIn) clauses.push(buildJsonEachInClause("target_app_version", conditions.targetAppVersionIn, params));
|
|
335
335
|
if (conditions.fingerprintHash !== void 0) if (conditions.fingerprintHash === null) clauses.push("fingerprint_hash IS NULL");
|
|
336
336
|
else {
|
|
337
337
|
clauses.push("fingerprint_hash = ?");
|
|
@@ -416,13 +416,13 @@ const d1Database = createDatabasePlugin({
|
|
|
416
416
|
const sql = (0, import_lib.default)(`
|
|
417
417
|
SELECT *
|
|
418
418
|
FROM bundle_patches
|
|
419
|
-
WHERE bundle_id IN (
|
|
419
|
+
WHERE bundle_id IN (SELECT value FROM json_each(?))
|
|
420
420
|
ORDER BY order_index ASC, base_bundle_id ASC
|
|
421
421
|
`);
|
|
422
422
|
const rows = await resolvePage(await cf.d1.database.query(config.databaseId, {
|
|
423
423
|
account_id: config.accountId,
|
|
424
424
|
sql,
|
|
425
|
-
params: bundleIds
|
|
425
|
+
params: [JSON.stringify(bundleIds)]
|
|
426
426
|
}));
|
|
427
427
|
for (const row of rows) {
|
|
428
428
|
const current = patchMap.get(row.bundle_id) ?? [];
|
|
@@ -665,6 +665,112 @@ const d1Database = createDatabasePlugin({
|
|
|
665
665
|
}
|
|
666
666
|
});
|
|
667
667
|
//#endregion
|
|
668
|
+
//#region src/r2S3Storage.ts
|
|
669
|
+
const ensureExpectedR2Bucket$1 = (bucket, bucketName) => {
|
|
670
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
671
|
+
};
|
|
672
|
+
const isS3ObjectNotFoundError = (error) => {
|
|
673
|
+
if (error instanceof Error) return error.name === "NotFound" || error.name === "NoSuchKey";
|
|
674
|
+
if (typeof error === "object" && error !== null && "$metadata" in error) return error.$metadata?.httpStatusCode === 404;
|
|
675
|
+
return false;
|
|
676
|
+
};
|
|
677
|
+
const createS3Client = (config) => {
|
|
678
|
+
const { accountId, basePath: _basePath, bucketName: _bucketName, endpoint, forcePathStyle, region, ...s3Config } = config;
|
|
679
|
+
return new S3Client({
|
|
680
|
+
...s3Config,
|
|
681
|
+
endpoint: endpoint ?? `https://${accountId}.r2.cloudflarestorage.com`,
|
|
682
|
+
forcePathStyle: forcePathStyle ?? true,
|
|
683
|
+
region: region ?? "auto"
|
|
684
|
+
});
|
|
685
|
+
};
|
|
686
|
+
const createS3StorageProfile = (config) => {
|
|
687
|
+
const { bucketName } = config;
|
|
688
|
+
const client = createS3Client(config);
|
|
689
|
+
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
690
|
+
return {
|
|
691
|
+
async delete(storageUri) {
|
|
692
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
693
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
694
|
+
await client.send(new DeleteObjectCommand({
|
|
695
|
+
Bucket: bucketName,
|
|
696
|
+
Key: key
|
|
697
|
+
}));
|
|
698
|
+
},
|
|
699
|
+
async upload(key, filePath) {
|
|
700
|
+
const Body = await fs.readFile(filePath);
|
|
701
|
+
const ContentType = getContentType(filePath);
|
|
702
|
+
const Key = getStorageKey(key, path.basename(filePath));
|
|
703
|
+
await new Upload({
|
|
704
|
+
client,
|
|
705
|
+
params: {
|
|
706
|
+
Body,
|
|
707
|
+
Bucket: bucketName,
|
|
708
|
+
CacheControl: "max-age=31536000",
|
|
709
|
+
ContentType,
|
|
710
|
+
Key
|
|
711
|
+
}
|
|
712
|
+
}).done();
|
|
713
|
+
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
714
|
+
},
|
|
715
|
+
async exists(storageUri) {
|
|
716
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
717
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
718
|
+
try {
|
|
719
|
+
await client.send(new HeadObjectCommand({
|
|
720
|
+
Bucket: bucketName,
|
|
721
|
+
Key: key
|
|
722
|
+
}));
|
|
723
|
+
return true;
|
|
724
|
+
} catch (error) {
|
|
725
|
+
if (isS3ObjectNotFoundError(error)) return false;
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
async downloadFile(storageUri, filePath) {
|
|
730
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
731
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
732
|
+
const response = await client.send(new GetObjectCommand({
|
|
733
|
+
Bucket: bucketName,
|
|
734
|
+
Key: key
|
|
735
|
+
}));
|
|
736
|
+
if (!response.Body) throw new Error("R2 object body is empty");
|
|
737
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
738
|
+
await fs.writeFile(filePath, await response.Body.transformToByteArray());
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
};
|
|
742
|
+
const createS3RuntimeStorageProfile = (config) => {
|
|
743
|
+
const { bucketName } = config;
|
|
744
|
+
const client = createS3Client(config);
|
|
745
|
+
return {
|
|
746
|
+
async readText(storageUri) {
|
|
747
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
748
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
749
|
+
try {
|
|
750
|
+
const response = await client.send(new GetObjectCommand({
|
|
751
|
+
Bucket: bucketName,
|
|
752
|
+
Key: key
|
|
753
|
+
}));
|
|
754
|
+
if (!response.Body) return null;
|
|
755
|
+
return response.Body.transformToString();
|
|
756
|
+
} catch (error) {
|
|
757
|
+
if (isS3ObjectNotFoundError(error)) return null;
|
|
758
|
+
throw error;
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
async getDownloadUrl(storageUri) {
|
|
762
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
763
|
+
ensureExpectedR2Bucket$1(bucket, bucketName);
|
|
764
|
+
const signedUrl = await getSignedUrl(client, new GetObjectCommand({
|
|
765
|
+
Bucket: bucketName,
|
|
766
|
+
Key: key
|
|
767
|
+
}), { expiresIn: 3600 });
|
|
768
|
+
if (!signedUrl) throw new Error("Failed to presign R2 URL");
|
|
769
|
+
return { fileUrl: signedUrl };
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
//#endregion
|
|
668
774
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
669
775
|
function isPlainObject(value) {
|
|
670
776
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -6838,55 +6944,108 @@ const createWrangler = ({ stdio, accountId, cloudflareApiToken, cwd }) => {
|
|
|
6838
6944
|
return (...command) => $("npx", ["wrangler", ...command]);
|
|
6839
6945
|
};
|
|
6840
6946
|
//#endregion
|
|
6947
|
+
//#region src/r2WranglerStorage.ts
|
|
6948
|
+
const ensureExpectedR2Bucket = (bucket, bucketName) => {
|
|
6949
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6950
|
+
};
|
|
6951
|
+
const isR2ObjectNotFoundError = (error) => {
|
|
6952
|
+
const output = [
|
|
6953
|
+
error.stderr,
|
|
6954
|
+
error.stdout,
|
|
6955
|
+
error.shortMessage,
|
|
6956
|
+
error.message
|
|
6957
|
+
].filter(Boolean).join("\n").toLowerCase();
|
|
6958
|
+
return output.includes("not found") || output.includes("no such object") || output.includes("does not exist");
|
|
6959
|
+
};
|
|
6960
|
+
const createWranglerStorageProfile = (config) => {
|
|
6961
|
+
const { bucketName, cloudflareApiToken, accountId } = config;
|
|
6962
|
+
const wrangler = createWrangler({
|
|
6963
|
+
accountId,
|
|
6964
|
+
cloudflareApiToken,
|
|
6965
|
+
cwd: process.cwd()
|
|
6966
|
+
});
|
|
6967
|
+
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
6968
|
+
return {
|
|
6969
|
+
async delete(storageUri) {
|
|
6970
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
6971
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
6972
|
+
try {
|
|
6973
|
+
await wrangler("r2", "object", "delete", [bucketName, key].join("/"), "--remote");
|
|
6974
|
+
} catch {
|
|
6975
|
+
throw new Error("Can not delete bundle");
|
|
6976
|
+
}
|
|
6977
|
+
},
|
|
6978
|
+
async upload(key, filePath) {
|
|
6979
|
+
const contentType = getContentType(filePath);
|
|
6980
|
+
const Key = getStorageKey(key, path.basename(filePath));
|
|
6981
|
+
try {
|
|
6982
|
+
const { stderr, exitCode } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", filePath, "--content-type", contentType, "--remote");
|
|
6983
|
+
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6984
|
+
} catch (error) {
|
|
6985
|
+
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6986
|
+
throw error;
|
|
6987
|
+
}
|
|
6988
|
+
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
6989
|
+
},
|
|
6990
|
+
async exists(storageUri) {
|
|
6991
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
6992
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
6993
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "hot-updater-r2-exists-"));
|
|
6994
|
+
const tempFilePath = path.join(tempDir, "object");
|
|
6995
|
+
try {
|
|
6996
|
+
await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", tempFilePath, "--remote");
|
|
6997
|
+
return true;
|
|
6998
|
+
} catch (error) {
|
|
6999
|
+
if (error instanceof ExecaError && isR2ObjectNotFoundError(error)) return false;
|
|
7000
|
+
throw error;
|
|
7001
|
+
} finally {
|
|
7002
|
+
await fs.rm(tempDir, {
|
|
7003
|
+
force: true,
|
|
7004
|
+
recursive: true
|
|
7005
|
+
});
|
|
7006
|
+
}
|
|
7007
|
+
},
|
|
7008
|
+
async downloadFile(storageUri, filePath) {
|
|
7009
|
+
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
7010
|
+
ensureExpectedR2Bucket(bucket, bucketName);
|
|
7011
|
+
try {
|
|
7012
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
7013
|
+
const { stderr, exitCode } = await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", filePath, "--remote");
|
|
7014
|
+
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
7015
|
+
} catch (error) {
|
|
7016
|
+
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
7017
|
+
throw error;
|
|
7018
|
+
}
|
|
7019
|
+
}
|
|
7020
|
+
};
|
|
7021
|
+
};
|
|
7022
|
+
const createWranglerRuntimeStorageProfile = () => {
|
|
7023
|
+
const error = /* @__PURE__ */ new Error("r2Storage runtime profile requires R2 S3 credentials. Wrangler-based R2 access is only supported by the node profile.");
|
|
7024
|
+
return {
|
|
7025
|
+
async readText() {
|
|
7026
|
+
throw error;
|
|
7027
|
+
},
|
|
7028
|
+
async getDownloadUrl() {
|
|
7029
|
+
throw error;
|
|
7030
|
+
}
|
|
7031
|
+
};
|
|
7032
|
+
};
|
|
7033
|
+
//#endregion
|
|
6841
7034
|
//#region src/r2Storage.ts
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
const r2Storage =
|
|
7035
|
+
const hasS3Credentials = (config) => {
|
|
7036
|
+
return Boolean(config.credentials);
|
|
7037
|
+
};
|
|
7038
|
+
const r2Storage = createUniversalStoragePlugin({
|
|
6846
7039
|
name: "r2Storage",
|
|
6847
7040
|
supportedProtocol: "r2",
|
|
6848
7041
|
factory: (config) => {
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
cwd: process.cwd()
|
|
6854
|
-
});
|
|
6855
|
-
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
7042
|
+
if (hasS3Credentials(config)) return {
|
|
7043
|
+
node: createS3StorageProfile(config),
|
|
7044
|
+
runtime: createS3RuntimeStorageProfile(config)
|
|
7045
|
+
};
|
|
6856
7046
|
return {
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6860
|
-
try {
|
|
6861
|
-
await wrangler("r2", "object", "delete", [bucketName, key].join("/"), "--remote");
|
|
6862
|
-
} catch {
|
|
6863
|
-
throw new Error("Can not delete bundle");
|
|
6864
|
-
}
|
|
6865
|
-
},
|
|
6866
|
-
async upload(key, filePath) {
|
|
6867
|
-
const contentType = getContentType(filePath);
|
|
6868
|
-
const Key = getStorageKey(key, path.basename(filePath));
|
|
6869
|
-
try {
|
|
6870
|
-
const { stderr, exitCode } = await wrangler("r2", "object", "put", [bucketName, Key].join("/"), "--file", filePath, "--content-type", contentType, "--remote");
|
|
6871
|
-
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6872
|
-
} catch (error) {
|
|
6873
|
-
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6874
|
-
throw error;
|
|
6875
|
-
}
|
|
6876
|
-
return { storageUri: `r2://${bucketName}/${Key}` };
|
|
6877
|
-
},
|
|
6878
|
-
async downloadFile(storageUri, filePath) {
|
|
6879
|
-
const { bucket, key } = parseStorageUri(storageUri, "r2");
|
|
6880
|
-
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
6881
|
-
try {
|
|
6882
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
6883
|
-
const { stderr, exitCode } = await wrangler("r2", "object", "get", [bucketName, key].join("/"), "--file", filePath, "--remote");
|
|
6884
|
-
if (exitCode !== 0 && stderr) throw new Error(stderr);
|
|
6885
|
-
} catch (error) {
|
|
6886
|
-
if (error instanceof ExecaError) throw new Error(error.stderr || error.stdout);
|
|
6887
|
-
throw error;
|
|
6888
|
-
}
|
|
6889
|
-
}
|
|
7047
|
+
node: createWranglerStorageProfile(config),
|
|
7048
|
+
runtime: createWranglerRuntimeStorageProfile()
|
|
6890
7049
|
};
|
|
6891
7050
|
}
|
|
6892
7051
|
});
|
package/dist/worker/index.cjs
CHANGED
|
@@ -3,6 +3,11 @@ let _hot_updater_js = require("@hot-updater/js");
|
|
|
3
3
|
let _hot_updater_core = require("@hot-updater/core");
|
|
4
4
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
5
5
|
//#region src/cloudflareWorkerDatabase.ts
|
|
6
|
+
const buildJsonEachInClause = (columnName, values, params) => {
|
|
7
|
+
if (values.length === 0) return "1 = 0";
|
|
8
|
+
params.push(JSON.stringify(values));
|
|
9
|
+
return `${columnName} IN (SELECT value FROM json_each(?))`;
|
|
10
|
+
};
|
|
6
11
|
function buildWhereClause(conditions) {
|
|
7
12
|
if (!conditions) return {
|
|
8
13
|
sql: "",
|
|
@@ -22,11 +27,7 @@ function buildWhereClause(conditions) {
|
|
|
22
27
|
clauses.push("enabled = ?");
|
|
23
28
|
params.push(conditions.enabled ? 1 : 0);
|
|
24
29
|
}
|
|
25
|
-
if (conditions.id?.in)
|
|
26
|
-
else {
|
|
27
|
-
clauses.push(`id IN (${conditions.id.in.map(() => "?").join(", ")})`);
|
|
28
|
-
params.push(...conditions.id.in);
|
|
29
|
-
}
|
|
30
|
+
if (conditions.id?.in) clauses.push(buildJsonEachInClause("id", conditions.id.in, params));
|
|
30
31
|
if (conditions.id?.eq) {
|
|
31
32
|
clauses.push("id = ?");
|
|
32
33
|
params.push(conditions.id.eq);
|
|
@@ -53,11 +54,7 @@ function buildWhereClause(conditions) {
|
|
|
53
54
|
clauses.push("target_app_version = ?");
|
|
54
55
|
params.push(conditions.targetAppVersion);
|
|
55
56
|
}
|
|
56
|
-
if (conditions.targetAppVersionIn)
|
|
57
|
-
else {
|
|
58
|
-
clauses.push(`target_app_version IN (${conditions.targetAppVersionIn.map(() => "?").join(", ")})`);
|
|
59
|
-
params.push(...conditions.targetAppVersionIn);
|
|
60
|
-
}
|
|
57
|
+
if (conditions.targetAppVersionIn) clauses.push(buildJsonEachInClause("target_app_version", conditions.targetAppVersionIn, params));
|
|
61
58
|
if (conditions.fingerprintHash !== void 0) if (conditions.fingerprintHash === null) clauses.push("fingerprint_hash IS NULL");
|
|
62
59
|
else {
|
|
63
60
|
clauses.push("fingerprint_hash = ?");
|
|
@@ -152,9 +149,9 @@ const d1WorkerDatabase = () => (0, _hot_updater_plugin_core.createDatabasePlugin
|
|
|
152
149
|
const rows = await queryAll(`
|
|
153
150
|
SELECT *
|
|
154
151
|
FROM bundle_patches
|
|
155
|
-
WHERE bundle_id IN (
|
|
152
|
+
WHERE bundle_id IN (SELECT value FROM json_each(?))
|
|
156
153
|
ORDER BY order_index ASC, base_bundle_id ASC
|
|
157
|
-
`, bundleIds, context);
|
|
154
|
+
`, [JSON.stringify(bundleIds)], context);
|
|
158
155
|
for (const row of rows) {
|
|
159
156
|
const current = patchMap.get(row.bundle_id) ?? [];
|
|
160
157
|
current.push(row);
|
package/dist/worker/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
2
|
import { HotUpdaterContext, RequestEnvContext as RequestEnvContext$1 } from "@hot-updater/plugin-core";
|
|
3
|
+
import { verifyJwtSignedUrl } from "@hot-updater/js";
|
|
3
4
|
|
|
4
5
|
//#region src/cloudflareWorkerDatabase.d.ts
|
|
5
6
|
type D1Result<T> = {
|
|
@@ -37,7 +38,7 @@ interface CloudflareWorkerStorageConfig<TContext extends RequestEnvContext$1<Clo
|
|
|
37
38
|
//#region src/worker/index.d.ts
|
|
38
39
|
interface CloudflareWorkerRuntimeEnv extends CloudflareWorkerDatabaseEnv, CloudflareWorkerStorageEnv {}
|
|
39
40
|
type RequestEnvContext<TEnv = CloudflareWorkerRuntimeEnv> = RequestEnvContext$1<TEnv>;
|
|
40
|
-
declare const d1Database: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>() => () =>
|
|
41
|
-
declare const r2Storage: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>(config: CloudflareWorkerStorageConfig<TContext>) => () =>
|
|
41
|
+
declare const d1Database: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>() => () => _$_hot_updater_plugin_core0.DatabasePlugin<TContext>;
|
|
42
|
+
declare const r2Storage: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>(config: CloudflareWorkerStorageConfig<TContext>) => () => _$_hot_updater_plugin_core0.RuntimeStoragePlugin<TContext>;
|
|
42
43
|
//#endregion
|
|
43
44
|
export { type CloudflareWorkerDatabaseEnv, CloudflareWorkerRuntimeEnv, type CloudflareWorkerStorageEnv, RequestEnvContext, d1Database, r2Storage, verifyJwtSignedUrl };
|
package/dist/worker/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { verifyJwtSignedUrl } from "@hot-updater/js";
|
|
2
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
3
|
import { HotUpdaterContext, RequestEnvContext as RequestEnvContext$1 } from "@hot-updater/plugin-core";
|
|
3
4
|
|
|
4
5
|
//#region src/cloudflareWorkerDatabase.d.ts
|
|
@@ -37,7 +38,7 @@ interface CloudflareWorkerStorageConfig<TContext extends RequestEnvContext$1<Clo
|
|
|
37
38
|
//#region src/worker/index.d.ts
|
|
38
39
|
interface CloudflareWorkerRuntimeEnv extends CloudflareWorkerDatabaseEnv, CloudflareWorkerStorageEnv {}
|
|
39
40
|
type RequestEnvContext<TEnv = CloudflareWorkerRuntimeEnv> = RequestEnvContext$1<TEnv>;
|
|
40
|
-
declare const d1Database: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>() => () =>
|
|
41
|
-
declare const r2Storage: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>(config: CloudflareWorkerStorageConfig<TContext>) => () =>
|
|
41
|
+
declare const d1Database: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>() => () => _$_hot_updater_plugin_core0.DatabasePlugin<TContext>;
|
|
42
|
+
declare const r2Storage: <TContext extends RequestEnvContext<CloudflareWorkerRuntimeEnv> = RequestEnvContext<CloudflareWorkerRuntimeEnv>>(config: CloudflareWorkerStorageConfig<TContext>) => () => _$_hot_updater_plugin_core0.RuntimeStoragePlugin<TContext>;
|
|
42
43
|
//#endregion
|
|
43
44
|
export { type CloudflareWorkerDatabaseEnv, CloudflareWorkerRuntimeEnv, type CloudflareWorkerStorageEnv, RequestEnvContext, d1Database, r2Storage, verifyJwtSignedUrl };
|
package/dist/worker/index.mjs
CHANGED
|
@@ -2,6 +2,11 @@ import { signToken, verifyJwtSignedUrl } from "@hot-updater/js";
|
|
|
2
2
|
import { DEFAULT_ROLLOUT_COHORT_COUNT, getAssetBaseStorageUri, getBundlePatches, getManifestFileHash, getManifestStorageUri, stripBundleArtifactMetadata } from "@hot-updater/core";
|
|
3
3
|
import { calculatePagination, createDatabasePlugin, createDatabasePluginGetUpdateInfo, createRuntimeStoragePlugin } from "@hot-updater/plugin-core";
|
|
4
4
|
//#region src/cloudflareWorkerDatabase.ts
|
|
5
|
+
const buildJsonEachInClause = (columnName, values, params) => {
|
|
6
|
+
if (values.length === 0) return "1 = 0";
|
|
7
|
+
params.push(JSON.stringify(values));
|
|
8
|
+
return `${columnName} IN (SELECT value FROM json_each(?))`;
|
|
9
|
+
};
|
|
5
10
|
function buildWhereClause(conditions) {
|
|
6
11
|
if (!conditions) return {
|
|
7
12
|
sql: "",
|
|
@@ -21,11 +26,7 @@ function buildWhereClause(conditions) {
|
|
|
21
26
|
clauses.push("enabled = ?");
|
|
22
27
|
params.push(conditions.enabled ? 1 : 0);
|
|
23
28
|
}
|
|
24
|
-
if (conditions.id?.in)
|
|
25
|
-
else {
|
|
26
|
-
clauses.push(`id IN (${conditions.id.in.map(() => "?").join(", ")})`);
|
|
27
|
-
params.push(...conditions.id.in);
|
|
28
|
-
}
|
|
29
|
+
if (conditions.id?.in) clauses.push(buildJsonEachInClause("id", conditions.id.in, params));
|
|
29
30
|
if (conditions.id?.eq) {
|
|
30
31
|
clauses.push("id = ?");
|
|
31
32
|
params.push(conditions.id.eq);
|
|
@@ -52,11 +53,7 @@ function buildWhereClause(conditions) {
|
|
|
52
53
|
clauses.push("target_app_version = ?");
|
|
53
54
|
params.push(conditions.targetAppVersion);
|
|
54
55
|
}
|
|
55
|
-
if (conditions.targetAppVersionIn)
|
|
56
|
-
else {
|
|
57
|
-
clauses.push(`target_app_version IN (${conditions.targetAppVersionIn.map(() => "?").join(", ")})`);
|
|
58
|
-
params.push(...conditions.targetAppVersionIn);
|
|
59
|
-
}
|
|
56
|
+
if (conditions.targetAppVersionIn) clauses.push(buildJsonEachInClause("target_app_version", conditions.targetAppVersionIn, params));
|
|
60
57
|
if (conditions.fingerprintHash !== void 0) if (conditions.fingerprintHash === null) clauses.push("fingerprint_hash IS NULL");
|
|
61
58
|
else {
|
|
62
59
|
clauses.push("fingerprint_hash = ?");
|
|
@@ -151,9 +148,9 @@ const d1WorkerDatabase = () => createDatabasePlugin({
|
|
|
151
148
|
const rows = await queryAll(`
|
|
152
149
|
SELECT *
|
|
153
150
|
FROM bundle_patches
|
|
154
|
-
WHERE bundle_id IN (
|
|
151
|
+
WHERE bundle_id IN (SELECT value FROM json_each(?))
|
|
155
152
|
ORDER BY order_index ASC, base_bundle_id ASC
|
|
156
|
-
`, bundleIds, context);
|
|
153
|
+
`, [JSON.stringify(bundleIds)], context);
|
|
157
154
|
for (const row of rows) {
|
|
158
155
|
const current = patchMap.get(row.bundle_id) ?? [];
|
|
159
156
|
current.push(row);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.33.0",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -47,14 +47,17 @@
|
|
|
47
47
|
"package.json"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
+
"@aws-sdk/client-s3": "3.1008.0",
|
|
51
|
+
"@aws-sdk/lib-storage": "3.1008.0",
|
|
52
|
+
"@aws-sdk/s3-request-presigner": "3.1008.0",
|
|
50
53
|
"cloudflare": "4.2.0",
|
|
51
54
|
"hono": "4.12.9",
|
|
52
55
|
"uuidv7": "^1.0.2",
|
|
53
|
-
"@hot-updater/
|
|
54
|
-
"@hot-updater/js": "0.
|
|
55
|
-
"@hot-updater/
|
|
56
|
-
"@hot-updater/
|
|
57
|
-
"@hot-updater/
|
|
56
|
+
"@hot-updater/core": "0.33.0",
|
|
57
|
+
"@hot-updater/js": "0.33.0",
|
|
58
|
+
"@hot-updater/cli-tools": "0.33.0",
|
|
59
|
+
"@hot-updater/plugin-core": "0.33.0",
|
|
60
|
+
"@hot-updater/server": "0.33.0"
|
|
58
61
|
},
|
|
59
62
|
"devDependencies": {
|
|
60
63
|
"@cloudflare/vitest-pool-workers": "0.13.0",
|
|
@@ -71,7 +74,7 @@
|
|
|
71
74
|
"vitest": "4.1.4",
|
|
72
75
|
"wrangler": "^4.5.0",
|
|
73
76
|
"xdg-app-paths": "^8.3.0",
|
|
74
|
-
"@hot-updater/test-utils": "0.
|
|
77
|
+
"@hot-updater/test-utils": "0.33.0"
|
|
75
78
|
},
|
|
76
79
|
"scripts": {
|
|
77
80
|
"build": "tsdown && pnpm build:worker",
|