@absolutejs/absolute 0.20.0-beta.82 → 0.20.0-beta.83
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/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +224 -2
- package/dist/build.js.map +3 -3
- package/dist/cli/index.js +1 -1
- package/dist/cli/{mobile-hkxh2ab5.js → mobile-9dx39bd3.js} +143 -2
- package/dist/index.js +224 -2
- package/dist/index.js.map +3 -3
- package/dist/mobile/index.js +265 -2
- package/dist/mobile/index.js.map +4 -4
- package/dist/src/mobile/updatePublisher.d.ts +19 -0
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -203,7 +203,7 @@ if (command === "dev") {
|
|
|
203
203
|
sendTelemetryEvent("cli:command", {
|
|
204
204
|
command: `mobile:${workspaceCommand ?? "unknown"}`
|
|
205
205
|
});
|
|
206
|
-
const { runMobile } = await import("./mobile-
|
|
206
|
+
const { runMobile } = await import("./mobile-9dx39bd3.js");
|
|
207
207
|
try {
|
|
208
208
|
await runMobile(args);
|
|
209
209
|
} catch (error) {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
installPackages
|
|
4
4
|
} from "./index-zh6hhrwy.js";
|
|
5
|
+
import {
|
|
6
|
+
formatBytes
|
|
7
|
+
} from "./index-9r7n9dqp.js";
|
|
5
8
|
import {
|
|
6
9
|
start
|
|
7
10
|
} from "./index-q78x1k9q.js";
|
|
@@ -21985,6 +21988,45 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
21985
21988
|
import { access as access11 } from "fs/promises";
|
|
21986
21989
|
import { isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
|
|
21987
21990
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
21991
|
+
var lifecycleMethod = (publisher, name) => {
|
|
21992
|
+
const method = publisher[name];
|
|
21993
|
+
if (typeof method !== "function")
|
|
21994
|
+
throw new TypeError(`Mobile update registry does not support ${name}. Re-run \`absolute mobile update provision --force\` after upgrading @absolutejs/deploy.`);
|
|
21995
|
+
return method;
|
|
21996
|
+
};
|
|
21997
|
+
var validateStorageIdentity = (result, appId) => {
|
|
21998
|
+
if (!object3(result) || result.appId !== appId || !Array.isArray(result.releases) || ![
|
|
21999
|
+
result.channelCount,
|
|
22000
|
+
result.reclaimableBytes,
|
|
22001
|
+
result.releaseBytes,
|
|
22002
|
+
result.releaseCount,
|
|
22003
|
+
result.totalBytes,
|
|
22004
|
+
result.totalObjectCount,
|
|
22005
|
+
result.untrackedBytes
|
|
22006
|
+
].every((value) => Number.isSafeInteger(value) && value >= 0))
|
|
22007
|
+
throw new TypeError("Mobile update registry returned an invalid storage report.");
|
|
22008
|
+
return result;
|
|
22009
|
+
};
|
|
22010
|
+
var inspectAbsoluteMobileUpdateStorage = async (options) => validateStorageIdentity(await lifecycleMethod(options.publisher, "inspectUpdateStorage")({
|
|
22011
|
+
appId: options.appId,
|
|
22012
|
+
...options.minAgeMs === undefined ? {} : { minAgeMs: options.minAgeMs },
|
|
22013
|
+
...options.retainRecent === undefined ? {} : { retainRecent: options.retainRecent },
|
|
22014
|
+
...options.signal ? { signal: options.signal } : {}
|
|
22015
|
+
}), options.appId);
|
|
22016
|
+
var pruneAbsoluteMobileUpdates = async (options) => {
|
|
22017
|
+
const result = await lifecycleMethod(options.publisher, "pruneUpdates")({
|
|
22018
|
+
appId: options.appId,
|
|
22019
|
+
...options.apply === undefined ? {} : { apply: options.apply },
|
|
22020
|
+
...options.gracePeriodMs === undefined ? {} : { gracePeriodMs: options.gracePeriodMs },
|
|
22021
|
+
...options.minAgeMs === undefined ? {} : { minAgeMs: options.minAgeMs },
|
|
22022
|
+
...options.retainRecent === undefined ? {} : { retainRecent: options.retainRecent },
|
|
22023
|
+
...options.signal ? { signal: options.signal } : {}
|
|
22024
|
+
});
|
|
22025
|
+
validateStorageIdentity(result, options.appId);
|
|
22026
|
+
if (!Array.isArray(result.marked) || !Array.isArray(result.restored) || !Array.isArray(result.swept) || typeof result.dryRun !== "boolean" || !Number.isSafeInteger(result.reclaimedBytes) || result.reclaimedBytes < 0)
|
|
22027
|
+
throw new TypeError("Mobile update registry returned an invalid collection report.");
|
|
22028
|
+
return result;
|
|
22029
|
+
};
|
|
21988
22030
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21989
22031
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
21990
22032
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
@@ -22741,7 +22783,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
22741
22783
|
throw new TypeError("--storage must be local or s3.");
|
|
22742
22784
|
const modulePath = valueAfter(args, "--registry") ?? mobile.updateServer.registryModule;
|
|
22743
22785
|
const packages = [
|
|
22744
|
-
"@absolutejs/deploy@0.25.
|
|
22786
|
+
"@absolutejs/deploy@0.25.7",
|
|
22745
22787
|
"@absolutejs/blob@0.5.2",
|
|
22746
22788
|
...requestedStorage === "s3" ? [
|
|
22747
22789
|
"@aws-sdk/client-s3@3.1095.0",
|
|
@@ -22825,6 +22867,97 @@ var rollbackMobileUpdate = async (args) => {
|
|
|
22825
22867
|
console.log(result.releaseId ? `Rolled ${result.channel} back to ${result.releaseId}.` : `Rolled ${result.channel} back to the embedded store build.`);
|
|
22826
22868
|
return result;
|
|
22827
22869
|
};
|
|
22870
|
+
var mobileUpdateDays = (args, flag) => {
|
|
22871
|
+
const value = valueAfter(args, flag);
|
|
22872
|
+
if (value === undefined)
|
|
22873
|
+
return;
|
|
22874
|
+
const parsed = Number(value);
|
|
22875
|
+
const milliseconds = parsed * 24 * 60 * 60 * 1000;
|
|
22876
|
+
if (!Number.isFinite(parsed) || parsed < 0 || !Number.isSafeInteger(milliseconds))
|
|
22877
|
+
throw new TypeError(`${flag} must be a non-negative number of days.`);
|
|
22878
|
+
return milliseconds;
|
|
22879
|
+
};
|
|
22880
|
+
var mobileUpdateRetention = (args) => {
|
|
22881
|
+
const retainedValue = valueAfter(args, "--retain");
|
|
22882
|
+
const retainRecent = retainedValue === undefined ? undefined : Number(retainedValue);
|
|
22883
|
+
if (retainRecent !== undefined && (!Number.isSafeInteger(retainRecent) || retainRecent < 0))
|
|
22884
|
+
throw new TypeError("--retain must be a non-negative integer.");
|
|
22885
|
+
return {
|
|
22886
|
+
minAgeMs: mobileUpdateDays(args, "--min-age-days"),
|
|
22887
|
+
retainRecent
|
|
22888
|
+
};
|
|
22889
|
+
};
|
|
22890
|
+
var printMobileUpdateStorage = (report) => {
|
|
22891
|
+
console.log(`${report.appId}: ${report.releaseCount} releases use ${formatBytes(report.releaseBytes)}; ${formatBytes(report.reclaimableBytes)} is eligible across ${report.totalObjectCount} stored objects.`);
|
|
22892
|
+
for (const release of report.releases) {
|
|
22893
|
+
let state = "eligible";
|
|
22894
|
+
if (release.protectedBy.length > 0)
|
|
22895
|
+
state = `kept: ${release.protectedBy.join(", ")}`;
|
|
22896
|
+
else if (release.markedAt)
|
|
22897
|
+
state = `marked ${release.markedAt}`;
|
|
22898
|
+
console.log(` ${release.releaseId} ${release.channel} ${formatBytes(release.bytes)} (${state})`);
|
|
22899
|
+
}
|
|
22900
|
+
if (report.untrackedBytes > 0)
|
|
22901
|
+
console.log(`${formatBytes(report.untrackedBytes)} is used by channels, collection markers, or incomplete/unrecognized objects and will not be swept as a release.`);
|
|
22902
|
+
};
|
|
22903
|
+
var inspectMobileUpdateStorage = async (args) => {
|
|
22904
|
+
const startedAt = performance.now();
|
|
22905
|
+
const { mobile } = await loadMobile(valueAfter(args, "--config"));
|
|
22906
|
+
if (!mobile.updates)
|
|
22907
|
+
throw new TypeError("mobile update storage requires mobile.updates config.");
|
|
22908
|
+
const policy = mobileUpdateRetention(args);
|
|
22909
|
+
const { publisher } = await mobileUpdatePublisher(args);
|
|
22910
|
+
const report = await inspectAbsoluteMobileUpdateStorage({
|
|
22911
|
+
appId: mobile.appId,
|
|
22912
|
+
publisher,
|
|
22913
|
+
...policy.minAgeMs === undefined ? {} : { minAgeMs: policy.minAgeMs },
|
|
22914
|
+
...policy.retainRecent === undefined ? {} : { retainRecent: policy.retainRecent }
|
|
22915
|
+
});
|
|
22916
|
+
if (args.includes("--json"))
|
|
22917
|
+
console.log(JSON.stringify(report, null, 2));
|
|
22918
|
+
else
|
|
22919
|
+
printMobileUpdateStorage(report);
|
|
22920
|
+
sendTelemetryEvent("mobile:update-storage", {
|
|
22921
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
22922
|
+
reclaimableReleaseCount: report.releases.filter((release) => release.protectedBy.length === 0).length,
|
|
22923
|
+
releaseCount: report.releaseCount
|
|
22924
|
+
});
|
|
22925
|
+
return report;
|
|
22926
|
+
};
|
|
22927
|
+
var collectMobileUpdates = async (args) => {
|
|
22928
|
+
const startedAt = performance.now();
|
|
22929
|
+
const { mobile } = await loadMobile(valueAfter(args, "--config"));
|
|
22930
|
+
if (!mobile.updates)
|
|
22931
|
+
throw new TypeError("mobile update gc requires mobile.updates config.");
|
|
22932
|
+
const policy = mobileUpdateRetention(args);
|
|
22933
|
+
const gracePeriodMs = mobileUpdateDays(args, "--grace-days");
|
|
22934
|
+
const { publisher } = await mobileUpdatePublisher(args);
|
|
22935
|
+
const result = await pruneAbsoluteMobileUpdates({
|
|
22936
|
+
appId: mobile.appId,
|
|
22937
|
+
apply: args.includes("--apply"),
|
|
22938
|
+
publisher,
|
|
22939
|
+
...gracePeriodMs === undefined ? {} : { gracePeriodMs },
|
|
22940
|
+
...policy.minAgeMs === undefined ? {} : { minAgeMs: policy.minAgeMs },
|
|
22941
|
+
...policy.retainRecent === undefined ? {} : { retainRecent: policy.retainRecent }
|
|
22942
|
+
});
|
|
22943
|
+
if (args.includes("--json"))
|
|
22944
|
+
console.log(JSON.stringify(result, null, 2));
|
|
22945
|
+
else {
|
|
22946
|
+
printMobileUpdateStorage(result);
|
|
22947
|
+
if (result.dryRun)
|
|
22948
|
+
console.log("No storage was changed. Re-run with --apply to mark eligible releases and sweep releases whose grace period has elapsed.");
|
|
22949
|
+
else
|
|
22950
|
+
console.log(`Collection applied: ${result.marked.length} marked, ${result.restored.length} restored, ${result.swept.length} swept, ${formatBytes(result.reclaimedBytes)} reclaimed.`);
|
|
22951
|
+
}
|
|
22952
|
+
sendTelemetryEvent("mobile:update-gc", {
|
|
22953
|
+
applied: !result.dryRun,
|
|
22954
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
22955
|
+
markedCount: result.marked.length,
|
|
22956
|
+
restoredCount: result.restored.length,
|
|
22957
|
+
sweptCount: result.swept.length
|
|
22958
|
+
});
|
|
22959
|
+
return result;
|
|
22960
|
+
};
|
|
22828
22961
|
var requireValueAfter = (args, flag) => {
|
|
22829
22962
|
const value = valueAfter(args, flag);
|
|
22830
22963
|
if (!value || value.startsWith("-")) {
|
|
@@ -24165,6 +24298,14 @@ var runMobile = async (args) => {
|
|
|
24165
24298
|
await rollbackMobileUpdate(args.slice(2));
|
|
24166
24299
|
return;
|
|
24167
24300
|
}
|
|
24301
|
+
if (command === "update" && args[1] === "storage") {
|
|
24302
|
+
await inspectMobileUpdateStorage(args.slice(2));
|
|
24303
|
+
return;
|
|
24304
|
+
}
|
|
24305
|
+
if (command === "update" && args[1] === "gc") {
|
|
24306
|
+
await collectMobileUpdates(args.slice(2));
|
|
24307
|
+
return;
|
|
24308
|
+
}
|
|
24168
24309
|
if (command === "publish" && args[1] === "android") {
|
|
24169
24310
|
await publishAndroid(args.slice(2));
|
|
24170
24311
|
return;
|
|
@@ -24173,7 +24314,7 @@ var runMobile = async (args) => {
|
|
|
24173
24314
|
await publishIos(args.slice(2));
|
|
24174
24315
|
return;
|
|
24175
24316
|
}
|
|
24176
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
|
|
24317
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
|
|
24177
24318
|
};
|
|
24178
24319
|
export {
|
|
24179
24320
|
runMobile
|
package/dist/index.js
CHANGED
|
@@ -12288,7 +12288,7 @@ import {
|
|
|
12288
12288
|
} from "crypto";
|
|
12289
12289
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
12290
12290
|
import path from "path";
|
|
12291
|
-
var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updates", MAX_FILE_BYTES, MAX_TOTAL_BYTES, HASH, RELEASE, APP_ID, NAME, EXPO_DESCRIPTOR = "_absolute/expo-update.json", EXPO_CODE_SIGNING_ALGORITHM = "rsa-v1_5-sha256", MobileUpdateRegistryError, object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field) => {
|
|
12291
|
+
var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updates", MAX_FILE_BYTES, MAX_TOTAL_BYTES, DAY_MS, DEFAULT_MIN_AGE_MS, DEFAULT_GRACE_PERIOD_MS, DEFAULT_RETAIN_RECENT = 5, HASH, RELEASE, APP_ID, NAME, EXPO_DESCRIPTOR = "_absolute/expo-update.json", EXPO_CODE_SIGNING_ALGORITHM = "rsa-v1_5-sha256", MobileUpdateRegistryError, object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field) => {
|
|
12292
12292
|
if (typeof value !== "string" || value.length === 0)
|
|
12293
12293
|
throw new MobileUpdateRegistryError(`Mobile update ${field} is invalid`);
|
|
12294
12294
|
return value;
|
|
@@ -12455,6 +12455,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12455
12455
|
const releaseRoot = (manifest) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;
|
|
12456
12456
|
const manifestKey = (manifest) => `${releaseRoot(manifest)}/update.json`;
|
|
12457
12457
|
const fileKey = (manifest, file2) => `${releaseRoot(manifest)}/files/${file2.path}`;
|
|
12458
|
+
const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
|
|
12458
12459
|
const channelKey = (appId, channel) => {
|
|
12459
12460
|
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
12460
12461
|
throw new MobileUpdateRegistryError("Mobile update channel identity is invalid");
|
|
@@ -12483,6 +12484,12 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12483
12484
|
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
12484
12485
|
return value;
|
|
12485
12486
|
};
|
|
12487
|
+
const assertNotMarked = async (appId, releaseId) => {
|
|
12488
|
+
if (!APP_ID.test(appId) || !RELEASE.test(releaseId))
|
|
12489
|
+
throw new MobileUpdateRegistryError("Mobile update release identity is invalid");
|
|
12490
|
+
if (await options.store.head(tombstoneKey(appId, releaseId)))
|
|
12491
|
+
throw new MobileUpdateRegistryError("Mobile update release is marked for collection. Increase retention and apply garbage collection to restore it before promotion");
|
|
12492
|
+
};
|
|
12486
12493
|
const writeChannel = async (input, signal) => {
|
|
12487
12494
|
const value = {
|
|
12488
12495
|
...input,
|
|
@@ -12507,6 +12514,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12507
12514
|
input.signal?.throwIfAborted();
|
|
12508
12515
|
if (input.rollout <= 0 || input.rollout > 1)
|
|
12509
12516
|
throw new MobileUpdateRegistryError("Mobile update rollout is invalid");
|
|
12517
|
+
await assertNotMarked(input.appId, input.releaseId);
|
|
12510
12518
|
const release = await readManifest(input.appId, input.releaseId);
|
|
12511
12519
|
if (!release || release.manifest.channel !== input.channel)
|
|
12512
12520
|
throw new MobileUpdateRegistryError("Mobile update was not published to this channel");
|
|
@@ -12552,11 +12560,221 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12552
12560
|
status: "selected"
|
|
12553
12561
|
};
|
|
12554
12562
|
};
|
|
12563
|
+
const retentionValues = (input) => {
|
|
12564
|
+
if (!APP_ID.test(input.appId))
|
|
12565
|
+
throw new MobileUpdateRegistryError("Mobile update appId is invalid");
|
|
12566
|
+
const retainRecent = input.retainRecent ?? DEFAULT_RETAIN_RECENT;
|
|
12567
|
+
const minAgeMs = input.minAgeMs ?? DEFAULT_MIN_AGE_MS;
|
|
12568
|
+
if (!Number.isSafeInteger(retainRecent) || retainRecent < 0)
|
|
12569
|
+
throw new MobileUpdateRegistryError("Mobile update retained release count is invalid");
|
|
12570
|
+
if (!Number.isSafeInteger(minAgeMs) || minAgeMs < 0)
|
|
12571
|
+
throw new MobileUpdateRegistryError("Mobile update minimum release age is invalid");
|
|
12572
|
+
return { minAgeMs, retainRecent };
|
|
12573
|
+
};
|
|
12574
|
+
const listObjects = async (appId, signal) => {
|
|
12575
|
+
const list = options.store.list;
|
|
12576
|
+
if (!list)
|
|
12577
|
+
throw new MobileUpdateRegistryError("Mobile update storage does not support lifecycle listing");
|
|
12578
|
+
const objects = [];
|
|
12579
|
+
const cursors = new Set;
|
|
12580
|
+
let cursor;
|
|
12581
|
+
do {
|
|
12582
|
+
signal?.throwIfAborted();
|
|
12583
|
+
const page = await list({
|
|
12584
|
+
...cursor ? { cursor } : {},
|
|
12585
|
+
prefix: `${root(appId)}/`
|
|
12586
|
+
});
|
|
12587
|
+
objects.push(...page.objects);
|
|
12588
|
+
if (!page.truncated)
|
|
12589
|
+
break;
|
|
12590
|
+
if (!page.cursor || cursors.has(page.cursor))
|
|
12591
|
+
throw new MobileUpdateRegistryError("Mobile update storage returned an invalid lifecycle cursor");
|
|
12592
|
+
cursors.add(page.cursor);
|
|
12593
|
+
cursor = page.cursor;
|
|
12594
|
+
} while (true);
|
|
12595
|
+
return objects;
|
|
12596
|
+
};
|
|
12597
|
+
const inventory = async (input) => {
|
|
12598
|
+
const { minAgeMs, retainRecent } = retentionValues(input);
|
|
12599
|
+
const objects = await listObjects(input.appId, input.signal);
|
|
12600
|
+
const appRoot = `${root(input.appId)}/`;
|
|
12601
|
+
const releasePrefix = `${appRoot}releases/`;
|
|
12602
|
+
const channelPrefix = `${appRoot}channels/`;
|
|
12603
|
+
const markerPrefix = `${appRoot}gc/`;
|
|
12604
|
+
const objectKeysByRelease = new Map;
|
|
12605
|
+
const objectBytesByRelease = new Map;
|
|
12606
|
+
for (const blobObject of objects) {
|
|
12607
|
+
if (!blobObject.key.startsWith(releasePrefix))
|
|
12608
|
+
continue;
|
|
12609
|
+
const suffix = blobObject.key.slice(releasePrefix.length);
|
|
12610
|
+
const releaseId = suffix.slice(0, suffix.indexOf("/"));
|
|
12611
|
+
if (!RELEASE.test(releaseId))
|
|
12612
|
+
continue;
|
|
12613
|
+
objectKeysByRelease.set(releaseId, [
|
|
12614
|
+
...objectKeysByRelease.get(releaseId) ?? [],
|
|
12615
|
+
blobObject.key
|
|
12616
|
+
]);
|
|
12617
|
+
objectBytesByRelease.set(releaseId, (objectBytesByRelease.get(releaseId) ?? 0) + blobObject.size);
|
|
12618
|
+
}
|
|
12619
|
+
const channels = [];
|
|
12620
|
+
for (const blobObject of objects) {
|
|
12621
|
+
if (!blobObject.key.startsWith(channelPrefix) || !blobObject.key.endsWith(".json"))
|
|
12622
|
+
continue;
|
|
12623
|
+
const bytes = await options.store.get(blobObject.key);
|
|
12624
|
+
if (!bytes)
|
|
12625
|
+
throw new MobileUpdateRegistryError("Mobile update channel disappeared during lifecycle inspection");
|
|
12626
|
+
const channel = parseChannel(decode(bytes));
|
|
12627
|
+
if (channel.appId !== input.appId)
|
|
12628
|
+
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
12629
|
+
channels.push(channel);
|
|
12630
|
+
}
|
|
12631
|
+
const markedAt = new Map;
|
|
12632
|
+
for (const blobObject of objects) {
|
|
12633
|
+
if (!blobObject.key.startsWith(markerPrefix) || !blobObject.key.endsWith(".json"))
|
|
12634
|
+
continue;
|
|
12635
|
+
const releaseId = blobObject.key.slice(markerPrefix.length, -".json".length);
|
|
12636
|
+
if (!RELEASE.test(releaseId))
|
|
12637
|
+
continue;
|
|
12638
|
+
const bytes = await options.store.get(blobObject.key);
|
|
12639
|
+
if (!bytes)
|
|
12640
|
+
continue;
|
|
12641
|
+
const marker = decode(bytes);
|
|
12642
|
+
if (!object3(marker) || marker.format !== 1 || marker.appId !== input.appId || marker.releaseId !== releaseId || !iso(marker.markedAt))
|
|
12643
|
+
throw new MobileUpdateRegistryError("Mobile update collection marker is invalid");
|
|
12644
|
+
markedAt.set(releaseId, marker.markedAt);
|
|
12645
|
+
}
|
|
12646
|
+
const active = new Set(channels.flatMap((channel) => channel.releaseId ? [channel.releaseId] : []));
|
|
12647
|
+
const fallback = new Set(channels.flatMap((channel) => channel.fallbackReleaseId ? [channel.fallbackReleaseId] : []));
|
|
12648
|
+
const manifests = [];
|
|
12649
|
+
for (const releaseId of objectKeysByRelease.keys()) {
|
|
12650
|
+
const release = await readManifest(input.appId, releaseId);
|
|
12651
|
+
if (release)
|
|
12652
|
+
manifests.push(release.manifest);
|
|
12653
|
+
}
|
|
12654
|
+
const recent = new Set;
|
|
12655
|
+
const manifestsByChannel = new Map;
|
|
12656
|
+
for (const manifest of manifests)
|
|
12657
|
+
manifestsByChannel.set(manifest.channel, [
|
|
12658
|
+
...manifestsByChannel.get(manifest.channel) ?? [],
|
|
12659
|
+
manifest
|
|
12660
|
+
]);
|
|
12661
|
+
for (const releases2 of manifestsByChannel.values())
|
|
12662
|
+
for (const manifest of releases2.toSorted((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, retainRecent))
|
|
12663
|
+
recent.add(manifest.releaseId);
|
|
12664
|
+
const now = clock().getTime();
|
|
12665
|
+
const releases = manifests.map((manifest) => {
|
|
12666
|
+
const protectedBy = [];
|
|
12667
|
+
if (active.has(manifest.releaseId))
|
|
12668
|
+
protectedBy.push("active");
|
|
12669
|
+
if (fallback.has(manifest.releaseId))
|
|
12670
|
+
protectedBy.push("fallback");
|
|
12671
|
+
if (recent.has(manifest.releaseId))
|
|
12672
|
+
protectedBy.push("recent");
|
|
12673
|
+
if (now - Date.parse(manifest.createdAt) < minAgeMs)
|
|
12674
|
+
protectedBy.push("age");
|
|
12675
|
+
return {
|
|
12676
|
+
bytes: objectBytesByRelease.get(manifest.releaseId) ?? 0,
|
|
12677
|
+
channel: manifest.channel,
|
|
12678
|
+
createdAt: manifest.createdAt,
|
|
12679
|
+
...markedAt.has(manifest.releaseId) ? { markedAt: markedAt.get(manifest.releaseId) } : {},
|
|
12680
|
+
objectCount: objectKeysByRelease.get(manifest.releaseId)?.length ?? 0,
|
|
12681
|
+
protectedBy,
|
|
12682
|
+
releaseId: manifest.releaseId
|
|
12683
|
+
};
|
|
12684
|
+
}).toSorted((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
12685
|
+
const releaseBytes = releases.reduce((total, release) => total + release.bytes, 0);
|
|
12686
|
+
const totalBytes = objects.reduce((total, object22) => total + object22.size, 0);
|
|
12687
|
+
return {
|
|
12688
|
+
objectKeysByRelease,
|
|
12689
|
+
report: {
|
|
12690
|
+
appId: input.appId,
|
|
12691
|
+
channelCount: channels.length,
|
|
12692
|
+
reclaimableBytes: releases.filter((release) => release.protectedBy.length === 0).reduce((total, release) => total + release.bytes, 0),
|
|
12693
|
+
releaseBytes,
|
|
12694
|
+
releaseCount: releases.length,
|
|
12695
|
+
releases,
|
|
12696
|
+
totalBytes,
|
|
12697
|
+
totalObjectCount: objects.length,
|
|
12698
|
+
untrackedBytes: totalBytes - releaseBytes
|
|
12699
|
+
}
|
|
12700
|
+
};
|
|
12701
|
+
};
|
|
12702
|
+
const pruneUpdates = async (input) => {
|
|
12703
|
+
const initial = await inventory(input);
|
|
12704
|
+
const result = {
|
|
12705
|
+
...initial.report,
|
|
12706
|
+
dryRun: input.apply !== true,
|
|
12707
|
+
marked: [],
|
|
12708
|
+
reclaimedBytes: 0,
|
|
12709
|
+
restored: [],
|
|
12710
|
+
swept: []
|
|
12711
|
+
};
|
|
12712
|
+
if (input.apply !== true)
|
|
12713
|
+
return result;
|
|
12714
|
+
const remove = options.store.delete;
|
|
12715
|
+
if (!remove)
|
|
12716
|
+
throw new MobileUpdateRegistryError("Mobile update storage does not support lifecycle deletion");
|
|
12717
|
+
const gracePeriodMs = input.gracePeriodMs ?? DEFAULT_GRACE_PERIOD_MS;
|
|
12718
|
+
if (!Number.isSafeInteger(gracePeriodMs) || gracePeriodMs < 0)
|
|
12719
|
+
throw new MobileUpdateRegistryError("Mobile update collection grace period is invalid");
|
|
12720
|
+
const now = clock();
|
|
12721
|
+
for (const release of initial.report.releases) {
|
|
12722
|
+
input.signal?.throwIfAborted();
|
|
12723
|
+
const marker = tombstoneKey(input.appId, release.releaseId);
|
|
12724
|
+
if (release.protectedBy.length > 0) {
|
|
12725
|
+
if (release.markedAt) {
|
|
12726
|
+
await remove(marker);
|
|
12727
|
+
result.restored.push(release.releaseId);
|
|
12728
|
+
}
|
|
12729
|
+
continue;
|
|
12730
|
+
}
|
|
12731
|
+
if (!release.markedAt) {
|
|
12732
|
+
const bytes = json({
|
|
12733
|
+
appId: input.appId,
|
|
12734
|
+
format: 1,
|
|
12735
|
+
markedAt: now.toISOString(),
|
|
12736
|
+
releaseId: release.releaseId
|
|
12737
|
+
});
|
|
12738
|
+
await options.store.put(marker, bytes, {
|
|
12739
|
+
cacheControl: "no-cache",
|
|
12740
|
+
contentType: "application/json",
|
|
12741
|
+
maxBytes: bytes.byteLength,
|
|
12742
|
+
metadata: { releaseid: release.releaseId, sha256: digest(bytes) },
|
|
12743
|
+
signal: input.signal
|
|
12744
|
+
});
|
|
12745
|
+
result.marked.push(release.releaseId);
|
|
12746
|
+
}
|
|
12747
|
+
}
|
|
12748
|
+
const current = await inventory(input);
|
|
12749
|
+
for (const release of current.report.releases) {
|
|
12750
|
+
input.signal?.throwIfAborted();
|
|
12751
|
+
if (release.protectedBy.length > 0 || !release.markedAt || now.getTime() - Date.parse(release.markedAt) < gracePeriodMs || result.marked.includes(release.releaseId))
|
|
12752
|
+
continue;
|
|
12753
|
+
const checked = await inventory(input);
|
|
12754
|
+
const candidate = checked.report.releases.find((value) => value.releaseId === release.releaseId);
|
|
12755
|
+
if (!candidate || candidate.protectedBy.length > 0 || !candidate.markedAt)
|
|
12756
|
+
continue;
|
|
12757
|
+
const keys = checked.objectKeysByRelease.get(release.releaseId) ?? [];
|
|
12758
|
+
for (const key of keys.toSorted((left, right) => {
|
|
12759
|
+
const leftManifest = left.endsWith("/update.json");
|
|
12760
|
+
const rightManifest = right.endsWith("/update.json");
|
|
12761
|
+
return Number(leftManifest) - Number(rightManifest);
|
|
12762
|
+
}))
|
|
12763
|
+
await remove(key);
|
|
12764
|
+
await remove(tombstoneKey(input.appId, release.releaseId));
|
|
12765
|
+
result.reclaimedBytes += candidate.bytes;
|
|
12766
|
+
result.swept.push(release.releaseId);
|
|
12767
|
+
}
|
|
12768
|
+
return result;
|
|
12769
|
+
};
|
|
12555
12770
|
return {
|
|
12771
|
+
inspectUpdateStorage: async (input) => (await inventory(input)).report,
|
|
12772
|
+
pruneUpdates,
|
|
12556
12773
|
publishUpdate: async (input) => {
|
|
12557
12774
|
input.signal?.throwIfAborted();
|
|
12558
12775
|
const manifest = parseMobileUpdateManifest(input.manifest);
|
|
12559
12776
|
verifyManifestSignature(manifest, options.publicKeys);
|
|
12777
|
+
await assertNotMarked(manifest.appId, manifest.releaseId);
|
|
12560
12778
|
const localRoot = path.resolve(input.releaseDirectory);
|
|
12561
12779
|
const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile4(path.join(localRoot, "update.json"), "utf8")));
|
|
12562
12780
|
if (JSON.stringify(localManifest) !== JSON.stringify(manifest))
|
|
@@ -12621,6 +12839,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12621
12839
|
if (!existing)
|
|
12622
12840
|
throw new MobileUpdateRegistryError("Mobile update channel does not exist");
|
|
12623
12841
|
if (input.releaseId) {
|
|
12842
|
+
await assertNotMarked(input.appId, input.releaseId);
|
|
12624
12843
|
const release = await readManifest(input.appId, input.releaseId);
|
|
12625
12844
|
if (!release || release.manifest.channel !== input.channel)
|
|
12626
12845
|
throw new MobileUpdateRegistryError("Mobile rollback release was not published");
|
|
@@ -12953,6 +13172,9 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12953
13172
|
var init_mobileUpdate = __esm(() => {
|
|
12954
13173
|
MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
12955
13174
|
MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
13175
|
+
DAY_MS = 24 * 60 * 60 * 1000;
|
|
13176
|
+
DEFAULT_MIN_AGE_MS = 30 * DAY_MS;
|
|
13177
|
+
DEFAULT_GRACE_PERIOD_MS = 7 * DAY_MS;
|
|
12956
13178
|
HASH = /^[a-f0-9]{64}$/;
|
|
12957
13179
|
RELEASE = /^amu_[a-f0-9]{64}$/;
|
|
12958
13180
|
APP_ID = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
@@ -44587,5 +44809,5 @@ export {
|
|
|
44587
44809
|
wrapPageHandlerWithStreamingSlots
|
|
44588
44810
|
};
|
|
44589
44811
|
|
|
44590
|
-
//# debugId=
|
|
44812
|
+
//# debugId=F0D48E431F12D4EB64756E2164756E21
|
|
44591
44813
|
//# sourceMappingURL=index.js.map
|