@absolutejs/absolute 0.20.0-beta.81 → 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 +269 -17
- package/dist/build.js.map +4 -4
- package/dist/cli/index.js +1 -1
- package/dist/cli/{mobile-rdwdcks1.js → mobile-9dx39bd3.js} +184 -12
- package/dist/index.js +269 -17
- package/dist/index.js.map +4 -4
- package/dist/mobile/index.js +310 -17
- package/dist/mobile/index.js.map +5 -5
- package/dist/mobile/remoteMacAgentEntry.js +102 -102
- package/dist/src/mobile/updatePublisher.d.ts +19 -0
- package/dist/src/mobile/updateServer.d.ts +1 -0
- package/package.json +6 -2
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))
|
|
@@ -12580,10 +12798,10 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12580
12798
|
cacheControl: "public, max-age=31536000, immutable",
|
|
12581
12799
|
contentType: "application/octet-stream",
|
|
12582
12800
|
maxBytes: file2.bytes,
|
|
12583
|
-
metadata: {
|
|
12801
|
+
metadata: { releaseid: manifest.releaseId, sha256: file2.sha256 },
|
|
12584
12802
|
signal: input.signal
|
|
12585
12803
|
});
|
|
12586
|
-
} else if (stored.size !== file2.bytes || stored.metadata?.sha256 !== file2.sha256 || stored.metadata?.releaseId !== manifest.releaseId)
|
|
12804
|
+
} else if (stored.size !== file2.bytes || stored.metadata?.sha256 !== file2.sha256 || (stored.metadata?.releaseid ?? stored.metadata?.releaseId) !== manifest.releaseId)
|
|
12587
12805
|
throw new MobileUpdateRegistryError("Stored mobile update file identity changed");
|
|
12588
12806
|
}
|
|
12589
12807
|
const bytes = json(manifest);
|
|
@@ -12591,7 +12809,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12591
12809
|
cacheControl: "public, max-age=31536000, immutable",
|
|
12592
12810
|
contentType: "application/json",
|
|
12593
12811
|
maxBytes: bytes.byteLength,
|
|
12594
|
-
metadata: {
|
|
12812
|
+
metadata: { releaseid: manifest.releaseId, sha256: digest(bytes) },
|
|
12595
12813
|
signal: input.signal
|
|
12596
12814
|
});
|
|
12597
12815
|
if (!await readManifest(manifest.appId, manifest.releaseId))
|
|
@@ -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");
|
|
@@ -12664,7 +12883,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
12664
12883
|
]);
|
|
12665
12884
|
if (!bytes || !head)
|
|
12666
12885
|
return null;
|
|
12667
|
-
if (bytes.byteLength !== file2.bytes || head.size !== file2.bytes || head.metadata?.sha256 !== file2.sha256 || head.metadata?.releaseId !== release.manifest.releaseId || digest(bytes) !== file2.sha256)
|
|
12886
|
+
if (bytes.byteLength !== file2.bytes || head.size !== file2.bytes || head.metadata?.sha256 !== file2.sha256 || (head.metadata?.releaseid ?? head.metadata?.releaseId) !== release.manifest.releaseId || digest(bytes) !== file2.sha256)
|
|
12668
12887
|
throw new MobileUpdateRegistryError("Stored mobile update file integrity failed");
|
|
12669
12888
|
return { bytes, file: file2 };
|
|
12670
12889
|
}
|
|
@@ -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]*)+$/;
|
|
@@ -13023,10 +13245,27 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13023
13245
|
const registry2 = loaded.default ?? loaded.registry;
|
|
13024
13246
|
if (!isRegistry2(registry2))
|
|
13025
13247
|
throw new TypeError("Mobile update registry must implement publication, promotion, rollback, resolution, and file reads.");
|
|
13248
|
+
const metadata2 = serverMetadata(loaded.absoluteMobileUpdateServer);
|
|
13249
|
+
const verifier = loaded.verifyAbsoluteMobileUpdateServer;
|
|
13250
|
+
if (metadata2.storage === "durable" && typeof verifier !== "function")
|
|
13251
|
+
throw new TypeError("Mobile update registries marked durable must export verifyAbsoluteMobileUpdateServer(). Re-run `absolute mobile update provision --storage s3 --force` or provide an active durability check.");
|
|
13026
13252
|
return {
|
|
13027
|
-
metadata:
|
|
13028
|
-
registry: registry2
|
|
13253
|
+
metadata: metadata2,
|
|
13254
|
+
registry: registry2,
|
|
13255
|
+
...typeof verifier === "function" ? {
|
|
13256
|
+
verifyDurability: async () => {
|
|
13257
|
+
await verifier();
|
|
13258
|
+
}
|
|
13259
|
+
} : {}
|
|
13029
13260
|
};
|
|
13261
|
+
}, verifyDurableModule = async (module) => {
|
|
13262
|
+
if (module.metadata.storage !== "durable")
|
|
13263
|
+
throw new TypeError("Mobile production updates require durable object storage. Re-run `absolute mobile update provision --storage s3 --force` or configure a durable adapter.");
|
|
13264
|
+
try {
|
|
13265
|
+
await module.verifyDurability?.();
|
|
13266
|
+
} catch (error) {
|
|
13267
|
+
throw new TypeError(`Durable mobile update storage verification failed for ${module.metadata.provider}. Check the bucket, endpoint, credentials, and read/write/delete permissions.`, { cause: error });
|
|
13268
|
+
}
|
|
13030
13269
|
}, expoSigningOptions = (config) => {
|
|
13031
13270
|
if (!config.updates?.expoCodeSigning)
|
|
13032
13271
|
return;
|
|
@@ -13058,8 +13297,8 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13058
13297
|
if (!updates || !server?.autoMount)
|
|
13059
13298
|
return new Elysia4({ name: "absolutejs-mobile-updates-disabled" });
|
|
13060
13299
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, server.registryModule);
|
|
13061
|
-
if (options.production
|
|
13062
|
-
|
|
13300
|
+
if (options.production)
|
|
13301
|
+
await verifyDurableModule(module);
|
|
13063
13302
|
const manifest = new URL(updates.manifestUrl);
|
|
13064
13303
|
if (!manifest.pathname.endsWith("/update.json"))
|
|
13065
13304
|
throw new TypeError("Auto-mounted mobile update manifests must end in /update.json.");
|
|
@@ -13077,8 +13316,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
13077
13316
|
if (!config.updates)
|
|
13078
13317
|
return;
|
|
13079
13318
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
|
|
13080
|
-
|
|
13081
|
-
throw new TypeError("Mobile production updates require durable object storage; the configured registry is local-only.");
|
|
13319
|
+
await verifyDurableModule(module);
|
|
13082
13320
|
if (config.engine === "expo")
|
|
13083
13321
|
expoSigningOptions(config);
|
|
13084
13322
|
return module.metadata;
|
|
@@ -13105,7 +13343,8 @@ export default createMobileUpdateRegistry({
|
|
|
13105
13343
|
store
|
|
13106
13344
|
});
|
|
13107
13345
|
`;
|
|
13108
|
-
return `import {
|
|
13346
|
+
return `import { randomUUID } from 'node:crypto';
|
|
13347
|
+
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
13109
13348
|
import { awsS3BlobStore } from '@absolutejs/blob/aws-s3';
|
|
13110
13349
|
import { createMobileUpdateRegistry } from '@absolutejs/deploy/mobile-update';
|
|
13111
13350
|
|
|
@@ -13117,6 +13356,7 @@ const required = (name: string) => {
|
|
|
13117
13356
|
return value;
|
|
13118
13357
|
};
|
|
13119
13358
|
|
|
13359
|
+
const bucket = required('ABSOLUTE_MOBILE_UPDATE_S3_BUCKET');
|
|
13120
13360
|
const client = new S3Client({
|
|
13121
13361
|
region: process.env.ABSOLUTE_MOBILE_UPDATE_S3_REGION ?? 'auto',
|
|
13122
13362
|
forcePathStyle: process.env.ABSOLUTE_MOBILE_UPDATE_S3_FORCE_PATH_STYLE === '1',
|
|
@@ -13124,10 +13364,22 @@ const client = new S3Client({
|
|
|
13124
13364
|
? { endpoint: process.env.ABSOLUTE_MOBILE_UPDATE_S3_ENDPOINT }
|
|
13125
13365
|
: {})
|
|
13126
13366
|
});
|
|
13127
|
-
const store = awsS3BlobStore({
|
|
13128
|
-
|
|
13129
|
-
|
|
13130
|
-
}
|
|
13367
|
+
const store = awsS3BlobStore({ bucket, client });
|
|
13368
|
+
|
|
13369
|
+
export const verifyAbsoluteMobileUpdateServer = async () => {
|
|
13370
|
+
const key = \`absolutejs/mobile-updates/_health/\${randomUUID()}\`;
|
|
13371
|
+
const expected = randomUUID();
|
|
13372
|
+
let stored = false;
|
|
13373
|
+
try {
|
|
13374
|
+
await client.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: expected }));
|
|
13375
|
+
stored = true;
|
|
13376
|
+
const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
13377
|
+
if ((await response.Body?.transformToString()) !== expected)
|
|
13378
|
+
throw new Error('Durability probe read did not match its write.');
|
|
13379
|
+
} finally {
|
|
13380
|
+
if (stored) await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
13381
|
+
}
|
|
13382
|
+
};
|
|
13131
13383
|
|
|
13132
13384
|
export default createMobileUpdateRegistry({
|
|
13133
13385
|
publicKeys: ${publicKeysSource(options.publicKeys)},
|
|
@@ -44557,5 +44809,5 @@ export {
|
|
|
44557
44809
|
wrapPageHandlerWithStreamingSlots
|
|
44558
44810
|
};
|
|
44559
44811
|
|
|
44560
|
-
//# debugId=
|
|
44812
|
+
//# debugId=F0D48E431F12D4EB64756E2164756E21
|
|
44561
44813
|
//# sourceMappingURL=index.js.map
|