@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/mobile/index.js
CHANGED
|
@@ -19970,7 +19970,7 @@ import {
|
|
|
19970
19970
|
} from "crypto";
|
|
19971
19971
|
import { readFile as readFile23, stat as stat5 } from "fs/promises";
|
|
19972
19972
|
import path from "path";
|
|
19973
|
-
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, object6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field2) => {
|
|
19973
|
+
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, object6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field2) => {
|
|
19974
19974
|
if (typeof value !== "string" || value.length === 0)
|
|
19975
19975
|
throw new MobileUpdateRegistryError(`Mobile update ${field2} is invalid`);
|
|
19976
19976
|
return value;
|
|
@@ -20137,6 +20137,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20137
20137
|
const releaseRoot = (manifest) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;
|
|
20138
20138
|
const manifestKey = (manifest) => `${releaseRoot(manifest)}/update.json`;
|
|
20139
20139
|
const fileKey = (manifest, file) => `${releaseRoot(manifest)}/files/${file.path}`;
|
|
20140
|
+
const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
|
|
20140
20141
|
const channelKey = (appId, channel) => {
|
|
20141
20142
|
if (!APP_ID.test(appId) || !NAME.test(channel))
|
|
20142
20143
|
throw new MobileUpdateRegistryError("Mobile update channel identity is invalid");
|
|
@@ -20165,6 +20166,12 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20165
20166
|
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
20166
20167
|
return value;
|
|
20167
20168
|
};
|
|
20169
|
+
const assertNotMarked = async (appId, releaseId) => {
|
|
20170
|
+
if (!APP_ID.test(appId) || !RELEASE.test(releaseId))
|
|
20171
|
+
throw new MobileUpdateRegistryError("Mobile update release identity is invalid");
|
|
20172
|
+
if (await options.store.head(tombstoneKey(appId, releaseId)))
|
|
20173
|
+
throw new MobileUpdateRegistryError("Mobile update release is marked for collection. Increase retention and apply garbage collection to restore it before promotion");
|
|
20174
|
+
};
|
|
20168
20175
|
const writeChannel = async (input, signal) => {
|
|
20169
20176
|
const value = {
|
|
20170
20177
|
...input,
|
|
@@ -20189,6 +20196,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20189
20196
|
input.signal?.throwIfAborted();
|
|
20190
20197
|
if (input.rollout <= 0 || input.rollout > 1)
|
|
20191
20198
|
throw new MobileUpdateRegistryError("Mobile update rollout is invalid");
|
|
20199
|
+
await assertNotMarked(input.appId, input.releaseId);
|
|
20192
20200
|
const release = await readManifest(input.appId, input.releaseId);
|
|
20193
20201
|
if (!release || release.manifest.channel !== input.channel)
|
|
20194
20202
|
throw new MobileUpdateRegistryError("Mobile update was not published to this channel");
|
|
@@ -20234,11 +20242,221 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20234
20242
|
status: "selected"
|
|
20235
20243
|
};
|
|
20236
20244
|
};
|
|
20245
|
+
const retentionValues = (input) => {
|
|
20246
|
+
if (!APP_ID.test(input.appId))
|
|
20247
|
+
throw new MobileUpdateRegistryError("Mobile update appId is invalid");
|
|
20248
|
+
const retainRecent = input.retainRecent ?? DEFAULT_RETAIN_RECENT;
|
|
20249
|
+
const minAgeMs = input.minAgeMs ?? DEFAULT_MIN_AGE_MS;
|
|
20250
|
+
if (!Number.isSafeInteger(retainRecent) || retainRecent < 0)
|
|
20251
|
+
throw new MobileUpdateRegistryError("Mobile update retained release count is invalid");
|
|
20252
|
+
if (!Number.isSafeInteger(minAgeMs) || minAgeMs < 0)
|
|
20253
|
+
throw new MobileUpdateRegistryError("Mobile update minimum release age is invalid");
|
|
20254
|
+
return { minAgeMs, retainRecent };
|
|
20255
|
+
};
|
|
20256
|
+
const listObjects = async (appId, signal) => {
|
|
20257
|
+
const list = options.store.list;
|
|
20258
|
+
if (!list)
|
|
20259
|
+
throw new MobileUpdateRegistryError("Mobile update storage does not support lifecycle listing");
|
|
20260
|
+
const objects = [];
|
|
20261
|
+
const cursors = new Set;
|
|
20262
|
+
let cursor;
|
|
20263
|
+
do {
|
|
20264
|
+
signal?.throwIfAborted();
|
|
20265
|
+
const page = await list({
|
|
20266
|
+
...cursor ? { cursor } : {},
|
|
20267
|
+
prefix: `${root(appId)}/`
|
|
20268
|
+
});
|
|
20269
|
+
objects.push(...page.objects);
|
|
20270
|
+
if (!page.truncated)
|
|
20271
|
+
break;
|
|
20272
|
+
if (!page.cursor || cursors.has(page.cursor))
|
|
20273
|
+
throw new MobileUpdateRegistryError("Mobile update storage returned an invalid lifecycle cursor");
|
|
20274
|
+
cursors.add(page.cursor);
|
|
20275
|
+
cursor = page.cursor;
|
|
20276
|
+
} while (true);
|
|
20277
|
+
return objects;
|
|
20278
|
+
};
|
|
20279
|
+
const inventory = async (input) => {
|
|
20280
|
+
const { minAgeMs, retainRecent } = retentionValues(input);
|
|
20281
|
+
const objects = await listObjects(input.appId, input.signal);
|
|
20282
|
+
const appRoot = `${root(input.appId)}/`;
|
|
20283
|
+
const releasePrefix = `${appRoot}releases/`;
|
|
20284
|
+
const channelPrefix = `${appRoot}channels/`;
|
|
20285
|
+
const markerPrefix = `${appRoot}gc/`;
|
|
20286
|
+
const objectKeysByRelease = new Map;
|
|
20287
|
+
const objectBytesByRelease = new Map;
|
|
20288
|
+
for (const blobObject of objects) {
|
|
20289
|
+
if (!blobObject.key.startsWith(releasePrefix))
|
|
20290
|
+
continue;
|
|
20291
|
+
const suffix = blobObject.key.slice(releasePrefix.length);
|
|
20292
|
+
const releaseId = suffix.slice(0, suffix.indexOf("/"));
|
|
20293
|
+
if (!RELEASE.test(releaseId))
|
|
20294
|
+
continue;
|
|
20295
|
+
objectKeysByRelease.set(releaseId, [
|
|
20296
|
+
...objectKeysByRelease.get(releaseId) ?? [],
|
|
20297
|
+
blobObject.key
|
|
20298
|
+
]);
|
|
20299
|
+
objectBytesByRelease.set(releaseId, (objectBytesByRelease.get(releaseId) ?? 0) + blobObject.size);
|
|
20300
|
+
}
|
|
20301
|
+
const channels = [];
|
|
20302
|
+
for (const blobObject of objects) {
|
|
20303
|
+
if (!blobObject.key.startsWith(channelPrefix) || !blobObject.key.endsWith(".json"))
|
|
20304
|
+
continue;
|
|
20305
|
+
const bytes = await options.store.get(blobObject.key);
|
|
20306
|
+
if (!bytes)
|
|
20307
|
+
throw new MobileUpdateRegistryError("Mobile update channel disappeared during lifecycle inspection");
|
|
20308
|
+
const channel = parseChannel(decode2(bytes));
|
|
20309
|
+
if (channel.appId !== input.appId)
|
|
20310
|
+
throw new MobileUpdateRegistryError("Stored mobile update channel identity changed");
|
|
20311
|
+
channels.push(channel);
|
|
20312
|
+
}
|
|
20313
|
+
const markedAt = new Map;
|
|
20314
|
+
for (const blobObject of objects) {
|
|
20315
|
+
if (!blobObject.key.startsWith(markerPrefix) || !blobObject.key.endsWith(".json"))
|
|
20316
|
+
continue;
|
|
20317
|
+
const releaseId = blobObject.key.slice(markerPrefix.length, -".json".length);
|
|
20318
|
+
if (!RELEASE.test(releaseId))
|
|
20319
|
+
continue;
|
|
20320
|
+
const bytes = await options.store.get(blobObject.key);
|
|
20321
|
+
if (!bytes)
|
|
20322
|
+
continue;
|
|
20323
|
+
const marker = decode2(bytes);
|
|
20324
|
+
if (!object6(marker) || marker.format !== 1 || marker.appId !== input.appId || marker.releaseId !== releaseId || !iso(marker.markedAt))
|
|
20325
|
+
throw new MobileUpdateRegistryError("Mobile update collection marker is invalid");
|
|
20326
|
+
markedAt.set(releaseId, marker.markedAt);
|
|
20327
|
+
}
|
|
20328
|
+
const active = new Set(channels.flatMap((channel) => channel.releaseId ? [channel.releaseId] : []));
|
|
20329
|
+
const fallback = new Set(channels.flatMap((channel) => channel.fallbackReleaseId ? [channel.fallbackReleaseId] : []));
|
|
20330
|
+
const manifests = [];
|
|
20331
|
+
for (const releaseId of objectKeysByRelease.keys()) {
|
|
20332
|
+
const release = await readManifest(input.appId, releaseId);
|
|
20333
|
+
if (release)
|
|
20334
|
+
manifests.push(release.manifest);
|
|
20335
|
+
}
|
|
20336
|
+
const recent = new Set;
|
|
20337
|
+
const manifestsByChannel = new Map;
|
|
20338
|
+
for (const manifest of manifests)
|
|
20339
|
+
manifestsByChannel.set(manifest.channel, [
|
|
20340
|
+
...manifestsByChannel.get(manifest.channel) ?? [],
|
|
20341
|
+
manifest
|
|
20342
|
+
]);
|
|
20343
|
+
for (const releases2 of manifestsByChannel.values())
|
|
20344
|
+
for (const manifest of releases2.toSorted((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, retainRecent))
|
|
20345
|
+
recent.add(manifest.releaseId);
|
|
20346
|
+
const now = clock().getTime();
|
|
20347
|
+
const releases = manifests.map((manifest) => {
|
|
20348
|
+
const protectedBy = [];
|
|
20349
|
+
if (active.has(manifest.releaseId))
|
|
20350
|
+
protectedBy.push("active");
|
|
20351
|
+
if (fallback.has(manifest.releaseId))
|
|
20352
|
+
protectedBy.push("fallback");
|
|
20353
|
+
if (recent.has(manifest.releaseId))
|
|
20354
|
+
protectedBy.push("recent");
|
|
20355
|
+
if (now - Date.parse(manifest.createdAt) < minAgeMs)
|
|
20356
|
+
protectedBy.push("age");
|
|
20357
|
+
return {
|
|
20358
|
+
bytes: objectBytesByRelease.get(manifest.releaseId) ?? 0,
|
|
20359
|
+
channel: manifest.channel,
|
|
20360
|
+
createdAt: manifest.createdAt,
|
|
20361
|
+
...markedAt.has(manifest.releaseId) ? { markedAt: markedAt.get(manifest.releaseId) } : {},
|
|
20362
|
+
objectCount: objectKeysByRelease.get(manifest.releaseId)?.length ?? 0,
|
|
20363
|
+
protectedBy,
|
|
20364
|
+
releaseId: manifest.releaseId
|
|
20365
|
+
};
|
|
20366
|
+
}).toSorted((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
20367
|
+
const releaseBytes = releases.reduce((total, release) => total + release.bytes, 0);
|
|
20368
|
+
const totalBytes = objects.reduce((total, object22) => total + object22.size, 0);
|
|
20369
|
+
return {
|
|
20370
|
+
objectKeysByRelease,
|
|
20371
|
+
report: {
|
|
20372
|
+
appId: input.appId,
|
|
20373
|
+
channelCount: channels.length,
|
|
20374
|
+
reclaimableBytes: releases.filter((release) => release.protectedBy.length === 0).reduce((total, release) => total + release.bytes, 0),
|
|
20375
|
+
releaseBytes,
|
|
20376
|
+
releaseCount: releases.length,
|
|
20377
|
+
releases,
|
|
20378
|
+
totalBytes,
|
|
20379
|
+
totalObjectCount: objects.length,
|
|
20380
|
+
untrackedBytes: totalBytes - releaseBytes
|
|
20381
|
+
}
|
|
20382
|
+
};
|
|
20383
|
+
};
|
|
20384
|
+
const pruneUpdates = async (input) => {
|
|
20385
|
+
const initial = await inventory(input);
|
|
20386
|
+
const result = {
|
|
20387
|
+
...initial.report,
|
|
20388
|
+
dryRun: input.apply !== true,
|
|
20389
|
+
marked: [],
|
|
20390
|
+
reclaimedBytes: 0,
|
|
20391
|
+
restored: [],
|
|
20392
|
+
swept: []
|
|
20393
|
+
};
|
|
20394
|
+
if (input.apply !== true)
|
|
20395
|
+
return result;
|
|
20396
|
+
const remove = options.store.delete;
|
|
20397
|
+
if (!remove)
|
|
20398
|
+
throw new MobileUpdateRegistryError("Mobile update storage does not support lifecycle deletion");
|
|
20399
|
+
const gracePeriodMs = input.gracePeriodMs ?? DEFAULT_GRACE_PERIOD_MS;
|
|
20400
|
+
if (!Number.isSafeInteger(gracePeriodMs) || gracePeriodMs < 0)
|
|
20401
|
+
throw new MobileUpdateRegistryError("Mobile update collection grace period is invalid");
|
|
20402
|
+
const now = clock();
|
|
20403
|
+
for (const release of initial.report.releases) {
|
|
20404
|
+
input.signal?.throwIfAborted();
|
|
20405
|
+
const marker = tombstoneKey(input.appId, release.releaseId);
|
|
20406
|
+
if (release.protectedBy.length > 0) {
|
|
20407
|
+
if (release.markedAt) {
|
|
20408
|
+
await remove(marker);
|
|
20409
|
+
result.restored.push(release.releaseId);
|
|
20410
|
+
}
|
|
20411
|
+
continue;
|
|
20412
|
+
}
|
|
20413
|
+
if (!release.markedAt) {
|
|
20414
|
+
const bytes = json({
|
|
20415
|
+
appId: input.appId,
|
|
20416
|
+
format: 1,
|
|
20417
|
+
markedAt: now.toISOString(),
|
|
20418
|
+
releaseId: release.releaseId
|
|
20419
|
+
});
|
|
20420
|
+
await options.store.put(marker, bytes, {
|
|
20421
|
+
cacheControl: "no-cache",
|
|
20422
|
+
contentType: "application/json",
|
|
20423
|
+
maxBytes: bytes.byteLength,
|
|
20424
|
+
metadata: { releaseid: release.releaseId, sha256: digest(bytes) },
|
|
20425
|
+
signal: input.signal
|
|
20426
|
+
});
|
|
20427
|
+
result.marked.push(release.releaseId);
|
|
20428
|
+
}
|
|
20429
|
+
}
|
|
20430
|
+
const current = await inventory(input);
|
|
20431
|
+
for (const release of current.report.releases) {
|
|
20432
|
+
input.signal?.throwIfAborted();
|
|
20433
|
+
if (release.protectedBy.length > 0 || !release.markedAt || now.getTime() - Date.parse(release.markedAt) < gracePeriodMs || result.marked.includes(release.releaseId))
|
|
20434
|
+
continue;
|
|
20435
|
+
const checked = await inventory(input);
|
|
20436
|
+
const candidate = checked.report.releases.find((value) => value.releaseId === release.releaseId);
|
|
20437
|
+
if (!candidate || candidate.protectedBy.length > 0 || !candidate.markedAt)
|
|
20438
|
+
continue;
|
|
20439
|
+
const keys = checked.objectKeysByRelease.get(release.releaseId) ?? [];
|
|
20440
|
+
for (const key of keys.toSorted((left, right) => {
|
|
20441
|
+
const leftManifest = left.endsWith("/update.json");
|
|
20442
|
+
const rightManifest = right.endsWith("/update.json");
|
|
20443
|
+
return Number(leftManifest) - Number(rightManifest);
|
|
20444
|
+
}))
|
|
20445
|
+
await remove(key);
|
|
20446
|
+
await remove(tombstoneKey(input.appId, release.releaseId));
|
|
20447
|
+
result.reclaimedBytes += candidate.bytes;
|
|
20448
|
+
result.swept.push(release.releaseId);
|
|
20449
|
+
}
|
|
20450
|
+
return result;
|
|
20451
|
+
};
|
|
20237
20452
|
return {
|
|
20453
|
+
inspectUpdateStorage: async (input) => (await inventory(input)).report,
|
|
20454
|
+
pruneUpdates,
|
|
20238
20455
|
publishUpdate: async (input) => {
|
|
20239
20456
|
input.signal?.throwIfAborted();
|
|
20240
20457
|
const manifest = parseMobileUpdateManifest(input.manifest);
|
|
20241
20458
|
verifyManifestSignature(manifest, options.publicKeys);
|
|
20459
|
+
await assertNotMarked(manifest.appId, manifest.releaseId);
|
|
20242
20460
|
const localRoot = path.resolve(input.releaseDirectory);
|
|
20243
20461
|
const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile23(path.join(localRoot, "update.json"), "utf8")));
|
|
20244
20462
|
if (JSON.stringify(localManifest) !== JSON.stringify(manifest))
|
|
@@ -20262,10 +20480,10 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20262
20480
|
cacheControl: "public, max-age=31536000, immutable",
|
|
20263
20481
|
contentType: "application/octet-stream",
|
|
20264
20482
|
maxBytes: file.bytes,
|
|
20265
|
-
metadata: {
|
|
20483
|
+
metadata: { releaseid: manifest.releaseId, sha256: file.sha256 },
|
|
20266
20484
|
signal: input.signal
|
|
20267
20485
|
});
|
|
20268
|
-
} else if (stored.size !== file.bytes || stored.metadata?.sha256 !== file.sha256 || stored.metadata?.releaseId !== manifest.releaseId)
|
|
20486
|
+
} else if (stored.size !== file.bytes || stored.metadata?.sha256 !== file.sha256 || (stored.metadata?.releaseid ?? stored.metadata?.releaseId) !== manifest.releaseId)
|
|
20269
20487
|
throw new MobileUpdateRegistryError("Stored mobile update file identity changed");
|
|
20270
20488
|
}
|
|
20271
20489
|
const bytes = json(manifest);
|
|
@@ -20273,7 +20491,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20273
20491
|
cacheControl: "public, max-age=31536000, immutable",
|
|
20274
20492
|
contentType: "application/json",
|
|
20275
20493
|
maxBytes: bytes.byteLength,
|
|
20276
|
-
metadata: {
|
|
20494
|
+
metadata: { releaseid: manifest.releaseId, sha256: digest(bytes) },
|
|
20277
20495
|
signal: input.signal
|
|
20278
20496
|
});
|
|
20279
20497
|
if (!await readManifest(manifest.appId, manifest.releaseId))
|
|
@@ -20303,6 +20521,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20303
20521
|
if (!existing)
|
|
20304
20522
|
throw new MobileUpdateRegistryError("Mobile update channel does not exist");
|
|
20305
20523
|
if (input.releaseId) {
|
|
20524
|
+
await assertNotMarked(input.appId, input.releaseId);
|
|
20306
20525
|
const release = await readManifest(input.appId, input.releaseId);
|
|
20307
20526
|
if (!release || release.manifest.channel !== input.channel)
|
|
20308
20527
|
throw new MobileUpdateRegistryError("Mobile rollback release was not published");
|
|
@@ -20346,7 +20565,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20346
20565
|
]);
|
|
20347
20566
|
if (!bytes || !head)
|
|
20348
20567
|
return null;
|
|
20349
|
-
if (bytes.byteLength !== file.bytes || head.size !== file.bytes || head.metadata?.sha256 !== file.sha256 || head.metadata?.releaseId !== release.manifest.releaseId || digest(bytes) !== file.sha256)
|
|
20568
|
+
if (bytes.byteLength !== file.bytes || head.size !== file.bytes || head.metadata?.sha256 !== file.sha256 || (head.metadata?.releaseid ?? head.metadata?.releaseId) !== release.manifest.releaseId || digest(bytes) !== file.sha256)
|
|
20350
20569
|
throw new MobileUpdateRegistryError("Stored mobile update file integrity failed");
|
|
20351
20570
|
return { bytes, file };
|
|
20352
20571
|
}
|
|
@@ -20635,6 +20854,9 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
|
|
|
20635
20854
|
var init_mobileUpdate = __esm(() => {
|
|
20636
20855
|
MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
20637
20856
|
MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
20857
|
+
DAY_MS = 24 * 60 * 60 * 1000;
|
|
20858
|
+
DEFAULT_MIN_AGE_MS = 30 * DAY_MS;
|
|
20859
|
+
DEFAULT_GRACE_PERIOD_MS = 7 * DAY_MS;
|
|
20638
20860
|
HASH = /^[a-f0-9]{64}$/;
|
|
20639
20861
|
RELEASE = /^amu_[a-f0-9]{64}$/;
|
|
20640
20862
|
APP_ID = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
@@ -20705,10 +20927,27 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
20705
20927
|
const registry4 = loaded.default ?? loaded.registry;
|
|
20706
20928
|
if (!isRegistry4(registry4))
|
|
20707
20929
|
throw new TypeError("Mobile update registry must implement publication, promotion, rollback, resolution, and file reads.");
|
|
20930
|
+
const metadata = serverMetadata(loaded.absoluteMobileUpdateServer);
|
|
20931
|
+
const verifier = loaded.verifyAbsoluteMobileUpdateServer;
|
|
20932
|
+
if (metadata.storage === "durable" && typeof verifier !== "function")
|
|
20933
|
+
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.");
|
|
20708
20934
|
return {
|
|
20709
|
-
metadata
|
|
20710
|
-
registry: registry4
|
|
20935
|
+
metadata,
|
|
20936
|
+
registry: registry4,
|
|
20937
|
+
...typeof verifier === "function" ? {
|
|
20938
|
+
verifyDurability: async () => {
|
|
20939
|
+
await verifier();
|
|
20940
|
+
}
|
|
20941
|
+
} : {}
|
|
20711
20942
|
};
|
|
20943
|
+
}, verifyDurableModule = async (module) => {
|
|
20944
|
+
if (module.metadata.storage !== "durable")
|
|
20945
|
+
throw new TypeError("Mobile production updates require durable object storage. Re-run `absolute mobile update provision --storage s3 --force` or configure a durable adapter.");
|
|
20946
|
+
try {
|
|
20947
|
+
await module.verifyDurability?.();
|
|
20948
|
+
} catch (error) {
|
|
20949
|
+
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 });
|
|
20950
|
+
}
|
|
20712
20951
|
}, expoSigningOptions = (config) => {
|
|
20713
20952
|
if (!config.updates?.expoCodeSigning)
|
|
20714
20953
|
return;
|
|
@@ -20740,8 +20979,8 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
20740
20979
|
if (!updates || !server?.autoMount)
|
|
20741
20980
|
return new Elysia4({ name: "absolutejs-mobile-updates-disabled" });
|
|
20742
20981
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, server.registryModule);
|
|
20743
|
-
if (options.production
|
|
20744
|
-
|
|
20982
|
+
if (options.production)
|
|
20983
|
+
await verifyDurableModule(module);
|
|
20745
20984
|
const manifest = new URL(updates.manifestUrl);
|
|
20746
20985
|
if (!manifest.pathname.endsWith("/update.json"))
|
|
20747
20986
|
throw new TypeError("Auto-mounted mobile update manifests must end in /update.json.");
|
|
@@ -20759,8 +20998,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
|
|
|
20759
20998
|
if (!config.updates)
|
|
20760
20999
|
return;
|
|
20761
21000
|
const module = await loadAbsoluteMobileUpdateServerModule(projectRoot, config.updateServer?.registryModule);
|
|
20762
|
-
|
|
20763
|
-
throw new TypeError("Mobile production updates require durable object storage; the configured registry is local-only.");
|
|
21001
|
+
await verifyDurableModule(module);
|
|
20764
21002
|
if (config.engine === "expo")
|
|
20765
21003
|
expoSigningOptions(config);
|
|
20766
21004
|
return module.metadata;
|
|
@@ -20787,7 +21025,8 @@ export default createMobileUpdateRegistry({
|
|
|
20787
21025
|
store
|
|
20788
21026
|
});
|
|
20789
21027
|
`;
|
|
20790
|
-
return `import {
|
|
21028
|
+
return `import { randomUUID } from 'node:crypto';
|
|
21029
|
+
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
20791
21030
|
import { awsS3BlobStore } from '@absolutejs/blob/aws-s3';
|
|
20792
21031
|
import { createMobileUpdateRegistry } from '@absolutejs/deploy/mobile-update';
|
|
20793
21032
|
|
|
@@ -20799,6 +21038,7 @@ const required = (name: string) => {
|
|
|
20799
21038
|
return value;
|
|
20800
21039
|
};
|
|
20801
21040
|
|
|
21041
|
+
const bucket = required('ABSOLUTE_MOBILE_UPDATE_S3_BUCKET');
|
|
20802
21042
|
const client = new S3Client({
|
|
20803
21043
|
region: process.env.ABSOLUTE_MOBILE_UPDATE_S3_REGION ?? 'auto',
|
|
20804
21044
|
forcePathStyle: process.env.ABSOLUTE_MOBILE_UPDATE_S3_FORCE_PATH_STYLE === '1',
|
|
@@ -20806,10 +21046,22 @@ const client = new S3Client({
|
|
|
20806
21046
|
? { endpoint: process.env.ABSOLUTE_MOBILE_UPDATE_S3_ENDPOINT }
|
|
20807
21047
|
: {})
|
|
20808
21048
|
});
|
|
20809
|
-
const store = awsS3BlobStore({
|
|
20810
|
-
|
|
20811
|
-
|
|
20812
|
-
}
|
|
21049
|
+
const store = awsS3BlobStore({ bucket, client });
|
|
21050
|
+
|
|
21051
|
+
export const verifyAbsoluteMobileUpdateServer = async () => {
|
|
21052
|
+
const key = \`absolutejs/mobile-updates/_health/\${randomUUID()}\`;
|
|
21053
|
+
const expected = randomUUID();
|
|
21054
|
+
let stored = false;
|
|
21055
|
+
try {
|
|
21056
|
+
await client.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: expected }));
|
|
21057
|
+
stored = true;
|
|
21058
|
+
const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
21059
|
+
if ((await response.Body?.transformToString()) !== expected)
|
|
21060
|
+
throw new Error('Durability probe read did not match its write.');
|
|
21061
|
+
} finally {
|
|
21062
|
+
if (stored) await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
21063
|
+
}
|
|
21064
|
+
};
|
|
20813
21065
|
|
|
20814
21066
|
export default createMobileUpdateRegistry({
|
|
20815
21067
|
publicKeys: ${publicKeysSource(options.publicKeys)},
|
|
@@ -38630,6 +38882,45 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
38630
38882
|
};
|
|
38631
38883
|
|
|
38632
38884
|
// src/mobile/updatePublisher.ts
|
|
38885
|
+
var lifecycleMethod = (publisher, name) => {
|
|
38886
|
+
const method = publisher[name];
|
|
38887
|
+
if (typeof method !== "function")
|
|
38888
|
+
throw new TypeError(`Mobile update registry does not support ${name}. Re-run \`absolute mobile update provision --force\` after upgrading @absolutejs/deploy.`);
|
|
38889
|
+
return method;
|
|
38890
|
+
};
|
|
38891
|
+
var validateStorageIdentity = (result, appId) => {
|
|
38892
|
+
if (!object5(result) || result.appId !== appId || !Array.isArray(result.releases) || ![
|
|
38893
|
+
result.channelCount,
|
|
38894
|
+
result.reclaimableBytes,
|
|
38895
|
+
result.releaseBytes,
|
|
38896
|
+
result.releaseCount,
|
|
38897
|
+
result.totalBytes,
|
|
38898
|
+
result.totalObjectCount,
|
|
38899
|
+
result.untrackedBytes
|
|
38900
|
+
].every((value) => Number.isSafeInteger(value) && value >= 0))
|
|
38901
|
+
throw new TypeError("Mobile update registry returned an invalid storage report.");
|
|
38902
|
+
return result;
|
|
38903
|
+
};
|
|
38904
|
+
var inspectAbsoluteMobileUpdateStorage = async (options) => validateStorageIdentity(await lifecycleMethod(options.publisher, "inspectUpdateStorage")({
|
|
38905
|
+
appId: options.appId,
|
|
38906
|
+
...options.minAgeMs === undefined ? {} : { minAgeMs: options.minAgeMs },
|
|
38907
|
+
...options.retainRecent === undefined ? {} : { retainRecent: options.retainRecent },
|
|
38908
|
+
...options.signal ? { signal: options.signal } : {}
|
|
38909
|
+
}), options.appId);
|
|
38910
|
+
var pruneAbsoluteMobileUpdates = async (options) => {
|
|
38911
|
+
const result = await lifecycleMethod(options.publisher, "pruneUpdates")({
|
|
38912
|
+
appId: options.appId,
|
|
38913
|
+
...options.apply === undefined ? {} : { apply: options.apply },
|
|
38914
|
+
...options.gracePeriodMs === undefined ? {} : { gracePeriodMs: options.gracePeriodMs },
|
|
38915
|
+
...options.minAgeMs === undefined ? {} : { minAgeMs: options.minAgeMs },
|
|
38916
|
+
...options.retainRecent === undefined ? {} : { retainRecent: options.retainRecent },
|
|
38917
|
+
...options.signal ? { signal: options.signal } : {}
|
|
38918
|
+
});
|
|
38919
|
+
validateStorageIdentity(result, options.appId);
|
|
38920
|
+
if (!Array.isArray(result.marked) || !Array.isArray(result.restored) || !Array.isArray(result.swept) || typeof result.dryRun !== "boolean" || !Number.isSafeInteger(result.reclaimedBytes) || result.reclaimedBytes < 0)
|
|
38921
|
+
throw new TypeError("Mobile update registry returned an invalid collection report.");
|
|
38922
|
+
return result;
|
|
38923
|
+
};
|
|
38633
38924
|
var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38634
38925
|
var isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
38635
38926
|
var projectPath2 = (projectRoot, requested, label) => {
|
|
@@ -38849,6 +39140,7 @@ export {
|
|
|
38849
39140
|
inspectAbsoluteAndroidInstalledApp,
|
|
38850
39141
|
inspectAbsoluteMobileRouteMetadata,
|
|
38851
39142
|
inspectAbsoluteMobileUpdateServer,
|
|
39143
|
+
inspectAbsoluteMobileUpdateStorage,
|
|
38852
39144
|
inspectAbsoluteRemoteMac,
|
|
38853
39145
|
inspectAbsoluteRemoteMacLanHost,
|
|
38854
39146
|
inspectAbsoluteRemoteMacWorkspace,
|
|
@@ -38906,6 +39198,7 @@ export {
|
|
|
38906
39198
|
projectUsesAbsoluteAuth,
|
|
38907
39199
|
projectUsesAbsoluteSync,
|
|
38908
39200
|
promoteAbsoluteMobileUpdate,
|
|
39201
|
+
pruneAbsoluteMobileUpdates,
|
|
38909
39202
|
publishAbsoluteAndroidRelease,
|
|
38910
39203
|
publishAbsoluteIosRelease,
|
|
38911
39204
|
publishAbsoluteMobileUpdate,
|
|
@@ -38954,5 +39247,5 @@ export {
|
|
|
38954
39247
|
writeAbsoluteMobileUpdateRegistry
|
|
38955
39248
|
};
|
|
38956
39249
|
|
|
38957
|
-
//# debugId=
|
|
39250
|
+
//# debugId=FB53026A710DD13464756E2164756E21
|
|
38958
39251
|
//# sourceMappingURL=index.js.map
|