@hot-updater/aws 0.29.5 → 0.29.7
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 +30 -22
- package/dist/iac/index.mjs +36 -27
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/lambda/index.cjs +657 -49
- package/package.json +8 -8
package/dist/iac/index.cjs
CHANGED
|
@@ -22,8 +22,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
enumerable: true
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
//#endregion
|
|
25
|
-
let fs = require("fs");
|
|
26
|
-
fs = __toESM(fs);
|
|
27
25
|
let _aws_sdk_credential_providers = require("@aws-sdk/credential-providers");
|
|
28
26
|
let _hot_updater_cli_tools = require("@hot-updater/cli-tools");
|
|
29
27
|
let node_url = require("node:url");
|
|
@@ -826,7 +824,7 @@ const handleCommand = (filePath, rawArguments, rawOptions) => {
|
|
|
826
824
|
var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
827
825
|
module.exports = isexe;
|
|
828
826
|
isexe.sync = sync;
|
|
829
|
-
var fs$
|
|
827
|
+
var fs$3 = require("fs");
|
|
830
828
|
function checkPathExt(path, options) {
|
|
831
829
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
832
830
|
if (!pathext) return true;
|
|
@@ -843,12 +841,12 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
843
841
|
return checkPathExt(path, options);
|
|
844
842
|
}
|
|
845
843
|
function isexe(path, options, cb) {
|
|
846
|
-
fs$
|
|
844
|
+
fs$3.stat(path, function(er, stat) {
|
|
847
845
|
cb(er, er ? false : checkStat(stat, path, options));
|
|
848
846
|
});
|
|
849
847
|
}
|
|
850
848
|
function sync(path, options) {
|
|
851
|
-
return checkStat(fs$
|
|
849
|
+
return checkStat(fs$3.statSync(path), path, options);
|
|
852
850
|
}
|
|
853
851
|
}));
|
|
854
852
|
//#endregion
|
|
@@ -856,14 +854,14 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
856
854
|
var require_mode = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
857
855
|
module.exports = isexe;
|
|
858
856
|
isexe.sync = sync;
|
|
859
|
-
var fs$
|
|
857
|
+
var fs$2 = require("fs");
|
|
860
858
|
function isexe(path, options, cb) {
|
|
861
|
-
fs$
|
|
859
|
+
fs$2.stat(path, function(er, stat) {
|
|
862
860
|
cb(er, er ? false : checkStat(stat, options));
|
|
863
861
|
});
|
|
864
862
|
}
|
|
865
863
|
function sync(path, options) {
|
|
866
|
-
return checkStat(fs$
|
|
864
|
+
return checkStat(fs$2.statSync(path), options);
|
|
867
865
|
}
|
|
868
866
|
function checkStat(stat, options) {
|
|
869
867
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -1078,16 +1076,16 @@ var require_shebang_command = /* @__PURE__ */ __commonJSMin(((exports, module) =
|
|
|
1078
1076
|
//#endregion
|
|
1079
1077
|
//#region ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
|
|
1080
1078
|
var require_readShebang = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1081
|
-
const fs$
|
|
1079
|
+
const fs$1 = require("fs");
|
|
1082
1080
|
const shebangCommand = require_shebang_command();
|
|
1083
1081
|
function readShebang(command) {
|
|
1084
1082
|
const size = 150;
|
|
1085
1083
|
const buffer = Buffer.alloc(size);
|
|
1086
1084
|
let fd;
|
|
1087
1085
|
try {
|
|
1088
|
-
fd = fs$
|
|
1089
|
-
fs$
|
|
1090
|
-
fs$
|
|
1086
|
+
fd = fs$1.openSync(command, "r");
|
|
1087
|
+
fs$1.readSync(fd, buffer, 0, size, 0);
|
|
1088
|
+
fs$1.closeSync(fd);
|
|
1091
1089
|
} catch (e) {}
|
|
1092
1090
|
return shebangCommand(buffer.toString());
|
|
1093
1091
|
}
|
|
@@ -7327,7 +7325,7 @@ var SSMKeyPairManager = class {
|
|
|
7327
7325
|
};
|
|
7328
7326
|
//#endregion
|
|
7329
7327
|
//#region iac/templates.ts
|
|
7330
|
-
const
|
|
7328
|
+
const getConfigScaffold = (build, { profile }) => {
|
|
7331
7329
|
const storageConfig = {
|
|
7332
7330
|
imports: [{
|
|
7333
7331
|
pkg: "@hot-updater/aws",
|
|
@@ -7345,14 +7343,21 @@ const getConfigTemplate = (build, { profile }) => {
|
|
|
7345
7343
|
cloudfrontDistributionId: process.env.HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID!,
|
|
7346
7344
|
})`
|
|
7347
7345
|
};
|
|
7348
|
-
let
|
|
7349
|
-
if (profile)
|
|
7346
|
+
let helperStatements = [];
|
|
7347
|
+
if (profile) helperStatements = [{
|
|
7348
|
+
name: "commonOptions",
|
|
7349
|
+
strategy: "merge-object",
|
|
7350
|
+
code: `
|
|
7350
7351
|
const commonOptions = {
|
|
7351
7352
|
bucketName: process.env.HOT_UPDATER_S3_BUCKET_NAME!,
|
|
7352
7353
|
region: process.env.HOT_UPDATER_S3_REGION!,
|
|
7353
7354
|
credentials: fromSSO({ profile: process.env.HOT_UPDATER_AWS_PROFILE! }),
|
|
7354
|
-
};`.trim()
|
|
7355
|
-
|
|
7355
|
+
};`.trim()
|
|
7356
|
+
}];
|
|
7357
|
+
else helperStatements = [{
|
|
7358
|
+
name: "commonOptions",
|
|
7359
|
+
strategy: "merge-object",
|
|
7360
|
+
code: `
|
|
7356
7361
|
const commonOptions = {
|
|
7357
7362
|
bucketName: process.env.HOT_UPDATER_S3_BUCKET_NAME!,
|
|
7358
7363
|
region: process.env.HOT_UPDATER_S3_REGION!,
|
|
@@ -7360,13 +7365,14 @@ const commonOptions = {
|
|
|
7360
7365
|
accessKeyId: process.env.HOT_UPDATER_S3_ACCESS_KEY_ID!,
|
|
7361
7366
|
secretAccessKey: process.env.HOT_UPDATER_S3_SECRET_ACCESS_KEY!,
|
|
7362
7367
|
},
|
|
7363
|
-
};`.trim()
|
|
7364
|
-
|
|
7368
|
+
};`.trim()
|
|
7369
|
+
}];
|
|
7370
|
+
const builder = new _hot_updater_cli_tools.ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase(databaseConfig).setIntermediateCode(helperStatements.map((statement) => statement.code.trim()).join("\n\n"));
|
|
7365
7371
|
if (profile) builder.addImport({
|
|
7366
7372
|
pkg: "@aws-sdk/credential-provider-sso",
|
|
7367
7373
|
named: ["fromSSO"]
|
|
7368
7374
|
});
|
|
7369
|
-
return
|
|
7375
|
+
return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder, { helperStatements });
|
|
7370
7376
|
};
|
|
7371
7377
|
const SOURCE_TEMPLATE = `// Add this to your App.tsx
|
|
7372
7378
|
import { HotUpdater } from "@hot-updater/react-native";
|
|
@@ -7536,7 +7542,7 @@ const runInit = async ({ build }) => {
|
|
|
7536
7542
|
distributionId,
|
|
7537
7543
|
accountId
|
|
7538
7544
|
});
|
|
7539
|
-
await
|
|
7545
|
+
const configWriteResult = await (0, _hot_updater_cli_tools.writeHotUpdaterConfig)(getConfigScaffold(build, { profile: ssoProfile }));
|
|
7540
7546
|
await (0, _hot_updater_cli_tools.makeEnv)({
|
|
7541
7547
|
HOT_UPDATER_S3_BUCKET_NAME: bucketName,
|
|
7542
7548
|
HOT_UPDATER_S3_REGION: bucketRegion,
|
|
@@ -7555,7 +7561,9 @@ const runInit = async ({ build }) => {
|
|
|
7555
7561
|
});
|
|
7556
7562
|
if (mode === "sso") await (0, _hot_updater_cli_tools.ensureInstallPackages)({ devDependencies: ["@aws-sdk/credential-provider-sso"] });
|
|
7557
7563
|
_hot_updater_cli_tools.p.log.success("Generated '.env.hotupdater' file with AWS settings.");
|
|
7558
|
-
_hot_updater_cli_tools.p.log.success("Generated 'hot-updater.config.ts' file with AWS settings.");
|
|
7564
|
+
if (configWriteResult.status === "created") _hot_updater_cli_tools.p.log.success("Generated 'hot-updater.config.ts' file with AWS settings.");
|
|
7565
|
+
else if (configWriteResult.status === "merged") _hot_updater_cli_tools.p.log.success("Updated 'hot-updater.config.ts' file with AWS settings.");
|
|
7566
|
+
else _hot_updater_cli_tools.p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
|
|
7559
7567
|
const sourceUrl = `https://${distributionDomain}/api/check-update`;
|
|
7560
7568
|
_hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, { source: sourceUrl }));
|
|
7561
7569
|
_hot_updater_cli_tools.p.log.message(`Next step: ${(0, _hot_updater_cli_tools.link)("https://hot-updater.dev/docs/managed/aws#step-4-changeenv-file-optional")}`);
|
package/dist/iac/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import fs from "fs";
|
|
3
2
|
import { fromSSO } from "@aws-sdk/credential-providers";
|
|
4
|
-
import { ConfigBuilder, colors, copyDirToTmp, createZip, ensureInstallPackages, getCwd, link, makeEnv, p, transformEnv, transformTemplate } from "@hot-updater/cli-tools";
|
|
3
|
+
import { ConfigBuilder, colors, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, createZip, ensureInstallPackages, getCwd, link, makeEnv, p, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
5
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
7
6
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -22,7 +21,7 @@ import crypto from "crypto";
|
|
|
22
21
|
import { CloudFront } from "@aws-sdk/client-cloudfront";
|
|
23
22
|
import { IAM } from "@aws-sdk/client-iam";
|
|
24
23
|
import { STS } from "@aws-sdk/client-sts";
|
|
25
|
-
import fs
|
|
24
|
+
import fs from "fs/promises";
|
|
26
25
|
import { Lambda } from "@aws-sdk/client-lambda";
|
|
27
26
|
import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, ListObjectsV2Command, S3 } from "@aws-sdk/client-s3";
|
|
28
27
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
@@ -820,7 +819,7 @@ const handleCommand = (filePath, rawArguments, rawOptions) => {
|
|
|
820
819
|
var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
821
820
|
module.exports = isexe;
|
|
822
821
|
isexe.sync = sync;
|
|
823
|
-
var fs$
|
|
822
|
+
var fs$3 = __require("fs");
|
|
824
823
|
function checkPathExt(path, options) {
|
|
825
824
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
826
825
|
if (!pathext) return true;
|
|
@@ -837,12 +836,12 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
837
836
|
return checkPathExt(path, options);
|
|
838
837
|
}
|
|
839
838
|
function isexe(path, options, cb) {
|
|
840
|
-
fs$
|
|
839
|
+
fs$3.stat(path, function(er, stat) {
|
|
841
840
|
cb(er, er ? false : checkStat(stat, path, options));
|
|
842
841
|
});
|
|
843
842
|
}
|
|
844
843
|
function sync(path, options) {
|
|
845
|
-
return checkStat(fs$
|
|
844
|
+
return checkStat(fs$3.statSync(path), path, options);
|
|
846
845
|
}
|
|
847
846
|
}));
|
|
848
847
|
//#endregion
|
|
@@ -850,14 +849,14 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
850
849
|
var require_mode = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
851
850
|
module.exports = isexe;
|
|
852
851
|
isexe.sync = sync;
|
|
853
|
-
var fs$
|
|
852
|
+
var fs$2 = __require("fs");
|
|
854
853
|
function isexe(path, options, cb) {
|
|
855
|
-
fs$
|
|
854
|
+
fs$2.stat(path, function(er, stat) {
|
|
856
855
|
cb(er, er ? false : checkStat(stat, options));
|
|
857
856
|
});
|
|
858
857
|
}
|
|
859
858
|
function sync(path, options) {
|
|
860
|
-
return checkStat(fs$
|
|
859
|
+
return checkStat(fs$2.statSync(path), options);
|
|
861
860
|
}
|
|
862
861
|
function checkStat(stat, options) {
|
|
863
862
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -1072,16 +1071,16 @@ var require_shebang_command = /* @__PURE__ */ __commonJSMin(((exports, module) =
|
|
|
1072
1071
|
//#endregion
|
|
1073
1072
|
//#region ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
|
|
1074
1073
|
var require_readShebang = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1075
|
-
const fs$
|
|
1074
|
+
const fs$1 = __require("fs");
|
|
1076
1075
|
const shebangCommand = require_shebang_command();
|
|
1077
1076
|
function readShebang(command) {
|
|
1078
1077
|
const size = 150;
|
|
1079
1078
|
const buffer = Buffer.alloc(size);
|
|
1080
1079
|
let fd;
|
|
1081
1080
|
try {
|
|
1082
|
-
fd = fs$
|
|
1083
|
-
fs$
|
|
1084
|
-
fs$
|
|
1081
|
+
fd = fs$1.openSync(command, "r");
|
|
1082
|
+
fs$1.readSync(fd, buffer, 0, size, 0);
|
|
1083
|
+
fs$1.closeSync(fd);
|
|
1085
1084
|
} catch (e) {}
|
|
1086
1085
|
return shebangCommand(buffer.toString());
|
|
1087
1086
|
}
|
|
@@ -6750,7 +6749,7 @@ var LambdaEdgeDeployer = class {
|
|
|
6750
6749
|
SSM_REGION: config.ssmRegion,
|
|
6751
6750
|
S3_BUCKET_NAME: config.bucketName
|
|
6752
6751
|
});
|
|
6753
|
-
await fs
|
|
6752
|
+
await fs.writeFile(indexPath, code);
|
|
6754
6753
|
const lambdaClient = new Lambda({
|
|
6755
6754
|
region: "us-east-1",
|
|
6756
6755
|
credentials: this.credentials
|
|
@@ -6784,7 +6783,7 @@ var LambdaEdgeDeployer = class {
|
|
|
6784
6783
|
Runtime: "nodejs22.x",
|
|
6785
6784
|
Role: lambdaRoleArn,
|
|
6786
6785
|
Handler: "index.handler",
|
|
6787
|
-
Code: { ZipFile: await fs
|
|
6786
|
+
Code: { ZipFile: await fs.readFile(zipFilePath) },
|
|
6788
6787
|
Description: "Hot Updater Lambda@Edge function",
|
|
6789
6788
|
Publish: true,
|
|
6790
6789
|
Timeout: 10
|
|
@@ -6797,7 +6796,7 @@ var LambdaEdgeDeployer = class {
|
|
|
6797
6796
|
message(`Function "${lambdaName}" already exists. Updating function code...`);
|
|
6798
6797
|
const updateResp = await lambdaClient.updateFunctionCode({
|
|
6799
6798
|
FunctionName: lambdaName,
|
|
6800
|
-
ZipFile: await fs
|
|
6799
|
+
ZipFile: await fs.readFile(zipFilePath),
|
|
6801
6800
|
Publish: true
|
|
6802
6801
|
});
|
|
6803
6802
|
message("Waiting for Lambda function update to complete...");
|
|
@@ -6829,7 +6828,7 @@ var LambdaEdgeDeployer = class {
|
|
|
6829
6828
|
return `Updated Lambda "${lambdaName}" function`;
|
|
6830
6829
|
} finally {
|
|
6831
6830
|
removeTmpDir();
|
|
6832
|
-
fs
|
|
6831
|
+
fs.rm(zipFilePath, { force: true });
|
|
6833
6832
|
}
|
|
6834
6833
|
}
|
|
6835
6834
|
},
|
|
@@ -7321,7 +7320,7 @@ var SSMKeyPairManager = class {
|
|
|
7321
7320
|
};
|
|
7322
7321
|
//#endregion
|
|
7323
7322
|
//#region iac/templates.ts
|
|
7324
|
-
const
|
|
7323
|
+
const getConfigScaffold = (build, { profile }) => {
|
|
7325
7324
|
const storageConfig = {
|
|
7326
7325
|
imports: [{
|
|
7327
7326
|
pkg: "@hot-updater/aws",
|
|
@@ -7339,14 +7338,21 @@ const getConfigTemplate = (build, { profile }) => {
|
|
|
7339
7338
|
cloudfrontDistributionId: process.env.HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID!,
|
|
7340
7339
|
})`
|
|
7341
7340
|
};
|
|
7342
|
-
let
|
|
7343
|
-
if (profile)
|
|
7341
|
+
let helperStatements = [];
|
|
7342
|
+
if (profile) helperStatements = [{
|
|
7343
|
+
name: "commonOptions",
|
|
7344
|
+
strategy: "merge-object",
|
|
7345
|
+
code: `
|
|
7344
7346
|
const commonOptions = {
|
|
7345
7347
|
bucketName: process.env.HOT_UPDATER_S3_BUCKET_NAME!,
|
|
7346
7348
|
region: process.env.HOT_UPDATER_S3_REGION!,
|
|
7347
7349
|
credentials: fromSSO({ profile: process.env.HOT_UPDATER_AWS_PROFILE! }),
|
|
7348
|
-
};`.trim()
|
|
7349
|
-
|
|
7350
|
+
};`.trim()
|
|
7351
|
+
}];
|
|
7352
|
+
else helperStatements = [{
|
|
7353
|
+
name: "commonOptions",
|
|
7354
|
+
strategy: "merge-object",
|
|
7355
|
+
code: `
|
|
7350
7356
|
const commonOptions = {
|
|
7351
7357
|
bucketName: process.env.HOT_UPDATER_S3_BUCKET_NAME!,
|
|
7352
7358
|
region: process.env.HOT_UPDATER_S3_REGION!,
|
|
@@ -7354,13 +7360,14 @@ const commonOptions = {
|
|
|
7354
7360
|
accessKeyId: process.env.HOT_UPDATER_S3_ACCESS_KEY_ID!,
|
|
7355
7361
|
secretAccessKey: process.env.HOT_UPDATER_S3_SECRET_ACCESS_KEY!,
|
|
7356
7362
|
},
|
|
7357
|
-
};`.trim()
|
|
7358
|
-
|
|
7363
|
+
};`.trim()
|
|
7364
|
+
}];
|
|
7365
|
+
const builder = new ConfigBuilder().setBuildType(build).setStorage(storageConfig).setDatabase(databaseConfig).setIntermediateCode(helperStatements.map((statement) => statement.code.trim()).join("\n\n"));
|
|
7359
7366
|
if (profile) builder.addImport({
|
|
7360
7367
|
pkg: "@aws-sdk/credential-provider-sso",
|
|
7361
7368
|
named: ["fromSSO"]
|
|
7362
7369
|
});
|
|
7363
|
-
return builder
|
|
7370
|
+
return createHotUpdaterConfigScaffoldFromBuilder(builder, { helperStatements });
|
|
7364
7371
|
};
|
|
7365
7372
|
const SOURCE_TEMPLATE = `// Add this to your App.tsx
|
|
7366
7373
|
import { HotUpdater } from "@hot-updater/react-native";
|
|
@@ -7530,7 +7537,7 @@ const runInit = async ({ build }) => {
|
|
|
7530
7537
|
distributionId,
|
|
7531
7538
|
accountId
|
|
7532
7539
|
});
|
|
7533
|
-
await
|
|
7540
|
+
const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build, { profile: ssoProfile }));
|
|
7534
7541
|
await makeEnv({
|
|
7535
7542
|
HOT_UPDATER_S3_BUCKET_NAME: bucketName,
|
|
7536
7543
|
HOT_UPDATER_S3_REGION: bucketRegion,
|
|
@@ -7549,7 +7556,9 @@ const runInit = async ({ build }) => {
|
|
|
7549
7556
|
});
|
|
7550
7557
|
if (mode === "sso") await ensureInstallPackages({ devDependencies: ["@aws-sdk/credential-provider-sso"] });
|
|
7551
7558
|
p.log.success("Generated '.env.hotupdater' file with AWS settings.");
|
|
7552
|
-
p.log.success("Generated 'hot-updater.config.ts' file with AWS settings.");
|
|
7559
|
+
if (configWriteResult.status === "created") p.log.success("Generated 'hot-updater.config.ts' file with AWS settings.");
|
|
7560
|
+
else if (configWriteResult.status === "merged") p.log.success("Updated 'hot-updater.config.ts' file with AWS settings.");
|
|
7561
|
+
else p.log.warn(`Kept existing 'hot-updater.config.ts' unchanged: ${configWriteResult.reason}`);
|
|
7553
7562
|
const sourceUrl = `https://${distributionDomain}/api/check-update`;
|
|
7554
7563
|
p.note(transformTemplate(SOURCE_TEMPLATE, { source: sourceUrl }));
|
|
7555
7564
|
p.log.message(`Next step: ${link("https://hot-updater.dev/docs/managed/aws#step-4-changeenv-file-optional")}`);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
|
-
import { StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
2
|
+
import { BlobDatabasePluginConfig, StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
3
3
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
4
4
|
|
|
5
5
|
//#region src/s3Database.d.ts
|
|
6
|
-
interface S3DatabaseConfig extends S3ClientConfig {
|
|
6
|
+
interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
7
7
|
bucketName: string;
|
|
8
8
|
/**
|
|
9
9
|
* CloudFront distribution ID used for cache invalidation.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
2
2
|
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
3
|
-
import { StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
3
|
+
import { BlobDatabasePluginConfig, StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
4
4
|
|
|
5
5
|
//#region src/s3Database.d.ts
|
|
6
|
-
interface S3DatabaseConfig extends S3ClientConfig {
|
|
6
|
+
interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
7
7
|
bucketName: string;
|
|
8
8
|
/**
|
|
9
9
|
* CloudFront distribution ID used for cache invalidation.
|
package/dist/lambda/index.cjs
CHANGED
|
@@ -1577,11 +1577,71 @@ function mergeWith(target, source, merge) {
|
|
|
1577
1577
|
//#endregion
|
|
1578
1578
|
//#region ../plugin-core/dist/createDatabasePlugin.mjs
|
|
1579
1579
|
const REPLACE_ON_UPDATE_KEYS = ["targetCohorts"];
|
|
1580
|
+
const DEFAULT_DESC_ORDER$1 = {
|
|
1581
|
+
field: "id",
|
|
1582
|
+
direction: "desc"
|
|
1583
|
+
};
|
|
1584
|
+
function normalizePage(value) {
|
|
1585
|
+
if (!Number.isInteger(value) || value === void 0 || value < 1) return;
|
|
1586
|
+
return value;
|
|
1587
|
+
}
|
|
1580
1588
|
function mergeBundleUpdate(baseBundle, patch) {
|
|
1581
1589
|
return mergeWith(baseBundle, patch, (_targetValue, sourceValue, key) => {
|
|
1582
1590
|
if (REPLACE_ON_UPDATE_KEYS.includes(key)) return sourceValue;
|
|
1583
1591
|
});
|
|
1584
1592
|
}
|
|
1593
|
+
function mergeIdFilter(base, patch) {
|
|
1594
|
+
return {
|
|
1595
|
+
...base,
|
|
1596
|
+
...patch
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
function mergeWhereWithIdFilter(where, idFilter) {
|
|
1600
|
+
return {
|
|
1601
|
+
...where,
|
|
1602
|
+
id: mergeIdFilter(where?.id, idFilter)
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
function buildCursorPageQuery(where, cursor, orderBy) {
|
|
1606
|
+
const direction = orderBy.direction;
|
|
1607
|
+
if (cursor.after) return {
|
|
1608
|
+
reverseData: false,
|
|
1609
|
+
where: mergeWhereWithIdFilter(where, { [direction === "desc" ? "lt" : "gt"]: cursor.after }),
|
|
1610
|
+
orderBy
|
|
1611
|
+
};
|
|
1612
|
+
if (cursor.before) return {
|
|
1613
|
+
reverseData: true,
|
|
1614
|
+
where: mergeWhereWithIdFilter(where, { [direction === "desc" ? "gt" : "lt"]: cursor.before }),
|
|
1615
|
+
orderBy: {
|
|
1616
|
+
field: orderBy.field,
|
|
1617
|
+
direction: direction === "desc" ? "asc" : "desc"
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
return {
|
|
1621
|
+
reverseData: false,
|
|
1622
|
+
where: where ?? {},
|
|
1623
|
+
orderBy
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function buildCountBeforeWhere(where, firstBundleId, orderBy) {
|
|
1627
|
+
return mergeWhereWithIdFilter(where, { [orderBy.direction === "desc" ? "gt" : "lt"]: firstBundleId });
|
|
1628
|
+
}
|
|
1629
|
+
function createPaginatedResult(total, limit, startIndex, data) {
|
|
1630
|
+
const pagination = calculatePagination(total, {
|
|
1631
|
+
limit,
|
|
1632
|
+
offset: startIndex
|
|
1633
|
+
});
|
|
1634
|
+
const nextCursor = data.length > 0 && startIndex + data.length < total ? data.at(-1)?.id : void 0;
|
|
1635
|
+
const previousCursor = data.length > 0 && startIndex > 0 ? data[0]?.id : void 0;
|
|
1636
|
+
return {
|
|
1637
|
+
data,
|
|
1638
|
+
pagination: {
|
|
1639
|
+
...pagination,
|
|
1640
|
+
...nextCursor ? { nextCursor } : {},
|
|
1641
|
+
...previousCursor ? { previousCursor } : {}
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1585
1645
|
/**
|
|
1586
1646
|
* Creates a database plugin with lazy initialization and automatic hook execution.
|
|
1587
1647
|
*
|
|
@@ -1623,6 +1683,59 @@ function createDatabasePlugin(options) {
|
|
|
1623
1683
|
data
|
|
1624
1684
|
});
|
|
1625
1685
|
};
|
|
1686
|
+
const runGetBundles = async (options, context) => {
|
|
1687
|
+
if (context === void 0) return getMethods().getBundles(options);
|
|
1688
|
+
return getMethods().getBundles(options, context);
|
|
1689
|
+
};
|
|
1690
|
+
const getBundlesWithLegacyCursorFallback = async (options, context) => {
|
|
1691
|
+
const orderBy = options.orderBy ?? DEFAULT_DESC_ORDER$1;
|
|
1692
|
+
const baseWhere = options.where;
|
|
1693
|
+
const total = (await runGetBundles({
|
|
1694
|
+
where: baseWhere,
|
|
1695
|
+
limit: 1,
|
|
1696
|
+
offset: 0,
|
|
1697
|
+
orderBy
|
|
1698
|
+
}, context)).pagination.total;
|
|
1699
|
+
if (!options.cursor?.after && !options.cursor?.before) {
|
|
1700
|
+
const firstPage = await runGetBundles({
|
|
1701
|
+
where: baseWhere,
|
|
1702
|
+
limit: options.limit,
|
|
1703
|
+
offset: 0,
|
|
1704
|
+
orderBy
|
|
1705
|
+
}, context);
|
|
1706
|
+
return createPaginatedResult(total, options.limit, 0, firstPage.data);
|
|
1707
|
+
}
|
|
1708
|
+
const { where, orderBy: queryOrderBy, reverseData } = buildCursorPageQuery(baseWhere, options.cursor, orderBy);
|
|
1709
|
+
const cursorPage = await runGetBundles({
|
|
1710
|
+
where,
|
|
1711
|
+
limit: options.limit,
|
|
1712
|
+
offset: 0,
|
|
1713
|
+
orderBy: queryOrderBy
|
|
1714
|
+
}, context);
|
|
1715
|
+
const data = reverseData ? cursorPage.data.slice().reverse() : cursorPage.data;
|
|
1716
|
+
if (data.length === 0) {
|
|
1717
|
+
const emptyStartIndex = options.cursor.after ? total : 0;
|
|
1718
|
+
return {
|
|
1719
|
+
data,
|
|
1720
|
+
pagination: {
|
|
1721
|
+
...calculatePagination(total, {
|
|
1722
|
+
limit: options.limit,
|
|
1723
|
+
offset: emptyStartIndex
|
|
1724
|
+
}),
|
|
1725
|
+
...options.cursor.after ? { previousCursor: options.cursor.after } : {},
|
|
1726
|
+
...options.cursor.before ? { nextCursor: options.cursor.before } : {}
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
const firstBundleId = data[0].id;
|
|
1731
|
+
const countBeforeResult = await runGetBundles({
|
|
1732
|
+
where: buildCountBeforeWhere(baseWhere, firstBundleId, orderBy),
|
|
1733
|
+
limit: 1,
|
|
1734
|
+
offset: 0,
|
|
1735
|
+
orderBy
|
|
1736
|
+
}, context);
|
|
1737
|
+
return createPaginatedResult(total, options.limit, countBeforeResult.pagination.total, data);
|
|
1738
|
+
};
|
|
1626
1739
|
const plugin = {
|
|
1627
1740
|
name: options.name,
|
|
1628
1741
|
async getBundleById(bundleId, context) {
|
|
@@ -1630,8 +1743,35 @@ function createDatabasePlugin(options) {
|
|
|
1630
1743
|
return getMethods().getBundleById(bundleId, context);
|
|
1631
1744
|
},
|
|
1632
1745
|
async getBundles(options, context) {
|
|
1633
|
-
if (
|
|
1634
|
-
|
|
1746
|
+
if (typeof options === "object" && options !== null && "offset" in options && options.offset !== void 0) throw new Error("Bundle offset pagination has been removed. Use cursor.after or cursor.before instead.");
|
|
1747
|
+
const methods = getMethods();
|
|
1748
|
+
const normalizedOptions = {
|
|
1749
|
+
...options,
|
|
1750
|
+
page: normalizePage(options.page),
|
|
1751
|
+
orderBy: options.orderBy ?? DEFAULT_DESC_ORDER$1
|
|
1752
|
+
};
|
|
1753
|
+
if (normalizedOptions.page !== void 0) {
|
|
1754
|
+
const { page, ...pageOptions } = normalizedOptions;
|
|
1755
|
+
const requestedOffset = (page - 1) * normalizedOptions.limit;
|
|
1756
|
+
let pageResult = await runGetBundles({
|
|
1757
|
+
...pageOptions,
|
|
1758
|
+
offset: requestedOffset
|
|
1759
|
+
}, context);
|
|
1760
|
+
const total = pageResult.pagination.total;
|
|
1761
|
+
const totalPages = total === 0 ? 0 : Math.ceil(total / normalizedOptions.limit);
|
|
1762
|
+
const maxOffset = totalPages === 0 ? 0 : (Math.max(1, totalPages) - 1) * normalizedOptions.limit;
|
|
1763
|
+
const resolvedOffset = Math.min(requestedOffset, maxOffset);
|
|
1764
|
+
if (resolvedOffset !== requestedOffset) pageResult = await runGetBundles({
|
|
1765
|
+
...pageOptions,
|
|
1766
|
+
offset: resolvedOffset
|
|
1767
|
+
}, context);
|
|
1768
|
+
return createPaginatedResult(total, normalizedOptions.limit, resolvedOffset, pageResult.data);
|
|
1769
|
+
}
|
|
1770
|
+
if (methods.supportsCursorPagination) {
|
|
1771
|
+
if (context === void 0) return methods.getBundles(normalizedOptions);
|
|
1772
|
+
return methods.getBundles(normalizedOptions, context);
|
|
1773
|
+
}
|
|
1774
|
+
return getBundlesWithLegacyCursorFallback(normalizedOptions, context);
|
|
1635
1775
|
},
|
|
1636
1776
|
async getChannels(context) {
|
|
1637
1777
|
if (context === void 0) return getMethods().getChannels();
|
|
@@ -3084,6 +3224,56 @@ function sortBundles$1(bundles, orderBy) {
|
|
|
3084
3224
|
});
|
|
3085
3225
|
}
|
|
3086
3226
|
//#endregion
|
|
3227
|
+
//#region ../plugin-core/dist/paginateBundles.mjs
|
|
3228
|
+
function paginateBundles({ bundles, limit, offset, cursor, orderBy }) {
|
|
3229
|
+
const sortedBundles = sortBundles$1(bundles, orderBy);
|
|
3230
|
+
const direction = orderBy?.direction ?? "desc";
|
|
3231
|
+
const total = sortedBundles.length;
|
|
3232
|
+
if (offset !== void 0) {
|
|
3233
|
+
const normalizedOffset = Math.max(0, offset);
|
|
3234
|
+
const data = limit > 0 ? sortedBundles.slice(normalizedOffset, normalizedOffset + limit) : sortedBundles.slice(normalizedOffset);
|
|
3235
|
+
const pagination = calculatePagination(total, {
|
|
3236
|
+
limit,
|
|
3237
|
+
offset: normalizedOffset
|
|
3238
|
+
});
|
|
3239
|
+
const nextCursor = data.length > 0 && normalizedOffset + data.length < total ? data.at(-1)?.id : void 0;
|
|
3240
|
+
const previousCursor = data.length > 0 && normalizedOffset > 0 ? data[0]?.id : void 0;
|
|
3241
|
+
return {
|
|
3242
|
+
data,
|
|
3243
|
+
pagination: {
|
|
3244
|
+
...pagination,
|
|
3245
|
+
...nextCursor ? { nextCursor } : {},
|
|
3246
|
+
...previousCursor ? { previousCursor } : {}
|
|
3247
|
+
}
|
|
3248
|
+
};
|
|
3249
|
+
}
|
|
3250
|
+
let data;
|
|
3251
|
+
if (cursor?.after) {
|
|
3252
|
+
const candidates = sortedBundles.filter((bundle) => direction === "desc" ? bundle.id.localeCompare(cursor.after) < 0 : bundle.id.localeCompare(cursor.after) > 0);
|
|
3253
|
+
data = limit > 0 ? candidates.slice(0, limit) : candidates;
|
|
3254
|
+
} else if (cursor?.before) {
|
|
3255
|
+
const candidates = sortedBundles.filter((bundle) => direction === "desc" ? bundle.id.localeCompare(cursor.before) > 0 : bundle.id.localeCompare(cursor.before) < 0);
|
|
3256
|
+
data = limit > 0 ? candidates.slice(Math.max(0, candidates.length - limit)) : candidates;
|
|
3257
|
+
} else data = limit > 0 ? sortedBundles.slice(0, limit) : sortedBundles;
|
|
3258
|
+
const startIndex = data.length > 0 ? sortedBundles.findIndex((bundle) => bundle.id === data[0].id) : cursor?.after ? total : 0;
|
|
3259
|
+
const pagination = calculatePagination(total, {
|
|
3260
|
+
limit,
|
|
3261
|
+
offset: startIndex
|
|
3262
|
+
});
|
|
3263
|
+
const nextCursor = data.length > 0 && startIndex + data.length < total ? data.at(-1)?.id : void 0;
|
|
3264
|
+
const previousCursor = data.length > 0 && startIndex > 0 ? data[0]?.id : void 0;
|
|
3265
|
+
return {
|
|
3266
|
+
data,
|
|
3267
|
+
pagination: {
|
|
3268
|
+
...pagination,
|
|
3269
|
+
...nextCursor ? { nextCursor } : {},
|
|
3270
|
+
...previousCursor ? { previousCursor } : {},
|
|
3271
|
+
...data.length === 0 && cursor?.after ? { previousCursor: cursor.after } : {},
|
|
3272
|
+
...data.length === 0 && cursor?.before ? { nextCursor: cursor.before } : {}
|
|
3273
|
+
}
|
|
3274
|
+
};
|
|
3275
|
+
}
|
|
3276
|
+
//#endregion
|
|
3087
3277
|
//#region ../js/dist/index.mjs
|
|
3088
3278
|
var __create = Object.create;
|
|
3089
3279
|
var __defProp = Object.defineProperty;
|
|
@@ -4490,6 +4680,135 @@ function resolveStorageTarget({ targetAppVersion, fingerprintHash }) {
|
|
|
4490
4680
|
if (!target) throw new Error("target not found");
|
|
4491
4681
|
return target;
|
|
4492
4682
|
}
|
|
4683
|
+
const DEFAULT_DESC_ORDER = {
|
|
4684
|
+
field: "id",
|
|
4685
|
+
direction: "desc"
|
|
4686
|
+
};
|
|
4687
|
+
const MANAGEMENT_INDEX_PREFIX = "_index";
|
|
4688
|
+
const MANAGEMENT_INDEX_VERSION = 1;
|
|
4689
|
+
const DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE = 128;
|
|
4690
|
+
const ALL_SCOPE_CACHE_KEY = "*|*";
|
|
4691
|
+
function resolveManagementIndexPageSize(config) {
|
|
4692
|
+
const pageSize = config.managementIndexPageSize ?? DEFAULT_MANAGEMENT_INDEX_PAGE_SIZE;
|
|
4693
|
+
if (!Number.isInteger(pageSize) || pageSize < 1) throw new Error("managementIndexPageSize must be a positive integer.");
|
|
4694
|
+
return pageSize;
|
|
4695
|
+
}
|
|
4696
|
+
function sortManagedBundles(bundles, orderBy = DEFAULT_DESC_ORDER) {
|
|
4697
|
+
return sortBundles$1(bundles, orderBy);
|
|
4698
|
+
}
|
|
4699
|
+
function isDefaultManagementOrder(orderBy) {
|
|
4700
|
+
return orderBy === void 0 || orderBy.field === DEFAULT_DESC_ORDER.field && orderBy.direction === DEFAULT_DESC_ORDER.direction;
|
|
4701
|
+
}
|
|
4702
|
+
function hasUnsupportedManagementFilters(where) {
|
|
4703
|
+
if (!where) return false;
|
|
4704
|
+
return Boolean(where.enabled !== void 0 || where.id !== void 0 || where.targetAppVersion !== void 0 || where.targetAppVersionIn !== void 0 || where.targetAppVersionNotNull !== void 0 || where.fingerprintHash !== void 0);
|
|
4705
|
+
}
|
|
4706
|
+
function getSupportedManagementScope(where, orderBy) {
|
|
4707
|
+
if (!isDefaultManagementOrder(orderBy) || hasUnsupportedManagementFilters(where)) return null;
|
|
4708
|
+
return {
|
|
4709
|
+
channel: where?.channel,
|
|
4710
|
+
platform: where?.platform
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
function encodeScopePart(value) {
|
|
4714
|
+
return encodeURIComponent(value);
|
|
4715
|
+
}
|
|
4716
|
+
function getManagementScopeCacheKey({ channel, platform }) {
|
|
4717
|
+
return `${channel ?? "*"}|${platform ?? "*"}`;
|
|
4718
|
+
}
|
|
4719
|
+
function getManagementScopePrefix({ channel, platform }) {
|
|
4720
|
+
if (channel && platform) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}/platform/${platform}`;
|
|
4721
|
+
if (channel) return `${MANAGEMENT_INDEX_PREFIX}/channel/${encodeScopePart(channel)}`;
|
|
4722
|
+
if (platform) return `${MANAGEMENT_INDEX_PREFIX}/platform/${platform}`;
|
|
4723
|
+
return `${MANAGEMENT_INDEX_PREFIX}/all`;
|
|
4724
|
+
}
|
|
4725
|
+
function getManagementRootKey(scope) {
|
|
4726
|
+
return `${getManagementScopePrefix(scope)}/root.json`;
|
|
4727
|
+
}
|
|
4728
|
+
function getManagementPageKey(scope, pageIndex) {
|
|
4729
|
+
return `${getManagementScopePrefix(scope)}/pages/${String(pageIndex).padStart(4, "0")}.json`;
|
|
4730
|
+
}
|
|
4731
|
+
function createBundleWithUpdateJsonKey(bundle) {
|
|
4732
|
+
const target = resolveStorageTarget(bundle);
|
|
4733
|
+
return {
|
|
4734
|
+
...bundle,
|
|
4735
|
+
_updateJsonKey: `${bundle.channel}/${bundle.platform}/${target}/update.json`
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4738
|
+
function getPageStartOffsets(pages) {
|
|
4739
|
+
const startOffsets = [];
|
|
4740
|
+
let offset = 0;
|
|
4741
|
+
for (const page of pages) {
|
|
4742
|
+
startOffsets.push(offset);
|
|
4743
|
+
offset += page.count;
|
|
4744
|
+
}
|
|
4745
|
+
return startOffsets;
|
|
4746
|
+
}
|
|
4747
|
+
function createEmptyManagementResult(limit) {
|
|
4748
|
+
return {
|
|
4749
|
+
data: [],
|
|
4750
|
+
pagination: calculatePagination(0, {
|
|
4751
|
+
limit,
|
|
4752
|
+
offset: 0
|
|
4753
|
+
})
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
function buildManagementIndexArtifacts(allBundles, pageSize) {
|
|
4757
|
+
const sortedAllBundles = sortManagedBundles(allBundles);
|
|
4758
|
+
const pages = /* @__PURE__ */ new Map();
|
|
4759
|
+
const scopes = [];
|
|
4760
|
+
const channels = [...new Set(sortedAllBundles.map((bundle) => bundle.channel))].sort();
|
|
4761
|
+
const addScope = (scope, scopeBundles, options) => {
|
|
4762
|
+
if (!options?.includeChannels && scopeBundles.length === 0) return;
|
|
4763
|
+
const pageKeys = [];
|
|
4764
|
+
const pageDescriptors = [];
|
|
4765
|
+
for (let pageIndex = 0; pageIndex * pageSize < scopeBundles.length; pageIndex++) {
|
|
4766
|
+
const page = scopeBundles.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
|
|
4767
|
+
const key = getManagementPageKey(scope, pageIndex);
|
|
4768
|
+
pages.set(key, page);
|
|
4769
|
+
pageKeys.push(key);
|
|
4770
|
+
pageDescriptors.push({
|
|
4771
|
+
key,
|
|
4772
|
+
count: page.length,
|
|
4773
|
+
firstId: page[0].id,
|
|
4774
|
+
lastId: page.at(-1).id
|
|
4775
|
+
});
|
|
4776
|
+
}
|
|
4777
|
+
const root = {
|
|
4778
|
+
version: MANAGEMENT_INDEX_VERSION,
|
|
4779
|
+
pageSize,
|
|
4780
|
+
total: scopeBundles.length,
|
|
4781
|
+
pages: pageDescriptors,
|
|
4782
|
+
...options?.includeChannels ? { channels } : {}
|
|
4783
|
+
};
|
|
4784
|
+
scopes.push({
|
|
4785
|
+
cacheKey: getManagementScopeCacheKey(scope),
|
|
4786
|
+
rootKey: getManagementRootKey(scope),
|
|
4787
|
+
root,
|
|
4788
|
+
pageKeys
|
|
4789
|
+
});
|
|
4790
|
+
};
|
|
4791
|
+
addScope({}, sortedAllBundles, { includeChannels: true });
|
|
4792
|
+
for (const channel of channels) {
|
|
4793
|
+
const channelBundles = sortedAllBundles.filter((bundle) => bundle.channel === channel);
|
|
4794
|
+
addScope({ channel }, channelBundles);
|
|
4795
|
+
for (const platform of ["ios", "android"]) {
|
|
4796
|
+
const scopedBundles = channelBundles.filter((bundle) => bundle.platform === platform);
|
|
4797
|
+
addScope({
|
|
4798
|
+
channel,
|
|
4799
|
+
platform
|
|
4800
|
+
}, scopedBundles);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
for (const platform of ["ios", "android"]) {
|
|
4804
|
+
const platformBundles = sortedAllBundles.filter((bundle) => bundle.platform === platform);
|
|
4805
|
+
addScope({ platform }, platformBundles);
|
|
4806
|
+
}
|
|
4807
|
+
return {
|
|
4808
|
+
pages,
|
|
4809
|
+
scopes
|
|
4810
|
+
};
|
|
4811
|
+
}
|
|
4493
4812
|
/**
|
|
4494
4813
|
* Creates a blob storage-based database plugin with lazy initialization.
|
|
4495
4814
|
*
|
|
@@ -4499,22 +4818,282 @@ function resolveStorageTarget({ targetAppVersion, fingerprintHash }) {
|
|
|
4499
4818
|
*/
|
|
4500
4819
|
const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
4501
4820
|
return (config, hooks) => {
|
|
4821
|
+
const managementIndexPageSize = resolveManagementIndexPageSize(config);
|
|
4502
4822
|
const { listObjects, loadObject, uploadObject, deleteObject, invalidatePaths, apiBasePath } = factory(config);
|
|
4503
4823
|
const bundlesMap = /* @__PURE__ */ new Map();
|
|
4504
4824
|
const pendingBundlesMap = /* @__PURE__ */ new Map();
|
|
4825
|
+
const managementRootCache = /* @__PURE__ */ new Map();
|
|
4505
4826
|
const PLATFORMS = ["ios", "android"];
|
|
4827
|
+
const getAllManagementArtifact = (artifacts) => {
|
|
4828
|
+
const allArtifact = artifacts.scopes.find((scope) => scope.cacheKey === ALL_SCOPE_CACHE_KEY);
|
|
4829
|
+
if (!allArtifact) throw new Error("all-bundles management index artifact not found");
|
|
4830
|
+
return allArtifact;
|
|
4831
|
+
};
|
|
4832
|
+
const replaceManagementRootCache = (artifacts) => {
|
|
4833
|
+
managementRootCache.clear();
|
|
4834
|
+
for (const scope of artifacts.scopes) managementRootCache.set(scope.cacheKey, scope.root);
|
|
4835
|
+
};
|
|
4836
|
+
const createHydratedBundle = (bundle) => {
|
|
4837
|
+
const hydratedBundle = createBundleWithUpdateJsonKey(bundle);
|
|
4838
|
+
bundlesMap.set(hydratedBundle.id, hydratedBundle);
|
|
4839
|
+
return hydratedBundle;
|
|
4840
|
+
};
|
|
4841
|
+
const loadStoredManagementRoot = async (scope) => {
|
|
4842
|
+
const cacheKey = getManagementScopeCacheKey(scope);
|
|
4843
|
+
const storedRoot = await loadObject(getManagementRootKey(scope));
|
|
4844
|
+
if (storedRoot) {
|
|
4845
|
+
managementRootCache.set(cacheKey, storedRoot);
|
|
4846
|
+
return storedRoot;
|
|
4847
|
+
}
|
|
4848
|
+
managementRootCache.delete(cacheKey);
|
|
4849
|
+
return null;
|
|
4850
|
+
};
|
|
4851
|
+
const loadManagementPage = async (descriptor, pageCache) => {
|
|
4852
|
+
if (pageCache?.has(descriptor.key)) return pageCache.get(descriptor.key) ?? null;
|
|
4853
|
+
const page = await loadObject(descriptor.key);
|
|
4854
|
+
pageCache?.set(descriptor.key, page);
|
|
4855
|
+
return page;
|
|
4856
|
+
};
|
|
4857
|
+
const loadBundleFromManagementRoot = async (root, bundleId) => {
|
|
4858
|
+
const pageIndex = findPageIndexContainingId(root.pages, bundleId);
|
|
4859
|
+
if (pageIndex < 0) return null;
|
|
4860
|
+
const descriptor = root.pages[pageIndex];
|
|
4861
|
+
const page = await loadManagementPage(descriptor);
|
|
4862
|
+
if (!page) return null;
|
|
4863
|
+
return page.find((item) => item.id === bundleId) ?? null;
|
|
4864
|
+
};
|
|
4865
|
+
const loadAllBundlesFromRoot = async (root) => {
|
|
4866
|
+
const allBundles = [];
|
|
4867
|
+
const pageCache = /* @__PURE__ */ new Map();
|
|
4868
|
+
for (const descriptor of root.pages) {
|
|
4869
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
4870
|
+
if (!page) return null;
|
|
4871
|
+
allBundles.push(...page);
|
|
4872
|
+
}
|
|
4873
|
+
return allBundles;
|
|
4874
|
+
};
|
|
4875
|
+
const persistManagementIndexArtifacts = async (nextArtifacts, previousArtifacts) => {
|
|
4876
|
+
for (const [key, page] of nextArtifacts.pages.entries()) await uploadObject(key, page);
|
|
4877
|
+
for (const scope of nextArtifacts.scopes) await uploadObject(scope.rootKey, scope.root);
|
|
4878
|
+
if (!previousArtifacts) return;
|
|
4879
|
+
const nextPageKeys = new Set(nextArtifacts.pages.keys());
|
|
4880
|
+
const nextRootKeys = new Set(nextArtifacts.scopes.map((scope) => scope.rootKey));
|
|
4881
|
+
for (const [key] of previousArtifacts.pages.entries()) if (!nextPageKeys.has(key)) await deleteObject(key).catch(() => {});
|
|
4882
|
+
for (const scope of previousArtifacts.scopes) if (!nextRootKeys.has(scope.rootKey)) await deleteObject(scope.rootKey).catch(() => {});
|
|
4883
|
+
};
|
|
4884
|
+
const ensureAllManagementRoot = async () => {
|
|
4885
|
+
const storedAllRoot = await loadStoredManagementRoot({});
|
|
4886
|
+
if (storedAllRoot && storedAllRoot.pageSize === managementIndexPageSize) return storedAllRoot;
|
|
4887
|
+
const rebuiltBundles = sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
4888
|
+
const nextArtifacts = buildManagementIndexArtifacts(rebuiltBundles, managementIndexPageSize);
|
|
4889
|
+
await persistManagementIndexArtifacts(nextArtifacts, storedAllRoot ? buildManagementIndexArtifacts(rebuiltBundles, storedAllRoot.pageSize) : void 0);
|
|
4890
|
+
replaceManagementRootCache(nextArtifacts);
|
|
4891
|
+
return getAllManagementArtifact(nextArtifacts).root;
|
|
4892
|
+
};
|
|
4893
|
+
const loadManagementScopeRoot = async (scope) => {
|
|
4894
|
+
const cacheKey = getManagementScopeCacheKey(scope);
|
|
4895
|
+
if (cacheKey === ALL_SCOPE_CACHE_KEY) return ensureAllManagementRoot();
|
|
4896
|
+
const storedRoot = await loadStoredManagementRoot(scope);
|
|
4897
|
+
if (storedRoot) return storedRoot;
|
|
4898
|
+
await ensureAllManagementRoot();
|
|
4899
|
+
const storedScopedRoot = await loadStoredManagementRoot(scope);
|
|
4900
|
+
if (storedScopedRoot) return storedScopedRoot;
|
|
4901
|
+
managementRootCache.set(cacheKey, null);
|
|
4902
|
+
return null;
|
|
4903
|
+
};
|
|
4904
|
+
const loadAllBundlesForManagementFallback = async () => {
|
|
4905
|
+
const allRoot = await loadManagementScopeRoot({});
|
|
4906
|
+
if (allRoot) {
|
|
4907
|
+
const pagedBundles = await loadAllBundlesFromRoot(allRoot);
|
|
4908
|
+
if (pagedBundles) return pagedBundles;
|
|
4909
|
+
}
|
|
4910
|
+
return sortManagedBundles((await reloadBundles()).map((bundle) => removeBundleInternalKeys(bundle)));
|
|
4911
|
+
};
|
|
4912
|
+
const loadCurrentBundlesForIndexRebuild = async () => {
|
|
4913
|
+
return loadAllBundlesForManagementFallback();
|
|
4914
|
+
};
|
|
4915
|
+
const findPageIndexContainingId = (pages, id) => {
|
|
4916
|
+
return pages.findIndex((page) => id.localeCompare(page.firstId) <= 0 && id.localeCompare(page.lastId) >= 0);
|
|
4917
|
+
};
|
|
4918
|
+
const readPagedBundles = async ({ root, limit, offset, cursor }) => {
|
|
4919
|
+
if (root.total === 0 || root.pages.length === 0) return createEmptyManagementResult(limit);
|
|
4920
|
+
const pageStartOffsets = getPageStartOffsets(root.pages);
|
|
4921
|
+
const pageCache = /* @__PURE__ */ new Map();
|
|
4922
|
+
if (offset !== void 0) {
|
|
4923
|
+
const normalizedOffset = Math.max(0, offset);
|
|
4924
|
+
if (normalizedOffset >= root.total) return {
|
|
4925
|
+
data: [],
|
|
4926
|
+
pagination: calculatePagination(root.total, {
|
|
4927
|
+
limit,
|
|
4928
|
+
offset: normalizedOffset
|
|
4929
|
+
})
|
|
4930
|
+
};
|
|
4931
|
+
let pageIndex = 0;
|
|
4932
|
+
for (let index = pageStartOffsets.length - 1; index >= 0; index--) if ((pageStartOffsets[index] ?? 0) <= normalizedOffset) {
|
|
4933
|
+
pageIndex = index;
|
|
4934
|
+
break;
|
|
4935
|
+
}
|
|
4936
|
+
const startInPage = normalizedOffset - (pageStartOffsets[pageIndex] ?? 0);
|
|
4937
|
+
const data = [];
|
|
4938
|
+
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
4939
|
+
const descriptor = root.pages[currentPageIndex];
|
|
4940
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
4941
|
+
if (!page) return paginateBundles({
|
|
4942
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
4943
|
+
limit,
|
|
4944
|
+
offset: normalizedOffset
|
|
4945
|
+
});
|
|
4946
|
+
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
4947
|
+
}
|
|
4948
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
4949
|
+
return {
|
|
4950
|
+
data: paginatedData,
|
|
4951
|
+
pagination: {
|
|
4952
|
+
...calculatePagination(root.total, {
|
|
4953
|
+
limit,
|
|
4954
|
+
offset: normalizedOffset
|
|
4955
|
+
}),
|
|
4956
|
+
...paginatedData.length > 0 && normalizedOffset + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
4957
|
+
...paginatedData.length > 0 && normalizedOffset > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
4958
|
+
}
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
if (cursor?.after) {
|
|
4962
|
+
let pageIndex = root.pages.findIndex((page) => {
|
|
4963
|
+
const containsCursor = cursor.after.localeCompare(page.firstId) <= 0 && cursor.after.localeCompare(page.lastId) >= 0;
|
|
4964
|
+
const wholePageEligible = cursor.after.localeCompare(page.firstId) > 0;
|
|
4965
|
+
return containsCursor || wholePageEligible;
|
|
4966
|
+
});
|
|
4967
|
+
if (pageIndex < 0) return {
|
|
4968
|
+
data: [],
|
|
4969
|
+
pagination: {
|
|
4970
|
+
...calculatePagination(root.total, {
|
|
4971
|
+
limit,
|
|
4972
|
+
offset: root.total
|
|
4973
|
+
}),
|
|
4974
|
+
previousCursor: cursor.after
|
|
4975
|
+
}
|
|
4976
|
+
};
|
|
4977
|
+
const data = [];
|
|
4978
|
+
let startIndex = null;
|
|
4979
|
+
while (pageIndex < root.pages.length && (limit <= 0 || data.length < limit)) {
|
|
4980
|
+
const descriptor = root.pages[pageIndex];
|
|
4981
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
4982
|
+
if (!page) return paginateBundles({
|
|
4983
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
4984
|
+
limit,
|
|
4985
|
+
cursor
|
|
4986
|
+
});
|
|
4987
|
+
const containsCursor = cursor.after.localeCompare(descriptor.firstId) <= 0 && cursor.after.localeCompare(descriptor.lastId) >= 0;
|
|
4988
|
+
let eligiblePageBundles = page;
|
|
4989
|
+
if (containsCursor) {
|
|
4990
|
+
const startInPage = page.findIndex((bundle) => bundle.id.localeCompare(cursor.after) < 0);
|
|
4991
|
+
if (startInPage < 0) eligiblePageBundles = [];
|
|
4992
|
+
else {
|
|
4993
|
+
eligiblePageBundles = page.slice(startInPage);
|
|
4994
|
+
startIndex ??= (pageStartOffsets[pageIndex] ?? 0) + startInPage;
|
|
4995
|
+
}
|
|
4996
|
+
} else if (eligiblePageBundles.length > 0) startIndex ??= pageStartOffsets[pageIndex] ?? 0;
|
|
4997
|
+
data.push(...eligiblePageBundles);
|
|
4998
|
+
if (limit > 0 && data.length >= limit) break;
|
|
4999
|
+
pageIndex += 1;
|
|
5000
|
+
}
|
|
5001
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
5002
|
+
const resolvedStartIndex = startIndex ?? root.total;
|
|
5003
|
+
return {
|
|
5004
|
+
data: paginatedData,
|
|
5005
|
+
pagination: {
|
|
5006
|
+
...calculatePagination(root.total, {
|
|
5007
|
+
limit,
|
|
5008
|
+
offset: resolvedStartIndex
|
|
5009
|
+
}),
|
|
5010
|
+
...paginatedData.length > 0 && resolvedStartIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
5011
|
+
...paginatedData.length > 0 && resolvedStartIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
5012
|
+
}
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
if (cursor?.before) {
|
|
5016
|
+
let pageIndex = -1;
|
|
5017
|
+
for (let index = root.pages.length - 1; index >= 0; index--) {
|
|
5018
|
+
const page = root.pages[index];
|
|
5019
|
+
const containsCursor = cursor.before.localeCompare(page.firstId) <= 0 && cursor.before.localeCompare(page.lastId) >= 0;
|
|
5020
|
+
const wholePageEligible = cursor.before.localeCompare(page.lastId) < 0;
|
|
5021
|
+
if (containsCursor || wholePageEligible) {
|
|
5022
|
+
pageIndex = index;
|
|
5023
|
+
break;
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
if (pageIndex < 0) return createEmptyManagementResult(limit);
|
|
5027
|
+
let startIndex = null;
|
|
5028
|
+
let collected = [];
|
|
5029
|
+
while (pageIndex >= 0 && (limit <= 0 || collected.length < limit)) {
|
|
5030
|
+
const descriptor = root.pages[pageIndex];
|
|
5031
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
5032
|
+
if (!page) return paginateBundles({
|
|
5033
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
5034
|
+
limit,
|
|
5035
|
+
cursor
|
|
5036
|
+
});
|
|
5037
|
+
const eligiblePageBundles = cursor.before.localeCompare(descriptor.firstId) <= 0 && cursor.before.localeCompare(descriptor.lastId) >= 0 ? page.filter((bundle) => bundle.id.localeCompare(cursor.before) > 0) : page;
|
|
5038
|
+
collected = [...eligiblePageBundles, ...collected];
|
|
5039
|
+
if (eligiblePageBundles.length > 0) startIndex = pageStartOffsets[pageIndex] ?? 0;
|
|
5040
|
+
if (limit > 0 && collected.length >= limit) break;
|
|
5041
|
+
pageIndex -= 1;
|
|
5042
|
+
}
|
|
5043
|
+
if (startIndex === null || collected.length === 0) return createEmptyManagementResult(limit);
|
|
5044
|
+
let paginatedData = collected;
|
|
5045
|
+
if (limit > 0 && collected.length > limit) {
|
|
5046
|
+
const dropCount = collected.length - limit;
|
|
5047
|
+
paginatedData = collected.slice(dropCount);
|
|
5048
|
+
startIndex += dropCount;
|
|
5049
|
+
}
|
|
5050
|
+
const pagination = calculatePagination(root.total, {
|
|
5051
|
+
limit,
|
|
5052
|
+
offset: startIndex
|
|
5053
|
+
});
|
|
5054
|
+
return {
|
|
5055
|
+
data: paginatedData,
|
|
5056
|
+
pagination: {
|
|
5057
|
+
...pagination,
|
|
5058
|
+
...paginatedData.length > 0 && startIndex + paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {},
|
|
5059
|
+
...paginatedData.length > 0 && startIndex > 0 ? { previousCursor: paginatedData[0]?.id } : {}
|
|
5060
|
+
}
|
|
5061
|
+
};
|
|
5062
|
+
}
|
|
5063
|
+
const pageIndex = 0;
|
|
5064
|
+
const startInPage = 0;
|
|
5065
|
+
const data = [];
|
|
5066
|
+
for (let currentPageIndex = pageIndex; currentPageIndex < root.pages.length && (limit <= 0 || data.length < limit); currentPageIndex++) {
|
|
5067
|
+
const descriptor = root.pages[currentPageIndex];
|
|
5068
|
+
const page = await loadManagementPage(descriptor, pageCache);
|
|
5069
|
+
if (!page) return paginateBundles({
|
|
5070
|
+
bundles: await loadAllBundlesForManagementFallback(),
|
|
5071
|
+
limit,
|
|
5072
|
+
cursor
|
|
5073
|
+
});
|
|
5074
|
+
data.push(...currentPageIndex === pageIndex ? page.slice(startInPage) : page);
|
|
5075
|
+
}
|
|
5076
|
+
const paginatedData = limit > 0 ? data.slice(0, limit) : data;
|
|
5077
|
+
return {
|
|
5078
|
+
data: paginatedData,
|
|
5079
|
+
pagination: {
|
|
5080
|
+
...calculatePagination(root.total, {
|
|
5081
|
+
limit,
|
|
5082
|
+
offset: 0
|
|
5083
|
+
}),
|
|
5084
|
+
...paginatedData.length > 0 && paginatedData.length < root.total ? { nextCursor: paginatedData.at(-1)?.id } : {}
|
|
5085
|
+
}
|
|
5086
|
+
};
|
|
5087
|
+
};
|
|
4506
5088
|
async function reloadBundles() {
|
|
4507
5089
|
bundlesMap.clear();
|
|
4508
|
-
const
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
}));
|
|
4514
|
-
});
|
|
4515
|
-
return (await Promise.all(filePromises)).flat();
|
|
5090
|
+
const filePromises = (await listObjects("")).filter((key) => /^[^/]+\/(?:ios|android)\/[^/]+\/update\.json$/.test(key)).map(async (key) => {
|
|
5091
|
+
return (await loadObject(key) ?? []).map((bundle) => ({
|
|
5092
|
+
...bundle,
|
|
5093
|
+
_updateJsonKey: key
|
|
5094
|
+
}));
|
|
4516
5095
|
});
|
|
4517
|
-
const allBundles = (await Promise.all(
|
|
5096
|
+
const allBundles = (await Promise.all(filePromises)).flat();
|
|
4518
5097
|
for (const bundle of allBundles) bundlesMap.set(bundle.id, bundle);
|
|
4519
5098
|
for (const [id, bundle] of pendingBundlesMap.entries()) bundlesMap.set(id, bundle);
|
|
4520
5099
|
return orderBy(allBundles, [(v) => v.id], ["desc"]);
|
|
@@ -4549,17 +5128,6 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4549
5128
|
if (JSON.stringify(oldTargetVersions) !== JSON.stringify(newTargetVersions)) await uploadObject(targetKey, newTargetVersions);
|
|
4550
5129
|
}
|
|
4551
5130
|
}
|
|
4552
|
-
/**
|
|
4553
|
-
* Lists update.json keys for a given platform.
|
|
4554
|
-
*
|
|
4555
|
-
* - If a channel is provided, only that channel's update.json files are listed.
|
|
4556
|
-
* - Otherwise, all channels for the given platform are returned.
|
|
4557
|
-
*/
|
|
4558
|
-
async function listUpdateJsonKeys(platform, channel) {
|
|
4559
|
-
const prefix = channel ? platform ? `${channel}/${platform}/` : `${channel}/` : "";
|
|
4560
|
-
const pattern = channel ? platform ? new RegExp(`^${channel}/${platform}/[^/]+/update\\.json$`) : new RegExp(`^${channel}/[^/]+/[^/]+/update\\.json$`) : platform ? new RegExp(`^[^/]+/${platform}/[^/]+/update\\.json$`) : /^[^/]+\/[^/]+\/[^/]+\/update\.json$/;
|
|
4561
|
-
return listObjects(prefix).then((keys) => keys.filter((key) => pattern.test(key)));
|
|
4562
|
-
}
|
|
4563
5131
|
const getAppVersionUpdateInfo = async ({ appVersion, bundleId, channel = "production", cohort, minBundleId, platform }) => {
|
|
4564
5132
|
const matchingVersions = filterCompatibleAppVersions(await loadObject(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion);
|
|
4565
5133
|
return getUpdateInfo((await Promise.allSettled(matchingVersions.map(async (targetAppVersion) => {
|
|
@@ -4607,41 +5175,56 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4607
5175
|
return createDatabasePlugin({
|
|
4608
5176
|
name,
|
|
4609
5177
|
factory: () => ({
|
|
5178
|
+
supportsCursorPagination: true,
|
|
4610
5179
|
async getBundleById(bundleId) {
|
|
4611
5180
|
const pendingBundle = pendingBundlesMap.get(bundleId);
|
|
4612
5181
|
if (pendingBundle) return removeBundleInternalKeys(pendingBundle);
|
|
4613
5182
|
const bundle = bundlesMap.get(bundleId);
|
|
4614
5183
|
if (bundle) return removeBundleInternalKeys(bundle);
|
|
4615
|
-
|
|
5184
|
+
const allRoot = await loadManagementScopeRoot({});
|
|
5185
|
+
if (allRoot) {
|
|
5186
|
+
const matchedBundle = await loadBundleFromManagementRoot(allRoot, bundleId);
|
|
5187
|
+
if (matchedBundle) return removeBundleInternalKeys(createHydratedBundle(matchedBundle));
|
|
5188
|
+
managementRootCache.delete(ALL_SCOPE_CACHE_KEY);
|
|
5189
|
+
const refreshedAllRoot = await loadStoredManagementRoot({});
|
|
5190
|
+
if (refreshedAllRoot) {
|
|
5191
|
+
const refreshedBundle = await loadBundleFromManagementRoot(refreshedAllRoot, bundleId);
|
|
5192
|
+
if (refreshedBundle) return removeBundleInternalKeys(createHydratedBundle(refreshedBundle));
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
const matchedBundle = (await reloadBundles()).find((item) => item.id === bundleId);
|
|
5196
|
+
if (!matchedBundle) return null;
|
|
5197
|
+
return removeBundleInternalKeys(matchedBundle);
|
|
4616
5198
|
},
|
|
4617
5199
|
async getUpdateInfo(args) {
|
|
4618
5200
|
if (args._updateStrategy === "appVersion") return getAppVersionUpdateInfo(args);
|
|
4619
5201
|
return getFingerprintUpdateInfo(args);
|
|
4620
5202
|
},
|
|
4621
5203
|
async getBundles(options) {
|
|
4622
|
-
|
|
4623
|
-
const
|
|
4624
|
-
if (
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
if (limit) paginatedData = paginatedData.slice(0, limit);
|
|
4630
|
-
return {
|
|
4631
|
-
data: paginatedData,
|
|
4632
|
-
pagination: calculatePagination(total, {
|
|
5204
|
+
const { where, limit, offset, orderBy, cursor } = options;
|
|
5205
|
+
const scope = getSupportedManagementScope(where, orderBy);
|
|
5206
|
+
if (scope) {
|
|
5207
|
+
const root = await loadManagementScopeRoot(scope);
|
|
5208
|
+
if (!root) return createEmptyManagementResult(limit);
|
|
5209
|
+
return readPagedBundles({
|
|
5210
|
+
root,
|
|
4633
5211
|
limit,
|
|
4634
|
-
offset
|
|
4635
|
-
|
|
4636
|
-
|
|
5212
|
+
offset,
|
|
5213
|
+
cursor
|
|
5214
|
+
});
|
|
5215
|
+
}
|
|
5216
|
+
let allBundles = await loadAllBundlesForManagementFallback();
|
|
5217
|
+
if (where) allBundles = allBundles.filter((bundle) => bundleMatchesQueryWhere$1(bundle, where));
|
|
5218
|
+
return paginateBundles({
|
|
5219
|
+
bundles: allBundles,
|
|
5220
|
+
limit,
|
|
5221
|
+
offset,
|
|
5222
|
+
cursor,
|
|
5223
|
+
orderBy
|
|
5224
|
+
});
|
|
4637
5225
|
},
|
|
4638
5226
|
async getChannels() {
|
|
4639
|
-
|
|
4640
|
-
const result = await this.getBundles({
|
|
4641
|
-
limit: total,
|
|
4642
|
-
offset: 0
|
|
4643
|
-
});
|
|
4644
|
-
return [...new Set(result.data.map((bundle) => bundle.channel))];
|
|
5227
|
+
return (await loadManagementScopeRoot({}))?.channels ?? [];
|
|
4645
5228
|
},
|
|
4646
5229
|
async commitBundle({ changedSets }) {
|
|
4647
5230
|
if (changedSets.length === 0) return;
|
|
@@ -4738,6 +5321,20 @@ const createBlobDatabasePlugin = ({ name, factory }) => {
|
|
|
4738
5321
|
await uploadObject(key, currentBundles);
|
|
4739
5322
|
})();
|
|
4740
5323
|
if (isTargetAppVersionChanged || isChannelChanged) for (const platform of PLATFORMS) await updateTargetVersionsForPlatform(platform);
|
|
5324
|
+
const currentIndexBundles = await loadCurrentBundlesForIndexRebuild();
|
|
5325
|
+
const nextIndexMap = new Map(currentIndexBundles.map((bundle) => [bundle.id, bundle]));
|
|
5326
|
+
for (const { operation, data } of changedSets) {
|
|
5327
|
+
if (operation === "delete") {
|
|
5328
|
+
nextIndexMap.delete(data.id);
|
|
5329
|
+
continue;
|
|
5330
|
+
}
|
|
5331
|
+
nextIndexMap.set(data.id, data);
|
|
5332
|
+
}
|
|
5333
|
+
const nextIndexBundles = sortManagedBundles(Array.from(nextIndexMap.values()));
|
|
5334
|
+
const previousArtifacts = buildManagementIndexArtifacts(currentIndexBundles, managementIndexPageSize);
|
|
5335
|
+
const nextArtifacts = buildManagementIndexArtifacts(nextIndexBundles, managementIndexPageSize);
|
|
5336
|
+
await persistManagementIndexArtifacts(nextArtifacts, previousArtifacts);
|
|
5337
|
+
replaceManagementRootCache(nextArtifacts);
|
|
4741
5338
|
const encondedPaths = /* @__PURE__ */ new Set();
|
|
4742
5339
|
for (const path of pathsToInvalidate) encondedPaths.add(encodeURI(path));
|
|
4743
5340
|
await invalidatePaths(Array.from(encondedPaths));
|
|
@@ -4908,13 +5505,13 @@ function createPluginDatabaseCore(getPlugin, resolveFileUrl, options) {
|
|
|
4908
5505
|
return isCohortEligibleForUpdate(bundle.id, cohort, bundle.rolloutCohortCount, bundle.targetCohorts);
|
|
4909
5506
|
};
|
|
4910
5507
|
const findUpdateInfoByScanning = async ({ args, queryWhere, isCandidate, context }) => {
|
|
4911
|
-
let
|
|
5508
|
+
let after;
|
|
4912
5509
|
while (true) {
|
|
4913
5510
|
const { data, pagination } = await getSortedBundlePage({
|
|
4914
5511
|
where: queryWhere,
|
|
4915
5512
|
limit: PAGE_SIZE,
|
|
4916
|
-
|
|
4917
|
-
|
|
5513
|
+
orderBy: DESC_ORDER,
|
|
5514
|
+
...after ? { cursor: { after } } : {}
|
|
4918
5515
|
}, context);
|
|
4919
5516
|
for (const bundle of data) {
|
|
4920
5517
|
if (!bundleMatchesQueryWhere(bundle, queryWhere) || !isCandidate(bundle)) continue;
|
|
@@ -4934,7 +5531,8 @@ function createPluginDatabaseCore(getPlugin, resolveFileUrl, options) {
|
|
|
4934
5531
|
return makeResponse(bundle, "ROLLBACK");
|
|
4935
5532
|
}
|
|
4936
5533
|
if (!pagination.hasNextPage) break;
|
|
4937
|
-
|
|
5534
|
+
after = data.at(-1)?.id;
|
|
5535
|
+
if (!after) break;
|
|
4938
5536
|
}
|
|
4939
5537
|
if (args.bundleId === "00000000-0000-0000-0000-000000000000") return null;
|
|
4940
5538
|
if (args.minBundleId && args.bundleId.localeCompare(args.minBundleId) <= 0) return null;
|
|
@@ -5185,7 +5783,13 @@ const handleGetBundles = async (_params, request, api, context) => {
|
|
|
5185
5783
|
const channel = url.searchParams.get("channel") ?? void 0;
|
|
5186
5784
|
const platform = url.searchParams.get("platform");
|
|
5187
5785
|
const limit = Number(url.searchParams.get("limit")) || 50;
|
|
5188
|
-
const
|
|
5786
|
+
const pageParam = url.searchParams.get("page");
|
|
5787
|
+
const offset = url.searchParams.get("offset");
|
|
5788
|
+
const after = url.searchParams.get("after") ?? void 0;
|
|
5789
|
+
const before = url.searchParams.get("before") ?? void 0;
|
|
5790
|
+
const page = pageParam === null ? void 0 : Number.isInteger(Number(pageParam)) && Number(pageParam) > 0 ? Number(pageParam) : null;
|
|
5791
|
+
if (offset !== null) throw new HandlerBadRequestError("The 'offset' query parameter has been removed. Use 'after' or 'before' cursor pagination instead.");
|
|
5792
|
+
if (page === null) throw new HandlerBadRequestError("The 'page' query parameter must be a positive integer.");
|
|
5189
5793
|
if (platform !== null && !isPlatform(platform)) throw new HandlerBadRequestError(`Invalid platform: ${platform}. Expected 'ios' or 'android'.`);
|
|
5190
5794
|
const result = await api.getBundles({
|
|
5191
5795
|
where: {
|
|
@@ -5193,7 +5797,11 @@ const handleGetBundles = async (_params, request, api, context) => {
|
|
|
5193
5797
|
...platform && { platform }
|
|
5194
5798
|
},
|
|
5195
5799
|
limit,
|
|
5196
|
-
|
|
5800
|
+
page,
|
|
5801
|
+
cursor: after || before ? {
|
|
5802
|
+
after,
|
|
5803
|
+
before
|
|
5804
|
+
} : void 0
|
|
5197
5805
|
}, context);
|
|
5198
5806
|
return new Response(JSON.stringify(result), {
|
|
5199
5807
|
status: 200,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/aws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.29.
|
|
4
|
+
"version": "0.29.7",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"es-toolkit": "^1.32.0",
|
|
43
43
|
"execa": "9.5.2",
|
|
44
44
|
"mime": "^4.0.4",
|
|
45
|
-
"@hot-updater/core": "0.29.
|
|
46
|
-
"@hot-updater/js": "0.29.
|
|
47
|
-
"@hot-updater/mock": "0.29.
|
|
48
|
-
"@hot-updater/test-utils": "0.29.
|
|
45
|
+
"@hot-updater/core": "0.29.7",
|
|
46
|
+
"@hot-updater/js": "0.29.7",
|
|
47
|
+
"@hot-updater/mock": "0.29.7",
|
|
48
|
+
"@hot-updater/test-utils": "0.29.7"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@aws-sdk/client-cloudfront": "3.1008.0",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"@aws-sdk/lib-storage": "3.1008.0",
|
|
61
61
|
"hono": "4.12.9",
|
|
62
62
|
"aws-lambda": "1.0.7",
|
|
63
|
-
"@hot-updater/plugin-core": "0.29.
|
|
64
|
-
"@hot-updater/
|
|
65
|
-
"@hot-updater/
|
|
63
|
+
"@hot-updater/plugin-core": "0.29.7",
|
|
64
|
+
"@hot-updater/server": "0.29.7",
|
|
65
|
+
"@hot-updater/cli-tools": "0.29.7"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsdown",
|