@hot-updater/aws 1.2.0-rc.11 → 1.2.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +34 -48
- package/dist/index.d.cts +2 -3
- package/dist/index.d.mts +2 -3
- package/dist/index.mjs +34 -48
- package/dist/lambda/index.cjs +160 -141
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -553,24 +553,36 @@ const EXPIRED_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
|
|
|
553
553
|
]);
|
|
554
554
|
/** Errors of the SDK finding no credentials, or an expired SSO session. */
|
|
555
555
|
const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set(["CredentialsProviderError", "TokenProviderError"]);
|
|
556
|
-
const SIGN_IN_HINT = ["In CI, set them with aws-actions/configure-aws-credentials.", "On your machine: aws sso login --profile <profile>, then set AWS_PROFILE."];
|
|
557
|
-
/** The widest line a message has: the CLI prints no wider. */
|
|
558
|
-
const LINE_WIDTH = 76;
|
|
559
|
-
/** `text`, such as AWS's own message, in lines that fit, broken at spaces. */
|
|
560
|
-
const wrap = (text) => {
|
|
561
|
-
const lines = [];
|
|
562
|
-
let line = "";
|
|
563
|
-
for (const word of text.split(/\s+/u).filter(Boolean)) if (line && line.length + 1 + word.length > LINE_WIDTH) {
|
|
564
|
-
lines.push(line);
|
|
565
|
-
line = word;
|
|
566
|
-
} else line = line ? `${line} ${word}` : word;
|
|
567
|
-
if (line) lines.push(line);
|
|
568
|
-
return lines;
|
|
569
|
-
};
|
|
570
556
|
/** An error of one line each, for a terminal. */
|
|
571
557
|
const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
|
|
572
558
|
const errorName = (error) => error instanceof Error ? error.name : "";
|
|
573
559
|
const errorMessage = (error) => (error instanceof Error ? error.message : String(error)).replace(/\.+$/u, "");
|
|
560
|
+
/** The access key variables set, which the SDK's chain tries first. */
|
|
561
|
+
const accessKeyVariables = () => {
|
|
562
|
+
if (!process.env["AWS_ACCESS_KEY_ID"] || !process.env["AWS_SECRET_ACCESS_KEY"]) return;
|
|
563
|
+
return process.env["AWS_SESSION_TOKEN"] ? "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN" : "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY";
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* The one step that can fix the credentials the SDK actually used. `AWS_PROFILE`
|
|
567
|
+
* wins over the access key variables in the SDK's chain, so it's looked at
|
|
568
|
+
* first; signing in fixes a profile only when it's an SSO one, as its error
|
|
569
|
+
* says, and fixes nothing when another source is in the way.
|
|
570
|
+
*/
|
|
571
|
+
const credentialHint = (error, configured) => {
|
|
572
|
+
if (configured !== void 0) return [`Check the credentials passed to ${NAME}().`];
|
|
573
|
+
const ci = Boolean(process.env["CI"]);
|
|
574
|
+
const profile = process.env["AWS_PROFILE"]?.trim();
|
|
575
|
+
if (profile) return !ci && /\bsso\b/iu.test(errorMessage(error)) ? ["Sign in again:", `aws sso login --profile ${profile}`] : [`Check the AWS profile ${profile}, which AWS_PROFILE selects.`];
|
|
576
|
+
const variables = accessKeyVariables();
|
|
577
|
+
if (variables) return [`Fix or unset ${variables}.`];
|
|
578
|
+
return ci ? ["Set them with aws-actions/configure-aws-credentials."] : ["Sign in, then set AWS_PROFILE:", "aws sso login --profile <profile>"];
|
|
579
|
+
};
|
|
580
|
+
/**
|
|
581
|
+
* The caller's ARN in a refusal, `User: arn:aws:sts::…`, so the message names
|
|
582
|
+
* who to grant. Only that token is read: the rest of AWS's message, such as
|
|
583
|
+
* the resource it refused, stays out.
|
|
584
|
+
*/
|
|
585
|
+
const refusedCaller = (message) => /\bUser: (arn:aws[a-z-]*:(?:iam|sts)::\d*:[\w+=,.@/-]*[\w-])/u.exec(message)?.[1];
|
|
574
586
|
const resolveEndpoint = (endpoint) => {
|
|
575
587
|
let url;
|
|
576
588
|
try {
|
|
@@ -650,40 +662,14 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
|
|
|
650
662
|
const describeFailure = (verb, id, error, permissions) => {
|
|
651
663
|
const name = errorName(error);
|
|
652
664
|
const status = error?.$metadata?.httpStatusCode;
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
], error);
|
|
661
|
-
if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure([
|
|
662
|
-
`AWS Secrets Manager refused the sign-in to ${verb} a secret.`,
|
|
663
|
-
secret,
|
|
664
|
-
...detail,
|
|
665
|
-
"They are expired or invalid: sign in again.",
|
|
666
|
-
...SIGN_IN_HINT
|
|
667
|
-
], error);
|
|
668
|
-
if (name === "AccessDeniedException") return failure([
|
|
669
|
-
`AWS Secrets Manager refused to ${verb} a secret.`,
|
|
670
|
-
secret,
|
|
671
|
-
...detail,
|
|
672
|
-
`The AWS identity needs ${permissions} on it.`,
|
|
673
|
-
`With a customer managed KMS key, it needs ${verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey"} too.`
|
|
674
|
-
], error);
|
|
675
|
-
if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([
|
|
676
|
-
`AWS Secrets Manager can't ${verb} a secret.`,
|
|
677
|
-
secret,
|
|
678
|
-
"It is scheduled for deletion. Restore it, or wait until it is gone:",
|
|
679
|
-
"aws secretsmanager restore-secret \\",
|
|
680
|
-
` --secret-id ${id}`
|
|
681
|
-
], error);
|
|
682
|
-
return failure([
|
|
683
|
-
`AWS Secrets Manager failed to ${verb} a secret.`,
|
|
684
|
-
secret,
|
|
685
|
-
...detail
|
|
686
|
-
], error);
|
|
665
|
+
if (NO_CREDENTIAL_ERRORS.has(name)) return failure([`${NAME} found no AWS credentials.`, ...credentialHint(error, credentials)], error);
|
|
666
|
+
if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure(["AWS rejected the credentials: expired or invalid.", ...credentialHint(error, credentials)], error);
|
|
667
|
+
if (name === "AccessDeniedException") {
|
|
668
|
+
const key = verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey";
|
|
669
|
+
return failure([`AWS Secrets Manager refused to ${verb} ${id}.`, `${refusedCaller(errorMessage(error)) ?? "The identity"} needs ${/\bkms\b/iu.test(errorMessage(error)) ? key : permissions} on it.`], error);
|
|
670
|
+
}
|
|
671
|
+
if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([`${id} is scheduled for deletion. Restore it, or wait until it is gone:`, `aws secretsmanager restore-secret --secret-id ${id}`], error);
|
|
672
|
+
return failure([`AWS Secrets Manager failed to ${verb} ${id}.`, `${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`], error);
|
|
687
673
|
};
|
|
688
674
|
const read = async ({ sdk, client }, id) => {
|
|
689
675
|
let output;
|
package/dist/index.d.cts
CHANGED
|
@@ -26,9 +26,8 @@ interface DynamoDBConfig extends Omit<DynamoDBClientConfig, "credentials"> {
|
|
|
26
26
|
readonly tableName: string;
|
|
27
27
|
/**
|
|
28
28
|
* DynamoDB bills each item a write touches, so Insights' aggregates are
|
|
29
|
-
* batched: through log rows a compaction merges (the default)
|
|
30
|
-
*
|
|
31
|
-
* each event.
|
|
29
|
+
* batched: through log rows a compaction merges (the default). `false` commits
|
|
30
|
+
* them with each event.
|
|
32
31
|
*/
|
|
33
32
|
readonly aggregateBatching?: EngineDatabase["aggregateBatching"] | false;
|
|
34
33
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -26,9 +26,8 @@ interface DynamoDBConfig extends Omit<DynamoDBClientConfig, "credentials"> {
|
|
|
26
26
|
readonly tableName: string;
|
|
27
27
|
/**
|
|
28
28
|
* DynamoDB bills each item a write touches, so Insights' aggregates are
|
|
29
|
-
* batched: through log rows a compaction merges (the default)
|
|
30
|
-
*
|
|
31
|
-
* each event.
|
|
29
|
+
* batched: through log rows a compaction merges (the default). `false` commits
|
|
30
|
+
* them with each event.
|
|
32
31
|
*/
|
|
33
32
|
readonly aggregateBatching?: EngineDatabase["aggregateBatching"] | false;
|
|
34
33
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -552,24 +552,36 @@ const EXPIRED_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
|
|
|
552
552
|
]);
|
|
553
553
|
/** Errors of the SDK finding no credentials, or an expired SSO session. */
|
|
554
554
|
const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set(["CredentialsProviderError", "TokenProviderError"]);
|
|
555
|
-
const SIGN_IN_HINT = ["In CI, set them with aws-actions/configure-aws-credentials.", "On your machine: aws sso login --profile <profile>, then set AWS_PROFILE."];
|
|
556
|
-
/** The widest line a message has: the CLI prints no wider. */
|
|
557
|
-
const LINE_WIDTH = 76;
|
|
558
|
-
/** `text`, such as AWS's own message, in lines that fit, broken at spaces. */
|
|
559
|
-
const wrap = (text) => {
|
|
560
|
-
const lines = [];
|
|
561
|
-
let line = "";
|
|
562
|
-
for (const word of text.split(/\s+/u).filter(Boolean)) if (line && line.length + 1 + word.length > LINE_WIDTH) {
|
|
563
|
-
lines.push(line);
|
|
564
|
-
line = word;
|
|
565
|
-
} else line = line ? `${line} ${word}` : word;
|
|
566
|
-
if (line) lines.push(line);
|
|
567
|
-
return lines;
|
|
568
|
-
};
|
|
569
555
|
/** An error of one line each, for a terminal. */
|
|
570
556
|
const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
|
|
571
557
|
const errorName = (error) => error instanceof Error ? error.name : "";
|
|
572
558
|
const errorMessage = (error) => (error instanceof Error ? error.message : String(error)).replace(/\.+$/u, "");
|
|
559
|
+
/** The access key variables set, which the SDK's chain tries first. */
|
|
560
|
+
const accessKeyVariables = () => {
|
|
561
|
+
if (!process.env["AWS_ACCESS_KEY_ID"] || !process.env["AWS_SECRET_ACCESS_KEY"]) return;
|
|
562
|
+
return process.env["AWS_SESSION_TOKEN"] ? "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN" : "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY";
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* The one step that can fix the credentials the SDK actually used. `AWS_PROFILE`
|
|
566
|
+
* wins over the access key variables in the SDK's chain, so it's looked at
|
|
567
|
+
* first; signing in fixes a profile only when it's an SSO one, as its error
|
|
568
|
+
* says, and fixes nothing when another source is in the way.
|
|
569
|
+
*/
|
|
570
|
+
const credentialHint = (error, configured) => {
|
|
571
|
+
if (configured !== void 0) return [`Check the credentials passed to ${NAME}().`];
|
|
572
|
+
const ci = Boolean(process.env["CI"]);
|
|
573
|
+
const profile = process.env["AWS_PROFILE"]?.trim();
|
|
574
|
+
if (profile) return !ci && /\bsso\b/iu.test(errorMessage(error)) ? ["Sign in again:", `aws sso login --profile ${profile}`] : [`Check the AWS profile ${profile}, which AWS_PROFILE selects.`];
|
|
575
|
+
const variables = accessKeyVariables();
|
|
576
|
+
if (variables) return [`Fix or unset ${variables}.`];
|
|
577
|
+
return ci ? ["Set them with aws-actions/configure-aws-credentials."] : ["Sign in, then set AWS_PROFILE:", "aws sso login --profile <profile>"];
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* The caller's ARN in a refusal, `User: arn:aws:sts::…`, so the message names
|
|
581
|
+
* who to grant. Only that token is read: the rest of AWS's message, such as
|
|
582
|
+
* the resource it refused, stays out.
|
|
583
|
+
*/
|
|
584
|
+
const refusedCaller = (message) => /\bUser: (arn:aws[a-z-]*:(?:iam|sts)::\d*:[\w+=,.@/-]*[\w-])/u.exec(message)?.[1];
|
|
573
585
|
const resolveEndpoint = (endpoint) => {
|
|
574
586
|
let url;
|
|
575
587
|
try {
|
|
@@ -649,40 +661,14 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
|
|
|
649
661
|
const describeFailure = (verb, id, error, permissions) => {
|
|
650
662
|
const name = errorName(error);
|
|
651
663
|
const status = error?.$metadata?.httpStatusCode;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
], error);
|
|
660
|
-
if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure([
|
|
661
|
-
`AWS Secrets Manager refused the sign-in to ${verb} a secret.`,
|
|
662
|
-
secret,
|
|
663
|
-
...detail,
|
|
664
|
-
"They are expired or invalid: sign in again.",
|
|
665
|
-
...SIGN_IN_HINT
|
|
666
|
-
], error);
|
|
667
|
-
if (name === "AccessDeniedException") return failure([
|
|
668
|
-
`AWS Secrets Manager refused to ${verb} a secret.`,
|
|
669
|
-
secret,
|
|
670
|
-
...detail,
|
|
671
|
-
`The AWS identity needs ${permissions} on it.`,
|
|
672
|
-
`With a customer managed KMS key, it needs ${verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey"} too.`
|
|
673
|
-
], error);
|
|
674
|
-
if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([
|
|
675
|
-
`AWS Secrets Manager can't ${verb} a secret.`,
|
|
676
|
-
secret,
|
|
677
|
-
"It is scheduled for deletion. Restore it, or wait until it is gone:",
|
|
678
|
-
"aws secretsmanager restore-secret \\",
|
|
679
|
-
` --secret-id ${id}`
|
|
680
|
-
], error);
|
|
681
|
-
return failure([
|
|
682
|
-
`AWS Secrets Manager failed to ${verb} a secret.`,
|
|
683
|
-
secret,
|
|
684
|
-
...detail
|
|
685
|
-
], error);
|
|
664
|
+
if (NO_CREDENTIAL_ERRORS.has(name)) return failure([`${NAME} found no AWS credentials.`, ...credentialHint(error, credentials)], error);
|
|
665
|
+
if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure(["AWS rejected the credentials: expired or invalid.", ...credentialHint(error, credentials)], error);
|
|
666
|
+
if (name === "AccessDeniedException") {
|
|
667
|
+
const key = verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey";
|
|
668
|
+
return failure([`AWS Secrets Manager refused to ${verb} ${id}.`, `${refusedCaller(errorMessage(error)) ?? "The identity"} needs ${/\bkms\b/iu.test(errorMessage(error)) ? key : permissions} on it.`], error);
|
|
669
|
+
}
|
|
670
|
+
if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([`${id} is scheduled for deletion. Restore it, or wait until it is gone:`, `aws secretsmanager restore-secret --secret-id ${id}`], error);
|
|
671
|
+
return failure([`AWS Secrets Manager failed to ${verb} ${id}.`, `${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`], error);
|
|
686
672
|
};
|
|
687
673
|
const read = async ({ sdk, client }, id) => {
|
|
688
674
|
let output;
|
package/dist/lambda/index.cjs
CHANGED
|
@@ -2131,7 +2131,10 @@ const CHANNEL_ID = {
|
|
|
2131
2131
|
* every database's migrator creates missing tables and then records the
|
|
2132
2132
|
* version, and none of them changes an existing table, so changing one
|
|
2133
2133
|
* needs a migration step of its own first. The schema fence answers 503 on
|
|
2134
|
-
* a database whose row differs, until it runs.
|
|
2134
|
+
* a database whose row differs, until it runs. Before a version is released,
|
|
2135
|
+
* its tables change in place in its own migration, and a database one of its
|
|
2136
|
+
* prereleases migrated takes the same change by hand: the fence compares this
|
|
2137
|
+
* row, not columns, so nothing reports it.
|
|
2135
2138
|
*/
|
|
2136
2139
|
const HOT_UPDATER_SCHEMA_VERSION = "1.2.0";
|
|
2137
2140
|
const bundles = defineTable({
|
|
@@ -2395,6 +2398,10 @@ const channels = defineTable({
|
|
|
2395
2398
|
* or `versionCode`. `submitted_to`, `submitted_at_ms` and
|
|
2396
2399
|
* `submitted_version` record the store track a build went to, when, and
|
|
2397
2400
|
* the store's label of the build; they stay null for a build no store took.
|
|
2401
|
+
* `store_build` is true on the build a person or a job says the store sells,
|
|
2402
|
+
* and null on every other: deploys patch from it over the scope's newest build.
|
|
2403
|
+
* It is never false, so `byStoreBuild` holds one entry per scope at most, on
|
|
2404
|
+
* every store, and an unmarked row, older than the column or not, holds none.
|
|
2398
2405
|
*/
|
|
2399
2406
|
const nativeBuilds = defineTable({
|
|
2400
2407
|
id: {
|
|
@@ -2471,6 +2478,10 @@ const nativeBuilds = defineTable({
|
|
|
2471
2478
|
type: "string",
|
|
2472
2479
|
maxLength: 255,
|
|
2473
2480
|
required: false
|
|
2481
|
+
},
|
|
2482
|
+
store_build: {
|
|
2483
|
+
type: "boolean",
|
|
2484
|
+
required: false
|
|
2474
2485
|
}
|
|
2475
2486
|
}, {
|
|
2476
2487
|
key: ["id"],
|
|
@@ -2483,6 +2494,11 @@ const nativeBuilds = defineTable({
|
|
|
2483
2494
|
eq: ["scope_key", "build_key"],
|
|
2484
2495
|
sort: ["id"]
|
|
2485
2496
|
},
|
|
2497
|
+
byStoreBuild: {
|
|
2498
|
+
eq: ["scope_key", "store_build"],
|
|
2499
|
+
sort: [],
|
|
2500
|
+
unique: true
|
|
2501
|
+
},
|
|
2486
2502
|
byChannel: {
|
|
2487
2503
|
eq: ["channel_id"],
|
|
2488
2504
|
sort: ["id"],
|
|
@@ -2703,15 +2719,8 @@ const LEASE_TABLE = "aggregate_lease";
|
|
|
2703
2719
|
const LEASE = { id: "compaction" };
|
|
2704
2720
|
/** How long a compaction holds the lease before another process may take it. */
|
|
2705
2721
|
const LEASE_MS = 3e4;
|
|
2706
|
-
/**
|
|
2707
|
-
|
|
2708
|
-
* first; memory mode flushes every 15, since its window is what a crash
|
|
2709
|
-
* loses.
|
|
2710
|
-
*/
|
|
2711
|
-
const WINDOW_MS$1 = {
|
|
2712
|
-
log: 6e4,
|
|
2713
|
-
memory: 15e3
|
|
2714
|
-
};
|
|
2722
|
+
/** A commit compacts once this has passed since the last compaction. */
|
|
2723
|
+
const WINDOW_MS$1 = 6e4;
|
|
2715
2724
|
/** Log rows a compaction reads from each log table for one group. */
|
|
2716
2725
|
const LOG_PAGE = 200;
|
|
2717
2726
|
/**
|
|
@@ -2725,8 +2734,6 @@ const GROUPS = {
|
|
|
2725
2734
|
};
|
|
2726
2735
|
/** Tries per write after a conflict or a transient error. */
|
|
2727
2736
|
const ATTEMPTS = 5;
|
|
2728
|
-
/** Aggregate rows a memory buffer holds before a commit waits for a flush. */
|
|
2729
|
-
const MAX_BUFFERED = 1e4;
|
|
2730
2737
|
const log = defineTable({
|
|
2731
2738
|
id: {
|
|
2732
2739
|
type: "string",
|
|
@@ -2776,7 +2783,7 @@ const valuesOf = ({ deltas, sketches }) => ({
|
|
|
2776
2783
|
/**
|
|
2777
2784
|
* Merges `values` into the change for `key`'s identity as `tx.aggregate`
|
|
2778
2785
|
* does: deltas add up and sketches merge. One constant `shardBy` keys each
|
|
2779
|
-
* identity to one entry;
|
|
2786
|
+
* identity to one entry; `compile` writes it to shard 0.
|
|
2780
2787
|
*/
|
|
2781
2788
|
const merge = (into, model, key, values) => recordAggregate(into, model, Object.fromEntries(shapeOf(model).key.map((field, at) => [field, key[at]])), values, "");
|
|
2782
2789
|
const mergeAll = (into, changes) => {
|
|
@@ -2810,7 +2817,7 @@ const increment = ({ model, deltas }, key) => Object.values(deltas).some((delta)
|
|
|
2810
2817
|
/**
|
|
2811
2818
|
* The guarded write that merges `change` into `current`: insert, patch, or
|
|
2812
2819
|
* delete at zero. Unlike a transaction's, a gauge may go below zero here: a
|
|
2813
|
-
*
|
|
2820
|
+
* group can hold a loss without the gain before it, and reads sum every shard.
|
|
2814
2821
|
*/
|
|
2815
2822
|
const rewrite = (change, key, current) => {
|
|
2816
2823
|
const { model } = change;
|
|
@@ -2848,12 +2855,12 @@ const rewrite = (change, key, current) => {
|
|
|
2848
2855
|
};
|
|
2849
2856
|
};
|
|
2850
2857
|
/**
|
|
2851
|
-
* The write for each change on
|
|
2852
|
-
* the row for counters, and a guarded rewrite
|
|
2853
|
-
* rows are read in one batch per table.
|
|
2858
|
+
* The write for each change on shard 0, since one compaction runs at a time:
|
|
2859
|
+
* a blind increment that creates the row for counters, and a guarded rewrite
|
|
2860
|
+
* for gauges and sketches, whose rows are read in one batch per table.
|
|
2854
2861
|
*/
|
|
2855
|
-
const compile = async (adapter, changes
|
|
2856
|
-
const keys = changes.map(({
|
|
2862
|
+
const compile = async (adapter, changes) => {
|
|
2863
|
+
const keys = changes.map(({ key }) => [...key.slice(0, -1), 0]);
|
|
2857
2864
|
const reads = /* @__PURE__ */ new Map();
|
|
2858
2865
|
changes.forEach((change, at) => {
|
|
2859
2866
|
if (rewrites(change)) reads.set(change.model, [...reads.get(change.model) ?? [], at]);
|
|
@@ -2907,14 +2914,13 @@ const encode = async (changes) => {
|
|
|
2907
2914
|
return zipped !== void 0 && zipped.length < json.length ? `z${zipped}` : json;
|
|
2908
2915
|
};
|
|
2909
2916
|
/**
|
|
2910
|
-
* Batches changes to aggregates declared `batched`. A transaction
|
|
2911
|
-
* them
|
|
2912
|
-
*
|
|
2913
|
-
*
|
|
2914
|
-
*
|
|
2915
|
-
* applies exactly once. One lease row keeps compactions one at a time.
|
|
2917
|
+
* Batches changes to aggregates declared `batched`. A transaction writes
|
|
2918
|
+
* them as one log row in its own atomic write, in place of their rows; a
|
|
2919
|
+
* compaction later merges log rows into the aggregates and deletes them,
|
|
2920
|
+
* atomically again, so each applies exactly once. One lease row keeps
|
|
2921
|
+
* compactions one at a time.
|
|
2916
2922
|
*/
|
|
2917
|
-
const createAggregateBatches = ({ engine, adapter, schema, batching: {
|
|
2923
|
+
const createAggregateBatches = ({ engine, adapter, schema, batching: { windowMs = WINDOW_MS$1 }, now }) => {
|
|
2918
2924
|
if (!schema.models.has(LEASE_TABLE)) throw new DatabaseTransactionError("An engine that batches aggregates needs aggregateBatchingModule in its schema.");
|
|
2919
2925
|
const holder = crypto.randomUUID().replaceAll("-", "");
|
|
2920
2926
|
const table = (name) => schema.models.get(name).table;
|
|
@@ -2942,68 +2948,6 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
2942
2948
|
}
|
|
2943
2949
|
return changes;
|
|
2944
2950
|
};
|
|
2945
|
-
/**
|
|
2946
|
-
* Writes changes in atomic chunks within `fits`, and returns the ones it
|
|
2947
|
-
* could not apply. A write whose outcome is unknown is dropped, never
|
|
2948
|
-
* resent, so no change applies twice.
|
|
2949
|
-
*/
|
|
2950
|
-
const apply = async (changes, shard) => {
|
|
2951
|
-
let rest = changes;
|
|
2952
|
-
let ops = [];
|
|
2953
|
-
let read = true;
|
|
2954
|
-
for (let failures = 0; rest.length > 0 && failures < ATTEMPTS;) try {
|
|
2955
|
-
if (read) ops = await compile(adapter, rest, shard);
|
|
2956
|
-
let size = rest.length;
|
|
2957
|
-
const chunk = () => ops.slice(0, size).filter(isOp);
|
|
2958
|
-
while (size > 1 && !adapter.fits(chunk())) size = Math.ceil(size / 2);
|
|
2959
|
-
let result = { ok: true };
|
|
2960
|
-
if (chunk().length > 0) result = await adapter.write(chunk()).catch((error) => {
|
|
2961
|
-
warn("flush")(error);
|
|
2962
|
-
return { ok: true };
|
|
2963
|
-
});
|
|
2964
|
-
read = !result.ok && !("retry" in result);
|
|
2965
|
-
if (result.ok) {
|
|
2966
|
-
rest = rest.slice(size);
|
|
2967
|
-
ops = ops.slice(size);
|
|
2968
|
-
failures = 0;
|
|
2969
|
-
} else {
|
|
2970
|
-
failures += 1;
|
|
2971
|
-
if (!read) await sleep$2(failures);
|
|
2972
|
-
}
|
|
2973
|
-
} catch (error) {
|
|
2974
|
-
warn("flush")(error);
|
|
2975
|
-
return rest;
|
|
2976
|
-
}
|
|
2977
|
-
return rest;
|
|
2978
|
-
};
|
|
2979
|
-
let buffer = /* @__PURE__ */ new Map();
|
|
2980
|
-
let timer;
|
|
2981
|
-
let flushing;
|
|
2982
|
-
const shards = /* @__PURE__ */ new Map();
|
|
2983
|
-
/** Each process writes its own shard, so flushes of two processes rarely meet. */
|
|
2984
|
-
const processShard = (model) => {
|
|
2985
|
-
if (!shards.has(model)) shards.set(model, Math.floor(Math.random() * shapeOf(model).shards));
|
|
2986
|
-
return shards.get(model);
|
|
2987
|
-
};
|
|
2988
|
-
const schedule = () => {
|
|
2989
|
-
if (timer !== void 0 || buffer.size === 0) return;
|
|
2990
|
-
timer = setTimeout(() => {
|
|
2991
|
-
timer = void 0;
|
|
2992
|
-
flushBuffer();
|
|
2993
|
-
}, windowMs);
|
|
2994
|
-
timer.unref?.();
|
|
2995
|
-
};
|
|
2996
|
-
/** Never rejects: what it cannot apply goes back into the buffer. */
|
|
2997
|
-
const flushBuffer = () => flushing ??= (async () => {
|
|
2998
|
-
clearTimeout(timer);
|
|
2999
|
-
timer = void 0;
|
|
3000
|
-
const pending = [...buffer.values()];
|
|
3001
|
-
buffer = /* @__PURE__ */ new Map();
|
|
3002
|
-
mergeAll(buffer, await apply(pending, processShard));
|
|
3003
|
-
})().catch(warn("flush")).finally(() => {
|
|
3004
|
-
flushing = void 0;
|
|
3005
|
-
schedule();
|
|
3006
|
-
});
|
|
3007
2951
|
let nextDue = 0;
|
|
3008
2952
|
let compacting;
|
|
3009
2953
|
/** Log rows this process read, by `[log table's index, id]`, for the deletes after a group. */
|
|
@@ -3138,7 +3082,7 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
3138
3082
|
more
|
|
3139
3083
|
};
|
|
3140
3084
|
};
|
|
3141
|
-
const rowsFor = async (changes) => (await compile(adapter, changes
|
|
3085
|
+
const rowsFor = async (changes) => (await compile(adapter, changes)).filter(isOp);
|
|
3142
3086
|
/**
|
|
3143
3087
|
* Applies the oldest pending log rows whose merged changes fit in one
|
|
3144
3088
|
* atomic write with the lease, which lists them as applied; the rows are
|
|
@@ -3232,20 +3176,6 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
3232
3176
|
}))).then((pages) => pages.some((page) => page.rows.length > 0)).finally(() => {
|
|
3233
3177
|
checking = void 0;
|
|
3234
3178
|
});
|
|
3235
|
-
/** Takes a committed transaction's changes; never rejects, since the transaction already committed. */
|
|
3236
|
-
const committed = async (changes) => {
|
|
3237
|
-
if (mode === "log") {
|
|
3238
|
-
if (now() >= nextDue) await compact(false, GROUPS.commit);
|
|
3239
|
-
return;
|
|
3240
|
-
}
|
|
3241
|
-
mergeAll(buffer, changes.values());
|
|
3242
|
-
schedule();
|
|
3243
|
-
if (buffer.size >= MAX_BUFFERED) await flushBuffer();
|
|
3244
|
-
};
|
|
3245
|
-
const flush = async () => {
|
|
3246
|
-
await flushBuffer();
|
|
3247
|
-
if (mode === "log" && await hasPending()) await compact(true, GROUPS.flush);
|
|
3248
|
-
};
|
|
3249
3179
|
return {
|
|
3250
3180
|
async transaction(fn) {
|
|
3251
3181
|
let changes = /* @__PURE__ */ new Map();
|
|
@@ -3271,7 +3201,7 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
3271
3201
|
aggregate
|
|
3272
3202
|
});
|
|
3273
3203
|
if (failure) throw failure.error;
|
|
3274
|
-
if (
|
|
3204
|
+
if (attempt.size > 0) {
|
|
3275
3205
|
const id = `${now().toString(36).padStart(9, "0")}${holder.slice(0, 8)}${crypto.randomUUID().slice(0, 8)}`;
|
|
3276
3206
|
const shard = Math.floor(Math.random() * LOG_TABLES.length);
|
|
3277
3207
|
const merged = mergeAll(/* @__PURE__ */ new Map(), attempt.values());
|
|
@@ -3285,24 +3215,18 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
3285
3215
|
open = false;
|
|
3286
3216
|
}
|
|
3287
3217
|
});
|
|
3288
|
-
if (changes.size > 0) await
|
|
3218
|
+
if (changes.size > 0 && now() >= nextDue) await compact(false, GROUPS.commit);
|
|
3289
3219
|
return result;
|
|
3290
3220
|
},
|
|
3291
3221
|
async beforeRead(name) {
|
|
3292
3222
|
if (!isBatched(schema.models.get(name))) return;
|
|
3293
|
-
if (
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
return false;
|
|
3298
|
-
})) await compact(true, GROUPS.read);
|
|
3299
|
-
}
|
|
3223
|
+
if (await hasPending().catch((error) => {
|
|
3224
|
+
warn("read")(error);
|
|
3225
|
+
return false;
|
|
3226
|
+
})) await compact(true, GROUPS.read);
|
|
3300
3227
|
},
|
|
3301
|
-
flush
|
|
3302
|
-
|
|
3303
|
-
clearTimeout(timer);
|
|
3304
|
-
timer = void 0;
|
|
3305
|
-
await flush();
|
|
3228
|
+
async flush() {
|
|
3229
|
+
if (await hasPending()) await compact(true, GROUPS.flush);
|
|
3306
3230
|
}
|
|
3307
3231
|
};
|
|
3308
3232
|
};
|
|
@@ -4331,13 +4255,10 @@ const createDatabaseEngine = ({ batching, now = Date.now, meter, ...options }) =
|
|
|
4331
4255
|
};
|
|
4332
4256
|
return {
|
|
4333
4257
|
...engine,
|
|
4334
|
-
/** Applies batched aggregate changes still
|
|
4258
|
+
/** Applies the batched aggregate changes still in the log. */
|
|
4335
4259
|
flush: async () => {
|
|
4336
4260
|
await batches?.flush();
|
|
4337
4261
|
},
|
|
4338
|
-
dispose: async () => {
|
|
4339
|
-
await batches?.dispose();
|
|
4340
|
-
},
|
|
4341
4262
|
database(module) {
|
|
4342
4263
|
const name = (model) => module.namespace ? `${module.namespace}_${model}` : model;
|
|
4343
4264
|
return {
|
|
@@ -4418,8 +4339,7 @@ const createEngine = (database, { plugins = [], now = Date.now } = {}) => {
|
|
|
4418
4339
|
if (module === void 0) throw new HotUpdaterConfigError(`Plugin "${plugin.id}" is not one of this engine's plugins.`);
|
|
4419
4340
|
return engine.database(module);
|
|
4420
4341
|
},
|
|
4421
|
-
flush: () => engine.flush()
|
|
4422
|
-
dispose: () => engine.dispose()
|
|
4342
|
+
flush: () => engine.flush()
|
|
4423
4343
|
};
|
|
4424
4344
|
};
|
|
4425
4345
|
//#endregion
|
|
@@ -4712,7 +4632,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
4712
4632
|
};
|
|
4713
4633
|
//#endregion
|
|
4714
4634
|
//#region ../../packages/server/dist/version.mjs
|
|
4715
|
-
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.
|
|
4635
|
+
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.13";
|
|
4716
4636
|
//#endregion
|
|
4717
4637
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4718
4638
|
/**
|
|
@@ -5917,8 +5837,22 @@ const pick = (row) => ({
|
|
|
5917
5837
|
build_number: row.build_number ?? null,
|
|
5918
5838
|
submitted_to: row.submitted_to ?? null,
|
|
5919
5839
|
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
5920
|
-
submitted_version: row.submitted_version ?? null
|
|
5840
|
+
submitted_version: row.submitted_version ?? null,
|
|
5841
|
+
store_build: row.store_build === true ? true : null
|
|
5921
5842
|
});
|
|
5843
|
+
/**
|
|
5844
|
+
* The channels only sandbox builds register on, which no store sells: the
|
|
5845
|
+
* registry's convention, as `@hot-updater/protocol/native-artifact` checks it
|
|
5846
|
+
* for a build's artifact.
|
|
5847
|
+
*/
|
|
5848
|
+
const SANDBOX_CHANNEL_PREFIX = "sandbox-";
|
|
5849
|
+
/**
|
|
5850
|
+
* How many older builds of a scope one read takes, looking for one older than
|
|
5851
|
+
* a bundle when a build made after the bundle is the newest.
|
|
5852
|
+
*/
|
|
5853
|
+
const OLDER_BUILDS_PAGE = 20;
|
|
5854
|
+
/** How many times a mark runs again after losing its scope's mark to another. */
|
|
5855
|
+
const MARK_ATTEMPTS = 8;
|
|
5922
5856
|
/** The store track each platform's builds are submitted to. */
|
|
5923
5857
|
const SUBMISSION_DESTINATIONS = {
|
|
5924
5858
|
android: "play-internal",
|
|
@@ -5995,7 +5929,8 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
5995
5929
|
build_number: registration.buildNumber ?? null,
|
|
5996
5930
|
submitted_to: null,
|
|
5997
5931
|
submitted_at_ms: null,
|
|
5998
|
-
submitted_version: null
|
|
5932
|
+
submitted_version: null,
|
|
5933
|
+
store_build: null
|
|
5999
5934
|
};
|
|
6000
5935
|
for (let attempt = 1;; attempt += 1) {
|
|
6001
5936
|
await insertChannel(db, {
|
|
@@ -6033,21 +5968,45 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
6033
5968
|
const scopeKey = nativeBuildScopeKey(scope);
|
|
6034
5969
|
const buildKey = options?.buildKey;
|
|
6035
5970
|
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
where: { scope_key: scopeKey },
|
|
6039
|
-
order: "desc",
|
|
6040
|
-
limit: 1
|
|
6041
|
-
}) : await db.findMany("native_builds", {
|
|
6042
|
-
index: "byBuildKey",
|
|
6043
|
-
where: {
|
|
5971
|
+
if (options?.storeBuild) {
|
|
5972
|
+
const marked = await db.findOne("native_builds", {
|
|
6044
5973
|
scope_key: scopeKey,
|
|
6045
|
-
|
|
6046
|
-
}
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
5974
|
+
store_build: true
|
|
5975
|
+
});
|
|
5976
|
+
return marked === null ? null : pick(marked);
|
|
5977
|
+
}
|
|
5978
|
+
const read = (limit, before) => {
|
|
5979
|
+
const page = {
|
|
5980
|
+
order: "desc",
|
|
5981
|
+
limit,
|
|
5982
|
+
...before === void 0 ? {} : { range: { lt: before } }
|
|
5983
|
+
};
|
|
5984
|
+
return buildKey === void 0 ? db.findMany("native_builds", {
|
|
5985
|
+
index: "byScope",
|
|
5986
|
+
where: { scope_key: scopeKey },
|
|
5987
|
+
...page
|
|
5988
|
+
}) : db.findMany("native_builds", {
|
|
5989
|
+
index: "byBuildKey",
|
|
5990
|
+
where: {
|
|
5991
|
+
scope_key: scopeKey,
|
|
5992
|
+
build_key: buildKey
|
|
5993
|
+
},
|
|
5994
|
+
...page
|
|
5995
|
+
});
|
|
5996
|
+
};
|
|
5997
|
+
const { rows } = await read(1);
|
|
5998
|
+
const newest = rows[0];
|
|
5999
|
+
if (newest === void 0) return null;
|
|
6000
|
+
const bundleId = options?.builtInOlderThan;
|
|
6001
|
+
if (bundleId === void 0 || newest.min_bundle_id < bundleId) return pick(newest);
|
|
6002
|
+
for (let before = newest.id;;) {
|
|
6003
|
+
const { rows: older } = await read(OLDER_BUILDS_PAGE, before);
|
|
6004
|
+
const usable = older.find(({ min_bundle_id }) => min_bundle_id < bundleId);
|
|
6005
|
+
if (usable !== void 0) return pick(usable);
|
|
6006
|
+
const last = older.at(-1);
|
|
6007
|
+
if (older.length < OLDER_BUILDS_PAGE || last === void 0) return null;
|
|
6008
|
+
before = last.id;
|
|
6009
|
+
}
|
|
6051
6010
|
},
|
|
6052
6011
|
async recordNativeBuildSubmission(id, { destination, version }) {
|
|
6053
6012
|
if (version.trim().length === 0 || version.length > SUBMITTED_VERSION_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build submission: version must be the store's label of the build, up to ${SUBMITTED_VERSION_MAX_LENGTH} characters.`);
|
|
@@ -6068,6 +6027,49 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
6068
6027
|
};
|
|
6069
6028
|
});
|
|
6070
6029
|
},
|
|
6030
|
+
async markNativeBuild(id, { storeBuild }) {
|
|
6031
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
6032
|
+
return await db.transaction(async (tx) => {
|
|
6033
|
+
const row = await tx.findOne("native_builds", { id });
|
|
6034
|
+
if (row === null) return null;
|
|
6035
|
+
const build = pick(row);
|
|
6036
|
+
if (!storeBuild) {
|
|
6037
|
+
if (build.store_build !== null) tx.update("native_builds", row, { store_build: null });
|
|
6038
|
+
return {
|
|
6039
|
+
nativeBuild: {
|
|
6040
|
+
...build,
|
|
6041
|
+
store_build: null
|
|
6042
|
+
},
|
|
6043
|
+
previous: null
|
|
6044
|
+
};
|
|
6045
|
+
}
|
|
6046
|
+
if (decodeChannelKey(build.channel_id.slice(8)).startsWith(SANDBOX_CHANNEL_PREFIX)) throw new NativeBuildInputError("A build on a sandbox channel can't be the store build.");
|
|
6047
|
+
if (build.store_build !== null) return {
|
|
6048
|
+
nativeBuild: build,
|
|
6049
|
+
previous: null
|
|
6050
|
+
};
|
|
6051
|
+
const holder = await tx.findOne("native_builds", {
|
|
6052
|
+
scope_key: build.scope_key,
|
|
6053
|
+
store_build: true
|
|
6054
|
+
});
|
|
6055
|
+
if (holder !== null) tx.update("native_builds", holder, { store_build: null });
|
|
6056
|
+
tx.update("native_builds", row, { store_build: true });
|
|
6057
|
+
return {
|
|
6058
|
+
nativeBuild: {
|
|
6059
|
+
...build,
|
|
6060
|
+
store_build: true
|
|
6061
|
+
},
|
|
6062
|
+
previous: holder === null ? null : {
|
|
6063
|
+
...pick(holder),
|
|
6064
|
+
store_build: null
|
|
6065
|
+
}
|
|
6066
|
+
};
|
|
6067
|
+
});
|
|
6068
|
+
} catch (error) {
|
|
6069
|
+
if (attempt < MARK_ATTEMPTS && error instanceof DatabaseConstraintError && error.reason === "unique") continue;
|
|
6070
|
+
throw error;
|
|
6071
|
+
}
|
|
6072
|
+
},
|
|
6071
6073
|
async deleteNativeBuild(id) {
|
|
6072
6074
|
return db.transaction(async (tx) => {
|
|
6073
6075
|
const row = await tx.findOne("native_builds", { id });
|
|
@@ -7150,7 +7152,13 @@ const createAdminRouteHandlers = () => ({
|
|
|
7150
7152
|
const url = new URL(request.url);
|
|
7151
7153
|
const scope = nativeBuildScopeOf(url);
|
|
7152
7154
|
const buildKey = url.searchParams.get("buildKey");
|
|
7153
|
-
|
|
7155
|
+
const builtInOlderThan = url.searchParams.get("builtInOlderThan");
|
|
7156
|
+
const options = {
|
|
7157
|
+
...buildKey === null ? {} : { buildKey },
|
|
7158
|
+
...url.searchParams.get("storeBuild") === "true" ? { storeBuild: true } : {},
|
|
7159
|
+
...builtInOlderThan === null ? {} : { builtInOlderThan }
|
|
7160
|
+
};
|
|
7161
|
+
return json$2({ data: await refusingInvalid(() => Object.keys(options).length === 0 ? core.findNativeBuild(scope) : core.findNativeBuild(scope, options)) });
|
|
7154
7162
|
}),
|
|
7155
7163
|
getNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
|
|
7156
7164
|
listNativeBuilds: onCore(async (core, request) => {
|
|
@@ -7171,6 +7179,12 @@ const createAdminRouteHandlers = () => ({
|
|
|
7171
7179
|
};
|
|
7172
7180
|
return json$2({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
|
|
7173
7181
|
}),
|
|
7182
|
+
markNativeBuild: onCore(async (core, request, params) => {
|
|
7183
|
+
const input = await body(request);
|
|
7184
|
+
if (!isRecord$5(input) || typeof input.storeBuild !== "boolean") throw new HandlerBadRequestError("A native build's store build mark needs storeBuild, true or false.");
|
|
7185
|
+
const mark = { storeBuild: input.storeBuild };
|
|
7186
|
+
return json$2({ data: await refusingInvalid(() => core.markNativeBuild(requireRouteParam(params, "id"), mark)) });
|
|
7187
|
+
}),
|
|
7174
7188
|
deleteNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
|
|
7175
7189
|
deleteBundles: onCore(async (core, request) => {
|
|
7176
7190
|
const { ids } = await body(request);
|
|
@@ -7316,6 +7330,11 @@ const ADMIN_ROUTES = [
|
|
|
7316
7330
|
path: "/native-builds/:id/submission",
|
|
7317
7331
|
handler: "recordNativeBuildSubmission"
|
|
7318
7332
|
},
|
|
7333
|
+
{
|
|
7334
|
+
method: "POST",
|
|
7335
|
+
path: "/native-builds/:id/store-build",
|
|
7336
|
+
handler: "markNativeBuild"
|
|
7337
|
+
},
|
|
7319
7338
|
{
|
|
7320
7339
|
method: "DELETE",
|
|
7321
7340
|
path: "/native-builds/:id",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/aws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.0-rc.
|
|
4
|
+
"version": "1.2.0-rc.13",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"es-toolkit": "^1.32.0",
|
|
46
46
|
"execa": "9.5.2",
|
|
47
47
|
"mime": "^4.0.4",
|
|
48
|
-
"@hot-updater/
|
|
49
|
-
"@hot-updater/
|
|
50
|
-
"@hot-updater/
|
|
48
|
+
"@hot-updater/protocol": "1.2.0-rc.13",
|
|
49
|
+
"@hot-updater/mock": "1.2.0-rc.13",
|
|
50
|
+
"@hot-updater/test-utils": "1.2.0-rc.13"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@aws-sdk/client-cloudfront": "3.1066.0",
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"@aws-sdk/lib-dynamodb": "3.1066.0",
|
|
65
65
|
"@aws-sdk/s3-request-presigner": "3.1066.0",
|
|
66
66
|
"hono": "4.12.34",
|
|
67
|
-
"@hot-updater/
|
|
68
|
-
"@hot-updater/
|
|
69
|
-
"@hot-updater/
|
|
70
|
-
"@hot-updater/
|
|
67
|
+
"@hot-updater/plugin-core": "1.2.0-rc.13",
|
|
68
|
+
"@hot-updater/server": "1.2.0-rc.13",
|
|
69
|
+
"@hot-updater/storage-s3": "1.2.0-rc.13",
|
|
70
|
+
"@hot-updater/cli-tools": "1.2.0-rc.13"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "tsdown",
|