@absolutejs/absolute 0.20.0-beta.106 → 0.20.0-beta.108

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.
@@ -20032,7 +20032,7 @@ __export(exports_mobileUpdate, {
20032
20032
  parseMobileUpdateManifest: () => parseMobileUpdateManifest
20033
20033
  });
20034
20034
  import {
20035
- createHash as createHash18,
20035
+ createHash as createHash19,
20036
20036
  createHmac,
20037
20037
  createPrivateKey,
20038
20038
  createPublicKey as createPublicKey2,
@@ -20042,7 +20042,7 @@ import {
20042
20042
  verify as verify2,
20043
20043
  X509Certificate as X509Certificate3
20044
20044
  } from "crypto";
20045
- import { readFile as readFile27, stat as stat7 } from "fs/promises";
20045
+ import { readFile as readFile28, stat as stat7 } from "fs/promises";
20046
20046
  import path from "path";
20047
20047
  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", HEALTH_TOKEN_VERSION = 1, HEALTH_KINDS, FAILURE_HEALTH_KINDS, MobileUpdateRegistryError, object6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), text2 = (value, field2) => {
20048
20048
  if (typeof value !== "string" || value.length === 0)
@@ -20192,7 +20192,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20192
20192
  if (!prefix || prefix.split("/").some((segment) => segment === "." || segment === ".."))
20193
20193
  throw new MobileUpdateRegistryError("Mobile update prefix is invalid");
20194
20194
  return prefix;
20195
- }, appHash = (appId) => createHash18("sha256").update(appId).digest("hex"), digest = (bytes) => createHash18("sha256").update(bytes).digest("hex"), json = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
20195
+ }, appHash = (appId) => createHash19("sha256").update(appId).digest("hex"), digest = (bytes) => createHash19("sha256").update(bytes).digest("hex"), json = (value) => new TextEncoder().encode(`${JSON.stringify(value, null, 2)}
20196
20196
  `), decode2 = (value) => JSON.parse(new TextDecoder().decode(value)), fileDigest = async (file) => {
20197
20197
  const hasher = new Bun.CryptoHasher("sha256");
20198
20198
  for await (const chunk of file.stream())
@@ -20203,7 +20203,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20203
20203
  return false;
20204
20204
  if (input.rollout === 1)
20205
20205
  return true;
20206
- const value = createHash18("sha256").update(`${input.appId}\x00${input.channel}\x00${input.releaseId}\x00${input.installationId}`).digest().readUInt32BE(0);
20206
+ const value = createHash19("sha256").update(`${input.appId}\x00${input.channel}\x00${input.releaseId}\x00${input.installationId}`).digest().readUInt32BE(0);
20207
20207
  return value / 4294967296 < input.rollout;
20208
20208
  }, base64Url = (value) => Buffer.from(value).toString("base64url"), healthPromotionId = (channel) => channel.promotionId ?? digest(new TextEncoder().encode(`${channel.appId}\x00${channel.channel}\x00${channel.releaseId ?? "embedded"}\x00${channel.promotedAt}`)), finiteMetric = (value, field2) => {
20209
20209
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
@@ -20243,7 +20243,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20243
20243
  const releaseRoot = (manifest) => `${root(manifest.appId)}/releases/${manifest.releaseId}`;
20244
20244
  const manifestKey = (manifest) => `${releaseRoot(manifest)}/update.json`;
20245
20245
  const fileKey = (manifest, file) => `${releaseRoot(manifest)}/files/${file.path}`;
20246
- const contentBlobKey = (appId, sha2565) => `${root(appId)}/blobs/${sha2565}`;
20246
+ const contentBlobKey = (appId, sha2566) => `${root(appId)}/blobs/${sha2566}`;
20247
20247
  const tombstoneKey = (appId, releaseId) => `${root(appId)}/gc/${releaseId}.json`;
20248
20248
  const healthRoot = (appId, promotionId, releaseId) => `${root(appId)}/health/${promotionId}/${releaseId}`;
20249
20249
  const pauseKey = (appId, promotionId, releaseId) => `${healthRoot(appId, promotionId, releaseId)}/paused.json`;
@@ -20864,10 +20864,10 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20864
20864
  const objectBytesByDigest = new Map;
20865
20865
  for (const blobObject of objects) {
20866
20866
  if (blobObject.key.startsWith(blobPrefix)) {
20867
- const sha2565 = blobObject.key.slice(blobPrefix.length);
20868
- if (HASH.test(sha2565)) {
20869
- objectKeysByDigest.set(sha2565, blobObject.key);
20870
- objectBytesByDigest.set(sha2565, blobObject.size);
20867
+ const sha2566 = blobObject.key.slice(blobPrefix.length);
20868
+ if (HASH.test(sha2566)) {
20869
+ objectKeysByDigest.set(sha2566, blobObject.key);
20870
+ objectBytesByDigest.set(sha2566, blobObject.size);
20871
20871
  }
20872
20872
  }
20873
20873
  if (!blobObject.key.startsWith(releasePrefix))
@@ -20950,7 +20950,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
20950
20950
  }).toSorted((left, right) => right.createdAt.localeCompare(left.createdAt));
20951
20951
  const protectedReleaseIds = new Set(releases.filter((release) => release.protectedBy.length > 0).map((release) => release.releaseId));
20952
20952
  const protectedDigests = new Set(manifests.filter((manifest) => protectedReleaseIds.has(manifest.releaseId)).flatMap((manifest) => manifest.files.map((file) => file.sha256)));
20953
- const reclaimableContentBytes = [...objectBytesByDigest].filter(([sha2565]) => !protectedDigests.has(sha2565)).reduce((total, [, bytes]) => total + bytes, 0);
20953
+ const reclaimableContentBytes = [...objectBytesByDigest].filter(([sha2566]) => !protectedDigests.has(sha2566)).reduce((total, [, bytes]) => total + bytes, 0);
20954
20954
  const contentBlobBytes = [...objectBytesByDigest.values()].reduce((total, bytes) => total + bytes, 0);
20955
20955
  const releaseBytes = releases.reduce((total, release) => total + release.bytes, 0);
20956
20956
  const totalBytes = objects.reduce((total, object22) => total + object22.size, 0);
@@ -21047,13 +21047,13 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21047
21047
  for (const file of manifest?.manifest.files ?? [])
21048
21048
  referencedDigests.add(file.sha256);
21049
21049
  }
21050
- for (const [sha2565, key] of afterReleaseSweep.objectKeysByDigest) {
21051
- if (referencedDigests.has(sha2565))
21050
+ for (const [sha2566, key] of afterReleaseSweep.objectKeysByDigest) {
21051
+ if (referencedDigests.has(sha2566))
21052
21052
  continue;
21053
21053
  const head = await options.store.head(key);
21054
21054
  await remove(key);
21055
21055
  result.reclaimedBytes += head?.size ?? 0;
21056
- result.sweptContentBlobs.push(sha2565);
21056
+ result.sweptContentBlobs.push(sha2566);
21057
21057
  }
21058
21058
  return result;
21059
21059
  };
@@ -21075,7 +21075,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21075
21075
  verifyManifestSignature(manifest, options.publicKeys);
21076
21076
  await assertNotMarked(manifest.appId, manifest.releaseId);
21077
21077
  const localRoot = path.resolve(input.releaseDirectory);
21078
- const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile27(path.join(localRoot, "update.json"), "utf8")));
21078
+ const localManifest = parseMobileUpdateManifest(JSON.parse(await readFile28(path.join(localRoot, "update.json"), "utf8")));
21079
21079
  if (JSON.stringify(localManifest) !== JSON.stringify(manifest))
21080
21080
  throw new MobileUpdateRegistryError("Local mobile update manifest changed");
21081
21081
  const existing = await readManifest(manifest.appId, manifest.releaseId);
@@ -21208,7 +21208,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21208
21208
  }
21209
21209
  };
21210
21210
  }, expoUpdateId = (releaseId) => {
21211
- const hash = RELEASE.test(releaseId) ? releaseId.slice("amu_".length, "amu_".length + 32) : createHash18("sha256").update(releaseId).digest("hex").slice(0, 32);
21211
+ const hash = RELEASE.test(releaseId) ? releaseId.slice("amu_".length, "amu_".length + 32) : createHash19("sha256").update(releaseId).digest("hex").slice(0, 32);
21212
21212
  return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20)}`;
21213
21213
  }, expoContentType = (extension2, launch2) => {
21214
21214
  if (launch2)
@@ -21361,8 +21361,8 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21361
21361
  });
21362
21362
  }
21363
21363
  const pathname = new URL(request.url).pathname.replace(/^\/+/, "");
21364
- const relative20 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21365
- if (request.method === "POST" && relative20 === "health") {
21364
+ const relative21 = pathname.startsWith(`${route}/`) ? pathname.slice(route.length + 1) : "";
21365
+ if (request.method === "POST" && relative21 === "health") {
21366
21366
  if (!options.registry.recordUpdateHealth)
21367
21367
  return new Response(null, { status: 404 });
21368
21368
  const appId = request.headers.get("x-absolute-mobile-app");
@@ -21405,7 +21405,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21405
21405
  }
21406
21406
  if (request.method !== "GET")
21407
21407
  return new Response(null, { status: 405 });
21408
- if (relative20 === "update.json") {
21408
+ if (relative21 === "update.json") {
21409
21409
  const expoProtocolVersion = request.headers.get("expo-protocol-version");
21410
21410
  const expoProtocol = expoProtocolVersion !== null;
21411
21411
  if (expoProtocol && expoProtocolVersion !== "1")
@@ -21523,7 +21523,7 @@ var MOBILE_UPDATE_REGISTRY_FORMAT = 1, DEFAULT_PREFIX = "absolutejs/mobile-updat
21523
21523
  }
21524
21524
  });
21525
21525
  }
21526
- const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative20);
21526
+ const match = /^(amu_[a-f0-9]{64})\/files\/(.+)$/.exec(relative21);
21527
21527
  if (!match?.[1] || !match[2])
21528
21528
  return new Response(null, { status: 404 });
21529
21529
  const file = await options.registry.readUpdateFile({
@@ -21616,15 +21616,15 @@ import {
21616
21616
  createPublicKey as createPublicKey3,
21617
21617
  X509Certificate as X509Certificate4
21618
21618
  } from "crypto";
21619
- import { access as access19, mkdir as mkdir21 } from "fs/promises";
21620
- import { dirname as dirname22, isAbsolute as isAbsolute11, relative as relative20, resolve as resolve22, sep as sep13 } from "path";
21619
+ import { access as access20, mkdir as mkdir22 } from "fs/promises";
21620
+ import { dirname as dirname22, isAbsolute as isAbsolute12, relative as relative21, resolve as resolve23, sep as sep14 } from "path";
21621
21621
  import { pathToFileURL as pathToFileURL5 } from "url";
21622
21622
  import { Elysia as Elysia4 } from "elysia";
21623
21623
  var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE = "mobile.update.ts", object7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectPath3 = (projectRoot, requested) => {
21624
- const root = resolve22(projectRoot);
21625
- const path2 = resolve22(root, requested);
21626
- const projectRelative = relative20(root, path2);
21627
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep13}`) || isAbsolute11(projectRelative))
21624
+ const root = resolve23(projectRoot);
21625
+ const path2 = resolve23(root, requested);
21626
+ const projectRelative = relative21(root, path2);
21627
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep14}`) || isAbsolute12(projectRelative))
21628
21628
  throw new TypeError("mobile.updates.server.registry must remain inside the project.");
21629
21629
  return path2;
21630
21630
  }, isRegistry4 = (value) => object7(value) && [
@@ -21647,7 +21647,7 @@ var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1, DEFAULT_MOBILE_UPDATE_REGISTRY_MOD
21647
21647
  return metadata;
21648
21648
  }, loadAbsoluteMobileUpdateServerModule = async (projectRoot, requestedModulePath = DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE) => {
21649
21649
  const modulePath = projectPath3(projectRoot, requestedModulePath);
21650
- await access19(modulePath).catch(() => {
21650
+ await access20(modulePath).catch(() => {
21651
21651
  throw new TypeError(`Mobile update server registry does not exist: ${modulePath}. Run \`absolute mobile update provision\`.`);
21652
21652
  });
21653
21653
  const loaded = await import(pathToFileURL5(modulePath).href);
@@ -21836,13 +21836,13 @@ ${generated}`);
21836
21836
  }, writeAbsoluteMobileUpdateRegistry = async (options) => {
21837
21837
  const path2 = projectPath3(options.projectRoot, options.modulePath ?? DEFAULT_MOBILE_UPDATE_REGISTRY_MODULE);
21838
21838
  if (!options.force) {
21839
- await access19(path2).then(() => {
21839
+ await access20(path2).then(() => {
21840
21840
  throw new TypeError(`Mobile update registry already exists: ${path2}. Pass --force to replace it.`);
21841
21841
  }, () => {
21842
21842
  return;
21843
21843
  });
21844
21844
  }
21845
- await mkdir21(dirname22(path2), { recursive: true });
21845
+ await mkdir22(dirname22(path2), { recursive: true });
21846
21846
  await Bun.write(path2, renderAbsoluteMobileUpdateRegistry({
21847
21847
  ...options.health ? { health: options.health } : {},
21848
21848
  ...options.rollout ? { rollout: options.rollout } : {},
@@ -35247,9 +35247,412 @@ init_pageProtocol();
35247
35247
  init_producerContext();
35248
35248
  init_releaseArtifact();
35249
35249
 
35250
+ // src/mobile/releaseCertification.ts
35251
+ import { createHash as createHash16 } from "crypto";
35252
+ import {
35253
+ access as access17,
35254
+ mkdir as mkdir20,
35255
+ mkdtemp as mkdtemp10,
35256
+ readFile as readFile26,
35257
+ rename as rename18,
35258
+ rm as rm15,
35259
+ writeFile as writeFile22
35260
+ } from "fs/promises";
35261
+ import { basename as basename8, isAbsolute as isAbsolute9, join as join30, relative as relative16, resolve as resolve18, sep as sep11 } from "path";
35262
+ var ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT = 1;
35263
+ var CERTIFICATION_PREFIX = "amobile_cert_";
35264
+ var SHA256 = /^[a-f0-9]{64}$/u;
35265
+ var STRENGTH_RANK = {
35266
+ device: 1,
35267
+ installed: 0,
35268
+ simulator: 0,
35269
+ store: 2
35270
+ };
35271
+ var isRequirement = (value) => value === "installed" || value === "simulator" || value === "device" || value === "store";
35272
+ var isStrength = (value) => value === "installed" || value === "simulator" || value === "device" || value === "store";
35273
+ var isArtifactExactness = (value) => value === "archive-equivalent" || value === "source-equivalent" || value === "store-delivered";
35274
+ var isDistribution = (value) => value === "apple-processed" || value === "registered-device" || value === "simulator-release";
35275
+ var isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35276
+ var inside4 = (root, candidate) => {
35277
+ const path = relative16(resolve18(root), resolve18(candidate));
35278
+ return path === "" || !isAbsolute9(path) && path !== ".." && !path.startsWith(`..${sep11}`);
35279
+ };
35280
+ var exists9 = async (path) => {
35281
+ try {
35282
+ await access17(path);
35283
+ return true;
35284
+ } catch {
35285
+ return false;
35286
+ }
35287
+ };
35288
+ var sha2564 = (bytes) => createHash16("sha256").update(bytes).digest("hex");
35289
+ var requireString2 = (value, name) => {
35290
+ if (typeof value !== "string" || !value)
35291
+ throw new TypeError(`Mobile certification ${name} is invalid.`);
35292
+ return value;
35293
+ };
35294
+ var requirePositiveInteger = (value, name) => {
35295
+ if (!Number.isSafeInteger(value) || Number(value) < 1)
35296
+ throw new TypeError(`Mobile certification ${name} is invalid.`);
35297
+ return Number(value);
35298
+ };
35299
+ var resolveEvidencePath = async (projectRoot, requested) => {
35300
+ const selected = resolve18(projectRoot, requested);
35301
+ if (!inside4(projectRoot, selected))
35302
+ throw new TypeError("Mobile certification evidence must remain inside the project.");
35303
+ if (basename8(selected) === "report.json")
35304
+ return selected;
35305
+ const report = join30(selected, "report.json");
35306
+ if (!await exists9(report))
35307
+ throw new TypeError(`Mobile certification evidence does not contain report.json: ${requested}`);
35308
+ return report;
35309
+ };
35310
+ var releaseIdentity = (release) => {
35311
+ const { metadata } = release;
35312
+ if (!metadata.signed)
35313
+ throw new TypeError("Mobile release certification requires a signed native release.");
35314
+ const common = {
35315
+ appBuild: metadata.appBuild,
35316
+ appId: metadata.appId,
35317
+ artifactBytes: metadata.bytes,
35318
+ artifactSha256: metadata.sha256,
35319
+ engine: metadata.engine,
35320
+ platform: metadata.platform,
35321
+ releaseId: metadata.releaseId,
35322
+ runtime: metadata.runtime,
35323
+ signed: true
35324
+ };
35325
+ if (metadata.platform === "android")
35326
+ return {
35327
+ ...common,
35328
+ ...metadata.versionCode === undefined ? {} : { versionCode: metadata.versionCode }
35329
+ };
35330
+ return {
35331
+ ...common,
35332
+ ...metadata.buildNumber === undefined ? {} : { buildNumber: metadata.buildNumber },
35333
+ marketingVersion: metadata.marketingVersion
35334
+ };
35335
+ };
35336
+ var requireCheck = (report, id, accepted = ["PASS"]) => {
35337
+ const checks = report.automatedChecks;
35338
+ if (!Array.isArray(checks))
35339
+ throw new TypeError("Mobile certification evidence has invalid automated checks.");
35340
+ const check = checks.find((candidate) => isRecord14(candidate) && candidate.id === id);
35341
+ if (!isRecord14(check) || !accepted.includes(String(check.result)))
35342
+ throw new TypeError(`Mobile certification evidence does not satisfy ${id}.`);
35343
+ };
35344
+ var validateCommonReport = (report, release) => {
35345
+ if (report.reportVersion !== 1 || report.platform !== release.platform || !isRecord14(report.metadata) || report.metadata.provider !== release.engine || !isRecord14(report.run) || report.run.status !== "pass" || report.run.appId !== release.appId)
35346
+ throw new TypeError("Mobile certification evidence does not match the release platform, provider, application, or successful-run contract.");
35347
+ requireCheck(report, "AUTO-DEV-01");
35348
+ return {
35349
+ generatedAt: requireString2(report.generatedAt, "evidence generatedAt"),
35350
+ run: report.run
35351
+ };
35352
+ };
35353
+ var validateReleaseFields = (value, release) => {
35354
+ if (value.releaseId !== release.releaseId || value.artifactSha256 !== release.artifactSha256 || value.artifactBytes !== release.artifactBytes || value.engine !== release.engine || value.signed !== true)
35355
+ throw new TypeError("Mobile certification evidence does not match the immutable release identity.");
35356
+ };
35357
+ var validateAndroidEvidence = (report, run, release, reportSha256, generatedAt) => {
35358
+ if (!isRecord14(run.release))
35359
+ throw new TypeError("Android certification evidence has no installed release result.");
35360
+ validateReleaseFields(run.release, release);
35361
+ if (run.release.embeddedOffline !== true)
35362
+ throw new TypeError("Android certification evidence did not prove embedded offline relaunch.");
35363
+ requireCheck(report, "AUTO-RELEASE-01");
35364
+ requireCheck(report, "AUTO-RELEASE-OFFLINE-01");
35365
+ return {
35366
+ generatedAt,
35367
+ networkUnavailable: "proven",
35368
+ remote: false,
35369
+ reportSha256,
35370
+ strength: "installed"
35371
+ };
35372
+ };
35373
+ var validateIosEvidence = (report, run, release, reportSha256, generatedAt) => {
35374
+ if (!isRecord14(run.iosRelease))
35375
+ throw new TypeError("iOS certification evidence has no installed release result.");
35376
+ const ios = run.iosRelease;
35377
+ validateReleaseFields(ios, release);
35378
+ if (ios.embeddedLocal !== true)
35379
+ throw new TypeError("iOS certification evidence did not prove embedded local rendering.");
35380
+ requireCheck(report, "AUTO-IOS-RELEASE-01");
35381
+ let strength;
35382
+ if (ios.distribution === "simulator-release" && ios.artifactExactness === "source-equivalent" && run.targetKind === "simulator" && ios.networkUnavailable === "not-proven")
35383
+ strength = "simulator";
35384
+ else if (ios.distribution === "registered-device" && ios.artifactExactness === "archive-equivalent" && run.targetKind === "device" && ios.networkUnavailable === "user-confirmed")
35385
+ strength = "device";
35386
+ else if (ios.distribution === "apple-processed" && ios.artifactExactness === "store-delivered" && run.targetKind === "device" && ios.networkUnavailable === "user-confirmed")
35387
+ strength = "store";
35388
+ else
35389
+ throw new TypeError("iOS certification evidence contains an invalid target, distribution, exactness, or offline-strength combination.");
35390
+ if (strength === "simulator")
35391
+ requireCheck(report, "AUTO-IOS-RELEASE-OFFLINE-01", ["SKIPPED"]);
35392
+ else
35393
+ requireCheck(report, "AUTO-IOS-RELEASE-OFFLINE-01");
35394
+ return {
35395
+ artifactExactness: ios.artifactExactness,
35396
+ distribution: ios.distribution,
35397
+ generatedAt,
35398
+ networkUnavailable: ios.networkUnavailable === "user-confirmed" ? "proven" : "not-proven",
35399
+ remote: ios.remote === true,
35400
+ reportSha256,
35401
+ strength
35402
+ };
35403
+ };
35404
+ var validateEvidence = async (projectRoot, requested, release) => {
35405
+ const path = await resolveEvidencePath(projectRoot, requested);
35406
+ const bytes = await readFile26(path);
35407
+ let value;
35408
+ try {
35409
+ value = JSON.parse(bytes.toString("utf8"));
35410
+ } catch {
35411
+ throw new TypeError("Mobile certification evidence report is not valid JSON.");
35412
+ }
35413
+ if (!isRecord14(value))
35414
+ throw new TypeError("Mobile certification evidence report must contain an object.");
35415
+ const { generatedAt, run } = validateCommonReport(value, release);
35416
+ const digest = sha2564(bytes);
35417
+ return release.platform === "android" ? validateAndroidEvidence(value, run, release, digest, generatedAt) : validateIosEvidence(value, run, release, digest, generatedAt);
35418
+ };
35419
+ var defaultRequirement = (platform4) => platform4 === "android" ? "installed" : "simulator";
35420
+ var validateRequirement = (platform4, requirement) => {
35421
+ if (platform4 === "android" && requirement !== "installed")
35422
+ throw new TypeError("Android certification currently uses its fixed installed/offline requirement; --require simulator, device, and store are iOS policies.");
35423
+ if (platform4 === "ios" && requirement === "installed")
35424
+ throw new TypeError("iOS certification requires simulator, device, or store evidence.");
35425
+ };
35426
+ var satisfiesRequirement = (strength, requirement) => {
35427
+ if (requirement === "installed")
35428
+ return strength === "installed";
35429
+ if (strength === "installed")
35430
+ return false;
35431
+ return STRENGTH_RANK[strength] >= STRENGTH_RANK[requirement];
35432
+ };
35433
+ var validateCertifiedEvidence = (platform4, evidence) => {
35434
+ if (platform4 === "android") {
35435
+ if (evidence.strength !== "installed" || evidence.networkUnavailable !== "proven" || evidence.artifactExactness !== undefined || evidence.distribution !== undefined || evidence.remote)
35436
+ throw new TypeError("Android release certification contains invalid evidence strength.");
35437
+ return;
35438
+ }
35439
+ const simulator = evidence.strength === "simulator" && evidence.artifactExactness === "source-equivalent" && evidence.distribution === "simulator-release" && evidence.networkUnavailable === "not-proven";
35440
+ const device = evidence.strength === "device" && evidence.artifactExactness === "archive-equivalent" && evidence.distribution === "registered-device" && evidence.networkUnavailable === "proven";
35441
+ const store = evidence.strength === "store" && evidence.artifactExactness === "store-delivered" && evidence.distribution === "apple-processed" && evidence.networkUnavailable === "proven";
35442
+ if (!simulator && !device && !store)
35443
+ throw new TypeError("iOS release certification contains invalid evidence strength.");
35444
+ };
35445
+ var canonicalJsonValue = (value) => {
35446
+ if (Array.isArray(value))
35447
+ return value.map(canonicalJsonValue);
35448
+ if (!isRecord14(value))
35449
+ return value;
35450
+ const sorted = {};
35451
+ for (const key of Object.keys(value).sort())
35452
+ sorted[key] = canonicalJsonValue(value[key]);
35453
+ return sorted;
35454
+ };
35455
+ var certificationBody = (certification) => JSON.stringify(canonicalJsonValue(certification));
35456
+ var createAbsoluteMobileReleaseCertification = async (options) => {
35457
+ if (options.evidencePaths.length === 0)
35458
+ throw new TypeError("Mobile release certification requires at least one --evidence report.");
35459
+ const release = releaseIdentity(options.release);
35460
+ const requirement = options.requirement ?? defaultRequirement(release.platform);
35461
+ validateRequirement(release.platform, requirement);
35462
+ const evidence = await Promise.all(options.evidencePaths.map((path) => validateEvidence(options.projectRoot, path, release)));
35463
+ evidence.sort((left, right) => left.reportSha256.localeCompare(right.reportSha256));
35464
+ const [firstEvidence] = evidence;
35465
+ if (!firstEvidence)
35466
+ throw new TypeError("Mobile release certification requires valid evidence.");
35467
+ const strength = evidence.reduce((strongest, candidate) => STRENGTH_RANK[candidate.strength] > STRENGTH_RANK[strongest] ? candidate.strength : strongest, firstEvidence.strength);
35468
+ if (!satisfiesRequirement(strength, requirement))
35469
+ throw new TypeError(`Mobile release evidence strength ${strength} does not satisfy required ${requirement} certification.`);
35470
+ const body = {
35471
+ evidence,
35472
+ format: ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT,
35473
+ generatedAt: options.generatedAt ?? evidence.reduce((latest, candidate) => candidate.generatedAt > latest ? candidate.generatedAt : latest, firstEvidence.generatedAt),
35474
+ release,
35475
+ requirement,
35476
+ status: "certified",
35477
+ strength
35478
+ };
35479
+ return {
35480
+ certificationId: `${CERTIFICATION_PREFIX}${sha2564(certificationBody(body))}`,
35481
+ ...body
35482
+ };
35483
+ };
35484
+ var parseCertification = (value) => {
35485
+ if (!isRecord14(value) || value.format !== ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT || value.status !== "certified" || !isRecord14(value.release) || !Array.isArray(value.evidence))
35486
+ throw new TypeError("Mobile release certification contract is invalid.");
35487
+ const { release } = value;
35488
+ const { platform: platform4 } = release;
35489
+ const { engine } = release;
35490
+ if (platform4 !== "android" && platform4 !== "ios" || engine !== "capacitor" && engine !== "expo" || release.signed !== true)
35491
+ throw new TypeError("Mobile release certification identity is invalid.");
35492
+ const { requirement } = value;
35493
+ const { strength } = value;
35494
+ if (!isRequirement(requirement) || !isStrength(strength))
35495
+ throw new TypeError("Mobile release certification policy is invalid.");
35496
+ validateRequirement(platform4, requirement);
35497
+ if (!satisfiesRequirement(strength, requirement))
35498
+ throw new TypeError("Mobile release certification does not satisfy its declared policy.");
35499
+ const artifactBytes = requirePositiveInteger(release.artifactBytes, "release artifactBytes");
35500
+ const artifactSha256 = requireString2(release.artifactSha256, "release artifactSha256");
35501
+ if (!SHA256.test(artifactSha256))
35502
+ throw new TypeError("Mobile release certification artifact digest is invalid.");
35503
+ const parsedRelease = {
35504
+ appBuild: requireString2(release.appBuild, "release appBuild"),
35505
+ appId: requireString2(release.appId, "release appId"),
35506
+ artifactBytes,
35507
+ artifactSha256,
35508
+ engine,
35509
+ platform: platform4,
35510
+ releaseId: requireString2(release.releaseId, "release releaseId"),
35511
+ runtime: requireString2(release.runtime, "release runtime"),
35512
+ signed: true,
35513
+ ...release.buildNumber === undefined ? {} : {
35514
+ buildNumber: requirePositiveInteger(release.buildNumber, "release buildNumber")
35515
+ },
35516
+ ...release.marketingVersion === undefined ? {} : {
35517
+ marketingVersion: requireString2(release.marketingVersion, "release marketingVersion")
35518
+ },
35519
+ ...release.versionCode === undefined ? {} : {
35520
+ versionCode: requirePositiveInteger(release.versionCode, "release versionCode")
35521
+ }
35522
+ };
35523
+ const evidence = value.evidence.map((candidate) => {
35524
+ if (!isRecord14(candidate) || !SHA256.test(String(candidate.reportSha256)) || !isStrength(candidate.strength) || candidate.networkUnavailable !== "not-proven" && candidate.networkUnavailable !== "proven" || typeof candidate.remote !== "boolean" || typeof candidate.generatedAt !== "string" || candidate.artifactExactness !== undefined && !isArtifactExactness(candidate.artifactExactness) || candidate.distribution !== undefined && !isDistribution(candidate.distribution))
35525
+ throw new TypeError("Mobile release certification evidence is invalid.");
35526
+ const parsedEvidence = {
35527
+ generatedAt: candidate.generatedAt,
35528
+ networkUnavailable: candidate.networkUnavailable,
35529
+ remote: candidate.remote,
35530
+ reportSha256: String(candidate.reportSha256),
35531
+ strength: candidate.strength,
35532
+ ...candidate.artifactExactness === undefined ? {} : {
35533
+ artifactExactness: candidate.artifactExactness
35534
+ },
35535
+ ...candidate.distribution === undefined ? {} : { distribution: candidate.distribution }
35536
+ };
35537
+ validateCertifiedEvidence(platform4, parsedEvidence);
35538
+ return parsedEvidence;
35539
+ });
35540
+ const strongestEvidence = evidence.reduce((strongest, candidate) => STRENGTH_RANK[candidate.strength] > STRENGTH_RANK[strongest] ? candidate.strength : strongest, evidence[0]?.strength ?? "installed");
35541
+ if (evidence.length === 0 || strongestEvidence !== strength)
35542
+ throw new TypeError("Mobile release certification evidence does not support its declared strength.");
35543
+ const parsed = {
35544
+ certificationId: requireString2(value.certificationId, "certificationId"),
35545
+ evidence,
35546
+ format: ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT,
35547
+ generatedAt: requireString2(value.generatedAt, "generatedAt"),
35548
+ release: parsedRelease,
35549
+ requirement,
35550
+ status: "certified",
35551
+ strength
35552
+ };
35553
+ const { certificationId, ...body } = parsed;
35554
+ if (certificationId !== `${CERTIFICATION_PREFIX}${sha2564(certificationBody(body))}`)
35555
+ throw new TypeError("Mobile release certification content digest is invalid.");
35556
+ return parsed;
35557
+ };
35558
+ var readAbsoluteMobileReleaseCertification = async (projectRoot, requested) => {
35559
+ const selected = resolve18(projectRoot, requested);
35560
+ if (!inside4(projectRoot, selected))
35561
+ throw new TypeError("Mobile release certification must remain inside the project.");
35562
+ const path = basename8(selected) === "certification.json" ? selected : join30(selected, "certification.json");
35563
+ const value = JSON.parse(await readFile26(path, "utf8"));
35564
+ return { certification: parseCertification(value), path };
35565
+ };
35566
+ var renderAbsoluteMobileReleaseCertification = (certification) => {
35567
+ const { release } = certification;
35568
+ const version = release.platform === "ios" ? `${release.marketingVersion ?? "unknown"} (${release.buildNumber ?? "unallocated"})` : String(release.versionCode ?? "unallocated");
35569
+ const evidence = certification.evidence.map((item) => `| ${item.strength} | ${item.networkUnavailable} | ${item.remote ? "paired Mac" : "local host"} | ${item.reportSha256} |`).join(`
35570
+ `);
35571
+ return `# AbsoluteJS mobile release certification
35572
+
35573
+ - Status: CERTIFIED
35574
+ - Certification: ${certification.certificationId}
35575
+ - Generated: ${certification.generatedAt}
35576
+ - Platform: ${release.platform}
35577
+ - Engine: ${release.engine}
35578
+ - Requirement: ${certification.requirement}
35579
+ - Evidence strength: ${certification.strength}
35580
+ - Release: ${release.releaseId}
35581
+ - Artifact SHA-256: ${release.artifactSha256}
35582
+ - Runtime fingerprint: ${release.runtime}
35583
+ - Embedded app build: ${release.appBuild}
35584
+ - Version/build: ${version}
35585
+
35586
+ This certification is local and contains no signing credentials, device identifiers, SSH destinations, report paths, application data, or page contents. Re-run verification against the immutable release before using it as a CI or promotion gate.
35587
+
35588
+ | Strength | Network unavailable | Execution | Report SHA-256 |
35589
+ | --- | --- | --- | --- |
35590
+ ${evidence}
35591
+ `;
35592
+ };
35593
+ var verifyAbsoluteMobileReleaseCertification = (certification, release, requirement) => {
35594
+ const parsed = parseCertification(certification);
35595
+ const expected = releaseIdentity(release);
35596
+ if (JSON.stringify(parsed.release) !== JSON.stringify(expected))
35597
+ throw new TypeError("Mobile release certification was invalidated by a release identity, artifact, runtime, signing, version, build, or embedded-bundle change.");
35598
+ if (requirement) {
35599
+ validateRequirement(expected.platform, requirement);
35600
+ if (!satisfiesRequirement(parsed.strength, requirement))
35601
+ throw new TypeError(`Mobile release certification strength ${parsed.strength} does not satisfy required ${requirement} policy.`);
35602
+ }
35603
+ return parsed;
35604
+ };
35605
+ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification, outputDirectory) => {
35606
+ const root = resolve18(projectRoot);
35607
+ const parent = resolve18(root, outputDirectory ?? join30(".absolutejs", "mobile", "certifications", certification.release.platform, certification.release.releaseId));
35608
+ if (!inside4(root, parent))
35609
+ throw new TypeError("Mobile certification output must remain inside the project.");
35610
+ await mkdir20(parent, { recursive: true });
35611
+ const destination = join30(parent, certification.certificationId);
35612
+ const paths = {
35613
+ directory: destination,
35614
+ jsonPath: join30(destination, "certification.json"),
35615
+ markdownPath: join30(destination, "certification.md")
35616
+ };
35617
+ const json = `${JSON.stringify(certification, null, 2)}
35618
+ `;
35619
+ const markdown = renderAbsoluteMobileReleaseCertification(certification);
35620
+ const verifyExisting = async () => {
35621
+ const [existingJson, existingMarkdown] = await Promise.all([
35622
+ readFile26(paths.jsonPath, "utf8"),
35623
+ readFile26(paths.markdownPath, "utf8")
35624
+ ]);
35625
+ if (existingJson !== json || existingMarkdown !== markdown)
35626
+ throw new TypeError(`Immutable mobile certification ${certification.certificationId} already exists with different content.`);
35627
+ };
35628
+ if (await exists9(destination)) {
35629
+ await verifyExisting();
35630
+ return paths;
35631
+ }
35632
+ const staging = await mkdtemp10(join30(parent, ".certification-"));
35633
+ try {
35634
+ await Promise.all([
35635
+ writeFile22(join30(staging, "certification.json"), json, {
35636
+ flag: "wx"
35637
+ }),
35638
+ writeFile22(join30(staging, "certification.md"), markdown, {
35639
+ flag: "wx"
35640
+ })
35641
+ ]);
35642
+ await rename18(staging, destination).catch(async (error) => {
35643
+ if (await exists9(destination))
35644
+ return;
35645
+ throw error;
35646
+ });
35647
+ } finally {
35648
+ await rm15(staging, { force: true, recursive: true });
35649
+ }
35650
+ await verifyExisting();
35651
+ return paths;
35652
+ };
35250
35653
  // src/mobile/releasePublisher.ts
35251
- import { access as access17 } from "fs/promises";
35252
- import { isAbsolute as isAbsolute9, relative as relative16, resolve as resolve18, sep as sep11 } from "path";
35654
+ import { access as access18 } from "fs/promises";
35655
+ import { isAbsolute as isAbsolute10, relative as relative17, resolve as resolve19, sep as sep12 } from "path";
35253
35656
  import { pathToFileURL as pathToFileURL3 } from "url";
35254
35657
  var prepareAbsoluteIosRelease = async (publisher, options) => {
35255
35658
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -35272,24 +35675,24 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
35272
35675
  }
35273
35676
  return versionCode;
35274
35677
  };
35275
- var isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35276
- var isPublisher = (value) => isRecord14(value) && typeof value.publish === "function";
35678
+ var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
35679
+ var isPublisher = (value) => isRecord15(value) && typeof value.publish === "function";
35277
35680
  var publisherModulePath = (projectRoot, requested) => {
35278
- const root = resolve18(projectRoot);
35279
- const path = resolve18(root, requested);
35280
- const projectRelative = relative16(root, path);
35281
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep11}`) || isAbsolute9(projectRelative)) {
35681
+ const root = resolve19(projectRoot);
35682
+ const path = resolve19(root, requested);
35683
+ const projectRelative = relative17(root, path);
35684
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep12}`) || isAbsolute10(projectRelative)) {
35282
35685
  throw new TypeError("mobile publish --registry must remain inside the project.");
35283
35686
  }
35284
35687
  return path;
35285
35688
  };
35286
35689
  var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
35287
35690
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
35288
- await access17(modulePath).catch(() => {
35691
+ await access18(modulePath).catch(() => {
35289
35692
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
35290
35693
  });
35291
35694
  const loaded = await import(pathToFileURL3(modulePath).href);
35292
- const publisher = isRecord14(loaded) ? loaded.default ?? loaded.registry : undefined;
35695
+ const publisher = isRecord15(loaded) ? loaded.default ?? loaded.registry : undefined;
35293
35696
  if (!isPublisher(publisher)) {
35294
35697
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
35295
35698
  }
@@ -35349,7 +35752,7 @@ var publishAbsoluteIosRelease = async (options) => {
35349
35752
  init_pageProtocol();
35350
35753
  init_releaseArtifact();
35351
35754
  import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
35352
- import { dirname as dirname20, extname as extname6, relative as relative17, resolve as resolve19 } from "path";
35755
+ import { dirname as dirname20, extname as extname6, relative as relative18, resolve as resolve20 } from "path";
35353
35756
  import ts2 from "typescript";
35354
35757
  var ROUTE_METHODS = new Set(["get", "head"]);
35355
35758
  var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
@@ -35518,7 +35921,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
35518
35921
  const declaration = symbol?.declarations?.[0];
35519
35922
  const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
35520
35923
  const exportedName = symbol?.name ?? expression.getText(sourceFile);
35521
- const source = posixPath(relative17(projectRoot, file));
35924
+ const source = posixPath(relative18(projectRoot, file));
35522
35925
  return `${source}#${exportedName}`;
35523
35926
  };
35524
35927
  var resolveAlias = (symbol, checker) => {
@@ -35757,7 +36160,7 @@ var analyzeProgram = (program, projectRoot) => {
35757
36160
  const checker = program.getTypeChecker();
35758
36161
  const analyzed = new Map;
35759
36162
  for (const sourceFile of program.getSourceFiles()) {
35760
- const resolvedFile = resolve19(sourceFile.fileName);
36163
+ const resolvedFile = resolve20(sourceFile.fileName);
35761
36164
  if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
35762
36165
  continue;
35763
36166
  const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
@@ -35850,14 +36253,14 @@ var transformFile = (source, fileName, analysis) => {
35850
36253
  };
35851
36254
  var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
35852
36255
  var createAbsoluteMobileRouteMetadataPlugin = (options) => {
35853
- const projectRoot = resolve19(options.projectRoot ?? process.cwd());
35854
- const entry = resolve19(options.entry);
36256
+ const projectRoot = resolve20(options.projectRoot ?? process.cwd());
36257
+ const entry = resolve20(options.entry);
35855
36258
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
35856
36259
  return {
35857
36260
  name: "absolute-mobile-route-metadata",
35858
36261
  setup(build) {
35859
36262
  build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
35860
- const analysis = analyzed.get(resolve19(path));
36263
+ const analysis = analyzed.get(resolve20(path));
35861
36264
  if (!analysis)
35862
36265
  return;
35863
36266
  const source = await Bun.file(path).text();
@@ -35870,11 +36273,11 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
35870
36273
  };
35871
36274
  };
35872
36275
  var inspectAbsoluteMobileRouteMetadata = (options) => {
35873
- const projectRoot = resolve19(options.projectRoot ?? process.cwd());
35874
- const entry = resolve19(options.entry);
36276
+ const projectRoot = resolve20(options.projectRoot ?? process.cwd());
36277
+ const entry = resolve20(options.entry);
35875
36278
  const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
35876
36279
  return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
35877
- file: posixPath(relative17(projectRoot, file)),
36280
+ file: posixPath(relative18(projectRoot, file)),
35878
36281
  metadata
35879
36282
  })));
35880
36283
  };
@@ -36208,7 +36611,7 @@ var createCapacitor = (win) => {
36208
36611
  callbackId
36209
36612
  }, callback);
36210
36613
  };
36211
- const p = new Promise((resolve20) => call.then(() => resolve20({ remove })));
36614
+ const p = new Promise((resolve21) => call.then(() => resolve21({ remove })));
36212
36615
  p.remove = async () => {
36213
36616
  console.warn(`Using addListener() without 'await' is deprecated.`);
36214
36617
  await remove();
@@ -36413,11 +36816,11 @@ class CapacitorCookiesPluginWeb extends WebPlugin {
36413
36816
  var CapacitorCookies = registerPlugin("CapacitorCookies", {
36414
36817
  web: () => new CapacitorCookiesPluginWeb
36415
36818
  });
36416
- var readBlobAsBase64 = async (blob) => new Promise((resolve20, reject) => {
36819
+ var readBlobAsBase64 = async (blob) => new Promise((resolve21, reject) => {
36417
36820
  const reader = new FileReader;
36418
36821
  reader.onload = () => {
36419
36822
  const base64String = reader.result;
36420
- resolve20(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
36823
+ resolve21(base64String.indexOf(",") >= 0 ? base64String.split(",")[1] : base64String);
36421
36824
  };
36422
36825
  reader.onerror = (error) => reject(error);
36423
36826
  reader.readAsDataURL(blob);
@@ -36593,7 +36996,7 @@ var REPORT_KINDS = new Set([
36593
36996
  "signal"
36594
36997
  ]);
36595
36998
  var nativeObservability = registerPlugin("AbsoluteMobileObservability");
36596
- var isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36999
+ var isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36597
37000
  var redactText = (value) => value.slice(0, MAX_STRING_LENGTH).replace(/\b(Bearer)\s+[A-Za-z0-9._~+/-]+=*/giu, "$1 [REDACTED]").replace(/([?&](?:access_token|api_?key|authorization|code|id_token|password|refresh_token|secret|token)=)[^&#\s]*/giu, "$1[REDACTED]").replace(/\b((?:access_?token|api_?key|authorization|client_?secret|cookie|id_?token|password|passwd|refresh_?token|secret|token)\s*[:=]\s*)[^,\s;]+/giu, "$1[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]");
36598
37001
  var redactValue = (value, depth = 0) => {
36599
37002
  if (depth >= MAX_REDACTION_DEPTH)
@@ -36604,7 +37007,7 @@ var redactValue = (value, depth = 0) => {
36604
37007
  return value;
36605
37008
  if (Array.isArray(value))
36606
37009
  return value.slice(0, 64).map((entry) => redactValue(entry, depth + 1));
36607
- if (!isRecord15(value))
37010
+ if (!isRecord16(value))
36608
37011
  return String(value).slice(0, MAX_STRING_LENGTH);
36609
37012
  return Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [
36610
37013
  key,
@@ -36612,7 +37015,7 @@ var redactValue = (value, depth = 0) => {
36612
37015
  ]));
36613
37016
  };
36614
37017
  var parseReport = (value) => {
36615
- if (!isRecord15(value) || !isRecord15(value.details))
37018
+ if (!isRecord16(value) || !isRecord16(value.details))
36616
37019
  return;
36617
37020
  if (typeof value.id !== "string" || !REPORT_ID.test(value.id))
36618
37021
  return;
@@ -36623,7 +37026,7 @@ var parseReport = (value) => {
36623
37026
  if (value.platform !== "android" && value.platform !== "ios")
36624
37027
  return;
36625
37028
  const details = redactValue(value.details);
36626
- if (!isRecord15(details))
37029
+ if (!isRecord16(details))
36627
37030
  return;
36628
37031
  if (new TextEncoder().encode(JSON.stringify(details)).byteLength > MAX_DETAIL_BYTES)
36629
37032
  return;
@@ -41077,8 +41480,8 @@ var createBeacon = (options) => {
41077
41480
  if (await fontAvailable(element))
41078
41481
  return;
41079
41482
  if (confirmationMs > 0) {
41080
- await new Promise((resolve20) => {
41081
- const timer2 = window.setTimeout(resolve20, confirmationMs);
41483
+ await new Promise((resolve21) => {
41484
+ const timer2 = window.setTimeout(resolve21, confirmationMs);
41082
41485
  cleanups.push(() => window.clearTimeout(timer2));
41083
41486
  });
41084
41487
  if (document.visibilityState === "hidden")
@@ -42066,51 +42469,51 @@ var createAbsoluteMobileUpdateClient = (options) => {
42066
42469
  };
42067
42470
  };
42068
42471
  // src/mobile/updatePublisher.ts
42069
- import { access as access18 } from "fs/promises";
42070
- import { isAbsolute as isAbsolute10, relative as relative19, resolve as resolve21, sep as sep12 } from "path";
42472
+ import { access as access19 } from "fs/promises";
42473
+ import { isAbsolute as isAbsolute11, relative as relative20, resolve as resolve22, sep as sep13 } from "path";
42071
42474
  import { pathToFileURL as pathToFileURL4 } from "url";
42072
42475
 
42073
42476
  // src/mobile/updateSigning.ts
42074
- import { createHash as createHash16, sign, verify } from "crypto";
42477
+ import { createHash as createHash17, sign, verify } from "crypto";
42075
42478
  import {
42076
42479
  cp as cp2,
42077
- mkdir as mkdir20,
42078
- mkdtemp as mkdtemp10,
42480
+ mkdir as mkdir21,
42481
+ mkdtemp as mkdtemp11,
42079
42482
  readdir as readdir10,
42080
- readFile as readFile26,
42081
- rename as rename18,
42082
- rm as rm15,
42483
+ readFile as readFile27,
42484
+ rename as rename19,
42485
+ rm as rm16,
42083
42486
  stat as stat6,
42084
- writeFile as writeFile22
42487
+ writeFile as writeFile23
42085
42488
  } from "fs/promises";
42086
- import { dirname as dirname21, join as join30, relative as relative18, resolve as resolve20 } from "path";
42489
+ import { dirname as dirname21, join as join31, relative as relative19, resolve as resolve21 } from "path";
42087
42490
  var UPDATE_MANIFEST_FILE = "update.json";
42088
42491
  var UPDATE_FILES_DIRECTORY = "files";
42089
- var sha2564 = (value) => createHash16("sha256").update(value).digest("hex");
42492
+ var sha2565 = (value) => createHash17("sha256").update(value).digest("hex");
42090
42493
  var listFiles = async (root, directory = root) => {
42091
42494
  const entries = await readdir10(directory, { withFileTypes: true });
42092
42495
  const paths = await Promise.all(entries.map(async (entry) => {
42093
- const path = join30(directory, entry.name);
42496
+ const path = join31(directory, entry.name);
42094
42497
  if (entry.isDirectory())
42095
42498
  return listFiles(root, path);
42096
42499
  if (!entry.isFile())
42097
42500
  throw new TypeError("Mobile updates cannot contain links or special files.");
42098
- return [relative18(root, path).replaceAll("\\", "/")];
42501
+ return [relative19(root, path).replaceAll("\\", "/")];
42099
42502
  }));
42100
42503
  return paths.flat().sort((left, right) => left.localeCompare(right));
42101
42504
  };
42102
42505
  var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
42103
- const source = join30(root, path);
42506
+ const source = join31(root, path);
42104
42507
  const [metadata, contents] = await Promise.all([
42105
42508
  stat6(source),
42106
- readFile26(source)
42509
+ readFile27(source)
42107
42510
  ]);
42108
- return { bytes: metadata.size, path, sha256: sha2564(contents) };
42511
+ return { bytes: metadata.size, path, sha256: sha2565(contents) };
42109
42512
  }));
42110
- var releaseIdFor = (value) => `amu_${sha2564(canonicalizeAbsoluteMobileUpdate(value))}`;
42513
+ var releaseIdFor = (value) => `amu_${sha2565(canonicalizeAbsoluteMobileUpdate(value))}`;
42111
42514
  var buildAbsoluteMobileUpdate = async (options) => {
42112
- const bundleDirectory = resolve20(options.bundleDirectory);
42113
- const outputRoot = resolve20(options.outputDirectory);
42515
+ const bundleDirectory = resolve21(options.bundleDirectory);
42516
+ const outputRoot = resolve21(options.outputDirectory);
42114
42517
  if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
42115
42518
  throw new TypeError("Mobile update output must be outside the embedded bundle.");
42116
42519
  const paths = await listFiles(bundleDirectory);
@@ -42138,38 +42541,38 @@ var buildAbsoluteMobileUpdate = async (options) => {
42138
42541
  value: signature.toString("base64")
42139
42542
  }
42140
42543
  });
42141
- await mkdir20(outputRoot, { recursive: true });
42142
- const outputDirectory = join30(outputRoot, manifest.releaseId);
42143
- const staging = await mkdtemp10(join30(outputRoot, ".stage-"));
42544
+ await mkdir21(outputRoot, { recursive: true });
42545
+ const outputDirectory = join31(outputRoot, manifest.releaseId);
42546
+ const staging = await mkdtemp11(join31(outputRoot, ".stage-"));
42144
42547
  try {
42145
- await cp2(bundleDirectory, join30(staging, UPDATE_FILES_DIRECTORY), {
42548
+ await cp2(bundleDirectory, join31(staging, UPDATE_FILES_DIRECTORY), {
42146
42549
  force: true,
42147
42550
  recursive: true
42148
42551
  });
42149
- await writeFile22(join30(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
42552
+ await writeFile23(join31(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
42150
42553
  `);
42151
- await rename18(staging, outputDirectory);
42554
+ await rename19(staging, outputDirectory);
42152
42555
  } catch (error) {
42153
- await rm15(staging, { force: true, recursive: true });
42556
+ await rm16(staging, { force: true, recursive: true });
42154
42557
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
42155
42558
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
42156
42559
  throw error;
42157
42560
  }
42158
42561
  return {
42159
42562
  manifest,
42160
- manifestPath: join30(outputDirectory, UPDATE_MANIFEST_FILE),
42563
+ manifestPath: join31(outputDirectory, UPDATE_MANIFEST_FILE),
42161
42564
  outputDirectory
42162
42565
  };
42163
42566
  };
42164
42567
  var copyAbsoluteMobileUpdateFile = async (updateDirectory, path, destination) => {
42165
- const source = resolve20(updateDirectory, UPDATE_FILES_DIRECTORY, path);
42166
- const root = resolve20(updateDirectory, UPDATE_FILES_DIRECTORY);
42568
+ const source = resolve21(updateDirectory, UPDATE_FILES_DIRECTORY, path);
42569
+ const root = resolve21(updateDirectory, UPDATE_FILES_DIRECTORY);
42167
42570
  if (!source.startsWith(`${root}/`))
42168
42571
  throw new TypeError("Update file escaped its release.");
42169
- await mkdir20(dirname21(destination), { recursive: true });
42572
+ await mkdir21(dirname21(destination), { recursive: true });
42170
42573
  await cp2(source, destination, { force: true });
42171
42574
  };
42172
- var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile26(join30(resolve20(directory), UPDATE_MANIFEST_FILE), "utf8")));
42575
+ var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile27(join31(resolve21(directory), UPDATE_MANIFEST_FILE), "utf8")));
42173
42576
  var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
42174
42577
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
42175
42578
  const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
@@ -42286,16 +42689,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
42286
42689
  var object5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
42287
42690
  var isPublisher2 = (value) => object5(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
42288
42691
  var projectPath2 = (projectRoot, requested, label) => {
42289
- const root = resolve21(projectRoot);
42290
- const path = resolve21(root, requested);
42291
- const projectRelative = relative19(root, path);
42292
- if (projectRelative === ".." || projectRelative.startsWith(`..${sep12}`) || isAbsolute10(projectRelative))
42692
+ const root = resolve22(projectRoot);
42693
+ const path = resolve22(root, requested);
42694
+ const projectRelative = relative20(root, path);
42695
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep13}`) || isAbsolute11(projectRelative))
42293
42696
  throw new TypeError(`${label} must remain inside the project.`);
42294
42697
  return path;
42295
42698
  };
42296
42699
  var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
42297
42700
  const modulePath = projectPath2(projectRoot, requestedModulePath, "mobile update registry");
42298
- await access18(modulePath).catch(() => {
42701
+ await access19(modulePath).catch(() => {
42299
42702
  throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
42300
42703
  });
42301
42704
  const loaded = await import(pathToFileURL4(modulePath).href);
@@ -42346,7 +42749,7 @@ var rollbackAbsoluteMobileUpdate = async (options) => {
42346
42749
  return result;
42347
42750
  };
42348
42751
  // src/mobile/updateRollout.ts
42349
- import { createHash as createHash17 } from "crypto";
42752
+ import { createHash as createHash18 } from "crypto";
42350
42753
  var UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-8][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u;
42351
42754
  var requireHeader = (headers, name) => {
42352
42755
  const value = headers.get(name);
@@ -42361,7 +42764,7 @@ var isAbsoluteMobileUpdateRolloutMember = (options) => {
42361
42764
  return false;
42362
42765
  if (options.rollout === 1)
42363
42766
  return true;
42364
- const digest = createHash17("sha256").update(`${options.appId}\x00${options.channel}\x00${options.releaseId}\x00${options.installationId}`).digest();
42767
+ const digest = createHash18("sha256").update(`${options.appId}\x00${options.channel}\x00${options.releaseId}\x00${options.installationId}`).digest();
42365
42768
  const bucket = digest.readUInt32BE(0) / 4294967296;
42366
42769
  return bucket < options.rollout;
42367
42770
  };
@@ -42408,6 +42811,7 @@ export {
42408
42811
  ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE,
42409
42812
  ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
42410
42813
  ABSOLUTE_MOBILE_PREVIEW_PATH,
42814
+ ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT,
42411
42815
  ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
42412
42816
  ABSOLUTE_MOBILE_ROUTE_DETAIL,
42413
42817
  ABSOLUTE_MOBILE_SHELL_ABI,
@@ -42493,6 +42897,7 @@ export {
42493
42897
  createAbsoluteMobilePageErrorResponse,
42494
42898
  createAbsoluteMobilePageRequest,
42495
42899
  createAbsoluteMobilePreviewPlugin,
42900
+ createAbsoluteMobileReleaseCertification,
42496
42901
  createAbsoluteMobileRouteMetadataPlugin,
42497
42902
  createAbsoluteMobileUpdateClient,
42498
42903
  createAbsoluteMobileUpdateRuntimeDescriptor,
@@ -42602,10 +43007,12 @@ export {
42602
43007
  readAbsoluteIosRelease,
42603
43008
  readAbsoluteMobileLinkIntent,
42604
43009
  readAbsoluteMobileMaterializedReleases,
43010
+ readAbsoluteMobileReleaseCertification,
42605
43011
  readAbsoluteMobileUpdate,
42606
43012
  reconcileAbsoluteMobileUpdateRollout,
42607
43013
  redactAbsoluteIosLog,
42608
43014
  removeAbsoluteRemoteMacProfile,
43015
+ renderAbsoluteMobileReleaseCertification,
42609
43016
  renderAbsoluteMobileUpdateRegistry,
42610
43017
  repairAbsoluteIosDevSession,
42611
43018
  reportAbsoluteMobileUpdateHealth,
@@ -42645,13 +43052,15 @@ export {
42645
43052
  validateAbsoluteSshDestination,
42646
43053
  verifyAbsoluteMobileAssociationFiles,
42647
43054
  verifyAbsoluteMobileCompatibilityProducer,
43055
+ verifyAbsoluteMobileReleaseCertification,
42648
43056
  verifyAbsoluteMobileUpdateSignature,
42649
43057
  waitForAbsoluteIosHmrLog,
42650
43058
  writeAbsoluteCapacitorConfig,
42651
43059
  writeAbsoluteExpoProject,
42652
43060
  writeAbsoluteMobileGithubWorkflow,
43061
+ writeAbsoluteMobileReleaseCertification,
42653
43062
  writeAbsoluteMobileUpdateRegistry
42654
43063
  };
42655
43064
 
42656
- //# debugId=45962AF96688DC1364756E2164756E21
43065
+ //# debugId=DB53CC7C8DF0442B64756E2164756E21
42657
43066
  //# sourceMappingURL=index.js.map