@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.
- package/CHANGELOG.md +13 -0
- package/README.md +6 -0
- package/changelog.json +37 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +645 -622
- package/dist/build.js.map +7 -6
- package/dist/cli/index.js +1 -1
- package/dist/cli/{mobile-w4my2dc7.js → mobile-7v8hfmdq.js} +673 -161
- package/dist/index.js +603 -580
- package/dist/index.js.map +7 -6
- package/dist/mobile/index.js +504 -95
- package/dist/mobile/index.js.map +5 -4
- package/dist/src/build/injectVueComposableTracking.d.ts +2 -28
- package/dist/src/build/vueModuleOutputPath.d.ts +7 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/releaseCertification.d.ts +59 -0
- package/package.json +1 -1
|
@@ -4415,9 +4415,9 @@ var require_sha256 = __commonJS(function(exports, module) {
|
|
|
4415
4415
|
var forge = require_forge();
|
|
4416
4416
|
require_md();
|
|
4417
4417
|
require_util();
|
|
4418
|
-
var
|
|
4419
|
-
forge.md.sha256 = forge.md.algorithms.sha256 =
|
|
4420
|
-
|
|
4418
|
+
var sha2565 = module.exports = forge.sha256 = forge.sha256 || {};
|
|
4419
|
+
forge.md.sha256 = forge.md.algorithms.sha256 = sha2565;
|
|
4420
|
+
sha2565.create = function() {
|
|
4421
4421
|
if (!_initialized) {
|
|
4422
4422
|
_init();
|
|
4423
4423
|
}
|
|
@@ -16779,15 +16779,15 @@ var require_main = __commonJS(function(exports) {
|
|
|
16779
16779
|
|
|
16780
16780
|
// src/cli/scripts/mobile.ts
|
|
16781
16781
|
import {
|
|
16782
|
-
access as
|
|
16783
|
-
mkdir as
|
|
16784
|
-
mkdtemp as
|
|
16785
|
-
readFile as
|
|
16786
|
-
rm as
|
|
16787
|
-
writeFile as
|
|
16782
|
+
access as access15,
|
|
16783
|
+
mkdir as mkdir16,
|
|
16784
|
+
mkdtemp as mkdtemp6,
|
|
16785
|
+
readFile as readFile20,
|
|
16786
|
+
rm as rm9,
|
|
16787
|
+
writeFile as writeFile17
|
|
16788
16788
|
} from "fs/promises";
|
|
16789
16789
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16790
|
-
import { join as
|
|
16790
|
+
import { join as join17, relative as relative14, resolve as resolve15 } from "path";
|
|
16791
16791
|
import { createInterface } from "readline/promises";
|
|
16792
16792
|
|
|
16793
16793
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -22170,9 +22170,413 @@ var createAbsoluteAndroidTestReport = (options) => {
|
|
|
22170
22170
|
});
|
|
22171
22171
|
};
|
|
22172
22172
|
|
|
22173
|
+
// src/mobile/releaseCertification.ts
|
|
22174
|
+
import { createHash as createHash5 } from "crypto";
|
|
22175
|
+
import {
|
|
22176
|
+
access as access8,
|
|
22177
|
+
mkdir as mkdir11,
|
|
22178
|
+
mkdtemp as mkdtemp4,
|
|
22179
|
+
readFile as readFile15,
|
|
22180
|
+
rename as rename10,
|
|
22181
|
+
rm as rm7,
|
|
22182
|
+
writeFile as writeFile12
|
|
22183
|
+
} from "fs/promises";
|
|
22184
|
+
import { basename as basename3, isAbsolute as isAbsolute5, join as join13, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
|
|
22185
|
+
var ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT = 1;
|
|
22186
|
+
var CERTIFICATION_PREFIX = "amobile_cert_";
|
|
22187
|
+
var SHA256 = /^[a-f0-9]{64}$/u;
|
|
22188
|
+
var STRENGTH_RANK = {
|
|
22189
|
+
device: 1,
|
|
22190
|
+
installed: 0,
|
|
22191
|
+
simulator: 0,
|
|
22192
|
+
store: 2
|
|
22193
|
+
};
|
|
22194
|
+
var isRequirement = (value) => value === "installed" || value === "simulator" || value === "device" || value === "store";
|
|
22195
|
+
var isStrength = (value) => value === "installed" || value === "simulator" || value === "device" || value === "store";
|
|
22196
|
+
var isArtifactExactness = (value) => value === "archive-equivalent" || value === "source-equivalent" || value === "store-delivered";
|
|
22197
|
+
var isDistribution = (value) => value === "apple-processed" || value === "registered-device" || value === "simulator-release";
|
|
22198
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22199
|
+
var inside3 = (root, candidate) => {
|
|
22200
|
+
const path = relative7(resolve8(root), resolve8(candidate));
|
|
22201
|
+
return path === "" || !isAbsolute5(path) && path !== ".." && !path.startsWith(`..${sep5}`);
|
|
22202
|
+
};
|
|
22203
|
+
var exists4 = async (path) => {
|
|
22204
|
+
try {
|
|
22205
|
+
await access8(path);
|
|
22206
|
+
return true;
|
|
22207
|
+
} catch {
|
|
22208
|
+
return false;
|
|
22209
|
+
}
|
|
22210
|
+
};
|
|
22211
|
+
var sha2563 = (bytes) => createHash5("sha256").update(bytes).digest("hex");
|
|
22212
|
+
var requireString3 = (value, name) => {
|
|
22213
|
+
if (typeof value !== "string" || !value)
|
|
22214
|
+
throw new TypeError(`Mobile certification ${name} is invalid.`);
|
|
22215
|
+
return value;
|
|
22216
|
+
};
|
|
22217
|
+
var requirePositiveInteger = (value, name) => {
|
|
22218
|
+
if (!Number.isSafeInteger(value) || Number(value) < 1)
|
|
22219
|
+
throw new TypeError(`Mobile certification ${name} is invalid.`);
|
|
22220
|
+
return Number(value);
|
|
22221
|
+
};
|
|
22222
|
+
var resolveEvidencePath = async (projectRoot, requested) => {
|
|
22223
|
+
const selected = resolve8(projectRoot, requested);
|
|
22224
|
+
if (!inside3(projectRoot, selected))
|
|
22225
|
+
throw new TypeError("Mobile certification evidence must remain inside the project.");
|
|
22226
|
+
if (basename3(selected) === "report.json")
|
|
22227
|
+
return selected;
|
|
22228
|
+
const report = join13(selected, "report.json");
|
|
22229
|
+
if (!await exists4(report))
|
|
22230
|
+
throw new TypeError(`Mobile certification evidence does not contain report.json: ${requested}`);
|
|
22231
|
+
return report;
|
|
22232
|
+
};
|
|
22233
|
+
var releaseIdentity = (release) => {
|
|
22234
|
+
const { metadata } = release;
|
|
22235
|
+
if (!metadata.signed)
|
|
22236
|
+
throw new TypeError("Mobile release certification requires a signed native release.");
|
|
22237
|
+
const common = {
|
|
22238
|
+
appBuild: metadata.appBuild,
|
|
22239
|
+
appId: metadata.appId,
|
|
22240
|
+
artifactBytes: metadata.bytes,
|
|
22241
|
+
artifactSha256: metadata.sha256,
|
|
22242
|
+
engine: metadata.engine,
|
|
22243
|
+
platform: metadata.platform,
|
|
22244
|
+
releaseId: metadata.releaseId,
|
|
22245
|
+
runtime: metadata.runtime,
|
|
22246
|
+
signed: true
|
|
22247
|
+
};
|
|
22248
|
+
if (metadata.platform === "android")
|
|
22249
|
+
return {
|
|
22250
|
+
...common,
|
|
22251
|
+
...metadata.versionCode === undefined ? {} : { versionCode: metadata.versionCode }
|
|
22252
|
+
};
|
|
22253
|
+
return {
|
|
22254
|
+
...common,
|
|
22255
|
+
...metadata.buildNumber === undefined ? {} : { buildNumber: metadata.buildNumber },
|
|
22256
|
+
marketingVersion: metadata.marketingVersion
|
|
22257
|
+
};
|
|
22258
|
+
};
|
|
22259
|
+
var requireCheck = (report, id, accepted = ["PASS"]) => {
|
|
22260
|
+
const checks = report.automatedChecks;
|
|
22261
|
+
if (!Array.isArray(checks))
|
|
22262
|
+
throw new TypeError("Mobile certification evidence has invalid automated checks.");
|
|
22263
|
+
const check = checks.find((candidate) => isRecord6(candidate) && candidate.id === id);
|
|
22264
|
+
if (!isRecord6(check) || !accepted.includes(String(check.result)))
|
|
22265
|
+
throw new TypeError(`Mobile certification evidence does not satisfy ${id}.`);
|
|
22266
|
+
};
|
|
22267
|
+
var validateCommonReport = (report, release) => {
|
|
22268
|
+
if (report.reportVersion !== 1 || report.platform !== release.platform || !isRecord6(report.metadata) || report.metadata.provider !== release.engine || !isRecord6(report.run) || report.run.status !== "pass" || report.run.appId !== release.appId)
|
|
22269
|
+
throw new TypeError("Mobile certification evidence does not match the release platform, provider, application, or successful-run contract.");
|
|
22270
|
+
requireCheck(report, "AUTO-DEV-01");
|
|
22271
|
+
return {
|
|
22272
|
+
generatedAt: requireString3(report.generatedAt, "evidence generatedAt"),
|
|
22273
|
+
run: report.run
|
|
22274
|
+
};
|
|
22275
|
+
};
|
|
22276
|
+
var validateReleaseFields = (value, release) => {
|
|
22277
|
+
if (value.releaseId !== release.releaseId || value.artifactSha256 !== release.artifactSha256 || value.artifactBytes !== release.artifactBytes || value.engine !== release.engine || value.signed !== true)
|
|
22278
|
+
throw new TypeError("Mobile certification evidence does not match the immutable release identity.");
|
|
22279
|
+
};
|
|
22280
|
+
var validateAndroidEvidence = (report, run, release, reportSha256, generatedAt) => {
|
|
22281
|
+
if (!isRecord6(run.release))
|
|
22282
|
+
throw new TypeError("Android certification evidence has no installed release result.");
|
|
22283
|
+
validateReleaseFields(run.release, release);
|
|
22284
|
+
if (run.release.embeddedOffline !== true)
|
|
22285
|
+
throw new TypeError("Android certification evidence did not prove embedded offline relaunch.");
|
|
22286
|
+
requireCheck(report, "AUTO-RELEASE-01");
|
|
22287
|
+
requireCheck(report, "AUTO-RELEASE-OFFLINE-01");
|
|
22288
|
+
return {
|
|
22289
|
+
generatedAt,
|
|
22290
|
+
networkUnavailable: "proven",
|
|
22291
|
+
remote: false,
|
|
22292
|
+
reportSha256,
|
|
22293
|
+
strength: "installed"
|
|
22294
|
+
};
|
|
22295
|
+
};
|
|
22296
|
+
var validateIosEvidence = (report, run, release, reportSha256, generatedAt) => {
|
|
22297
|
+
if (!isRecord6(run.iosRelease))
|
|
22298
|
+
throw new TypeError("iOS certification evidence has no installed release result.");
|
|
22299
|
+
const ios = run.iosRelease;
|
|
22300
|
+
validateReleaseFields(ios, release);
|
|
22301
|
+
if (ios.embeddedLocal !== true)
|
|
22302
|
+
throw new TypeError("iOS certification evidence did not prove embedded local rendering.");
|
|
22303
|
+
requireCheck(report, "AUTO-IOS-RELEASE-01");
|
|
22304
|
+
let strength;
|
|
22305
|
+
if (ios.distribution === "simulator-release" && ios.artifactExactness === "source-equivalent" && run.targetKind === "simulator" && ios.networkUnavailable === "not-proven")
|
|
22306
|
+
strength = "simulator";
|
|
22307
|
+
else if (ios.distribution === "registered-device" && ios.artifactExactness === "archive-equivalent" && run.targetKind === "device" && ios.networkUnavailable === "user-confirmed")
|
|
22308
|
+
strength = "device";
|
|
22309
|
+
else if (ios.distribution === "apple-processed" && ios.artifactExactness === "store-delivered" && run.targetKind === "device" && ios.networkUnavailable === "user-confirmed")
|
|
22310
|
+
strength = "store";
|
|
22311
|
+
else
|
|
22312
|
+
throw new TypeError("iOS certification evidence contains an invalid target, distribution, exactness, or offline-strength combination.");
|
|
22313
|
+
if (strength === "simulator")
|
|
22314
|
+
requireCheck(report, "AUTO-IOS-RELEASE-OFFLINE-01", ["SKIPPED"]);
|
|
22315
|
+
else
|
|
22316
|
+
requireCheck(report, "AUTO-IOS-RELEASE-OFFLINE-01");
|
|
22317
|
+
return {
|
|
22318
|
+
artifactExactness: ios.artifactExactness,
|
|
22319
|
+
distribution: ios.distribution,
|
|
22320
|
+
generatedAt,
|
|
22321
|
+
networkUnavailable: ios.networkUnavailable === "user-confirmed" ? "proven" : "not-proven",
|
|
22322
|
+
remote: ios.remote === true,
|
|
22323
|
+
reportSha256,
|
|
22324
|
+
strength
|
|
22325
|
+
};
|
|
22326
|
+
};
|
|
22327
|
+
var validateEvidence = async (projectRoot, requested, release) => {
|
|
22328
|
+
const path = await resolveEvidencePath(projectRoot, requested);
|
|
22329
|
+
const bytes = await readFile15(path);
|
|
22330
|
+
let value;
|
|
22331
|
+
try {
|
|
22332
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
22333
|
+
} catch {
|
|
22334
|
+
throw new TypeError("Mobile certification evidence report is not valid JSON.");
|
|
22335
|
+
}
|
|
22336
|
+
if (!isRecord6(value))
|
|
22337
|
+
throw new TypeError("Mobile certification evidence report must contain an object.");
|
|
22338
|
+
const { generatedAt, run } = validateCommonReport(value, release);
|
|
22339
|
+
const digest = sha2563(bytes);
|
|
22340
|
+
return release.platform === "android" ? validateAndroidEvidence(value, run, release, digest, generatedAt) : validateIosEvidence(value, run, release, digest, generatedAt);
|
|
22341
|
+
};
|
|
22342
|
+
var defaultRequirement = (platform) => platform === "android" ? "installed" : "simulator";
|
|
22343
|
+
var validateRequirement = (platform, requirement) => {
|
|
22344
|
+
if (platform === "android" && requirement !== "installed")
|
|
22345
|
+
throw new TypeError("Android certification currently uses its fixed installed/offline requirement; --require simulator, device, and store are iOS policies.");
|
|
22346
|
+
if (platform === "ios" && requirement === "installed")
|
|
22347
|
+
throw new TypeError("iOS certification requires simulator, device, or store evidence.");
|
|
22348
|
+
};
|
|
22349
|
+
var satisfiesRequirement = (strength, requirement) => {
|
|
22350
|
+
if (requirement === "installed")
|
|
22351
|
+
return strength === "installed";
|
|
22352
|
+
if (strength === "installed")
|
|
22353
|
+
return false;
|
|
22354
|
+
return STRENGTH_RANK[strength] >= STRENGTH_RANK[requirement];
|
|
22355
|
+
};
|
|
22356
|
+
var validateCertifiedEvidence = (platform, evidence) => {
|
|
22357
|
+
if (platform === "android") {
|
|
22358
|
+
if (evidence.strength !== "installed" || evidence.networkUnavailable !== "proven" || evidence.artifactExactness !== undefined || evidence.distribution !== undefined || evidence.remote)
|
|
22359
|
+
throw new TypeError("Android release certification contains invalid evidence strength.");
|
|
22360
|
+
return;
|
|
22361
|
+
}
|
|
22362
|
+
const simulator = evidence.strength === "simulator" && evidence.artifactExactness === "source-equivalent" && evidence.distribution === "simulator-release" && evidence.networkUnavailable === "not-proven";
|
|
22363
|
+
const device = evidence.strength === "device" && evidence.artifactExactness === "archive-equivalent" && evidence.distribution === "registered-device" && evidence.networkUnavailable === "proven";
|
|
22364
|
+
const store = evidence.strength === "store" && evidence.artifactExactness === "store-delivered" && evidence.distribution === "apple-processed" && evidence.networkUnavailable === "proven";
|
|
22365
|
+
if (!simulator && !device && !store)
|
|
22366
|
+
throw new TypeError("iOS release certification contains invalid evidence strength.");
|
|
22367
|
+
};
|
|
22368
|
+
var canonicalJsonValue = (value) => {
|
|
22369
|
+
if (Array.isArray(value))
|
|
22370
|
+
return value.map(canonicalJsonValue);
|
|
22371
|
+
if (!isRecord6(value))
|
|
22372
|
+
return value;
|
|
22373
|
+
const sorted = {};
|
|
22374
|
+
for (const key of Object.keys(value).sort())
|
|
22375
|
+
sorted[key] = canonicalJsonValue(value[key]);
|
|
22376
|
+
return sorted;
|
|
22377
|
+
};
|
|
22378
|
+
var certificationBody = (certification) => JSON.stringify(canonicalJsonValue(certification));
|
|
22379
|
+
var createAbsoluteMobileReleaseCertification = async (options) => {
|
|
22380
|
+
if (options.evidencePaths.length === 0)
|
|
22381
|
+
throw new TypeError("Mobile release certification requires at least one --evidence report.");
|
|
22382
|
+
const release = releaseIdentity(options.release);
|
|
22383
|
+
const requirement = options.requirement ?? defaultRequirement(release.platform);
|
|
22384
|
+
validateRequirement(release.platform, requirement);
|
|
22385
|
+
const evidence = await Promise.all(options.evidencePaths.map((path) => validateEvidence(options.projectRoot, path, release)));
|
|
22386
|
+
evidence.sort((left, right) => left.reportSha256.localeCompare(right.reportSha256));
|
|
22387
|
+
const [firstEvidence] = evidence;
|
|
22388
|
+
if (!firstEvidence)
|
|
22389
|
+
throw new TypeError("Mobile release certification requires valid evidence.");
|
|
22390
|
+
const strength = evidence.reduce((strongest, candidate) => STRENGTH_RANK[candidate.strength] > STRENGTH_RANK[strongest] ? candidate.strength : strongest, firstEvidence.strength);
|
|
22391
|
+
if (!satisfiesRequirement(strength, requirement))
|
|
22392
|
+
throw new TypeError(`Mobile release evidence strength ${strength} does not satisfy required ${requirement} certification.`);
|
|
22393
|
+
const body = {
|
|
22394
|
+
evidence,
|
|
22395
|
+
format: ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT,
|
|
22396
|
+
generatedAt: options.generatedAt ?? evidence.reduce((latest, candidate) => candidate.generatedAt > latest ? candidate.generatedAt : latest, firstEvidence.generatedAt),
|
|
22397
|
+
release,
|
|
22398
|
+
requirement,
|
|
22399
|
+
status: "certified",
|
|
22400
|
+
strength
|
|
22401
|
+
};
|
|
22402
|
+
return {
|
|
22403
|
+
certificationId: `${CERTIFICATION_PREFIX}${sha2563(certificationBody(body))}`,
|
|
22404
|
+
...body
|
|
22405
|
+
};
|
|
22406
|
+
};
|
|
22407
|
+
var parseCertification = (value) => {
|
|
22408
|
+
if (!isRecord6(value) || value.format !== ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT || value.status !== "certified" || !isRecord6(value.release) || !Array.isArray(value.evidence))
|
|
22409
|
+
throw new TypeError("Mobile release certification contract is invalid.");
|
|
22410
|
+
const { release } = value;
|
|
22411
|
+
const { platform } = release;
|
|
22412
|
+
const { engine } = release;
|
|
22413
|
+
if (platform !== "android" && platform !== "ios" || engine !== "capacitor" && engine !== "expo" || release.signed !== true)
|
|
22414
|
+
throw new TypeError("Mobile release certification identity is invalid.");
|
|
22415
|
+
const { requirement } = value;
|
|
22416
|
+
const { strength } = value;
|
|
22417
|
+
if (!isRequirement(requirement) || !isStrength(strength))
|
|
22418
|
+
throw new TypeError("Mobile release certification policy is invalid.");
|
|
22419
|
+
validateRequirement(platform, requirement);
|
|
22420
|
+
if (!satisfiesRequirement(strength, requirement))
|
|
22421
|
+
throw new TypeError("Mobile release certification does not satisfy its declared policy.");
|
|
22422
|
+
const artifactBytes = requirePositiveInteger(release.artifactBytes, "release artifactBytes");
|
|
22423
|
+
const artifactSha256 = requireString3(release.artifactSha256, "release artifactSha256");
|
|
22424
|
+
if (!SHA256.test(artifactSha256))
|
|
22425
|
+
throw new TypeError("Mobile release certification artifact digest is invalid.");
|
|
22426
|
+
const parsedRelease = {
|
|
22427
|
+
appBuild: requireString3(release.appBuild, "release appBuild"),
|
|
22428
|
+
appId: requireString3(release.appId, "release appId"),
|
|
22429
|
+
artifactBytes,
|
|
22430
|
+
artifactSha256,
|
|
22431
|
+
engine,
|
|
22432
|
+
platform,
|
|
22433
|
+
releaseId: requireString3(release.releaseId, "release releaseId"),
|
|
22434
|
+
runtime: requireString3(release.runtime, "release runtime"),
|
|
22435
|
+
signed: true,
|
|
22436
|
+
...release.buildNumber === undefined ? {} : {
|
|
22437
|
+
buildNumber: requirePositiveInteger(release.buildNumber, "release buildNumber")
|
|
22438
|
+
},
|
|
22439
|
+
...release.marketingVersion === undefined ? {} : {
|
|
22440
|
+
marketingVersion: requireString3(release.marketingVersion, "release marketingVersion")
|
|
22441
|
+
},
|
|
22442
|
+
...release.versionCode === undefined ? {} : {
|
|
22443
|
+
versionCode: requirePositiveInteger(release.versionCode, "release versionCode")
|
|
22444
|
+
}
|
|
22445
|
+
};
|
|
22446
|
+
const evidence = value.evidence.map((candidate) => {
|
|
22447
|
+
if (!isRecord6(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))
|
|
22448
|
+
throw new TypeError("Mobile release certification evidence is invalid.");
|
|
22449
|
+
const parsedEvidence = {
|
|
22450
|
+
generatedAt: candidate.generatedAt,
|
|
22451
|
+
networkUnavailable: candidate.networkUnavailable,
|
|
22452
|
+
remote: candidate.remote,
|
|
22453
|
+
reportSha256: String(candidate.reportSha256),
|
|
22454
|
+
strength: candidate.strength,
|
|
22455
|
+
...candidate.artifactExactness === undefined ? {} : {
|
|
22456
|
+
artifactExactness: candidate.artifactExactness
|
|
22457
|
+
},
|
|
22458
|
+
...candidate.distribution === undefined ? {} : { distribution: candidate.distribution }
|
|
22459
|
+
};
|
|
22460
|
+
validateCertifiedEvidence(platform, parsedEvidence);
|
|
22461
|
+
return parsedEvidence;
|
|
22462
|
+
});
|
|
22463
|
+
const strongestEvidence = evidence.reduce((strongest, candidate) => STRENGTH_RANK[candidate.strength] > STRENGTH_RANK[strongest] ? candidate.strength : strongest, evidence[0]?.strength ?? "installed");
|
|
22464
|
+
if (evidence.length === 0 || strongestEvidence !== strength)
|
|
22465
|
+
throw new TypeError("Mobile release certification evidence does not support its declared strength.");
|
|
22466
|
+
const parsed = {
|
|
22467
|
+
certificationId: requireString3(value.certificationId, "certificationId"),
|
|
22468
|
+
evidence,
|
|
22469
|
+
format: ABSOLUTE_MOBILE_RELEASE_CERTIFICATION_FORMAT,
|
|
22470
|
+
generatedAt: requireString3(value.generatedAt, "generatedAt"),
|
|
22471
|
+
release: parsedRelease,
|
|
22472
|
+
requirement,
|
|
22473
|
+
status: "certified",
|
|
22474
|
+
strength
|
|
22475
|
+
};
|
|
22476
|
+
const { certificationId, ...body } = parsed;
|
|
22477
|
+
if (certificationId !== `${CERTIFICATION_PREFIX}${sha2563(certificationBody(body))}`)
|
|
22478
|
+
throw new TypeError("Mobile release certification content digest is invalid.");
|
|
22479
|
+
return parsed;
|
|
22480
|
+
};
|
|
22481
|
+
var readAbsoluteMobileReleaseCertification = async (projectRoot, requested) => {
|
|
22482
|
+
const selected = resolve8(projectRoot, requested);
|
|
22483
|
+
if (!inside3(projectRoot, selected))
|
|
22484
|
+
throw new TypeError("Mobile release certification must remain inside the project.");
|
|
22485
|
+
const path = basename3(selected) === "certification.json" ? selected : join13(selected, "certification.json");
|
|
22486
|
+
const value = JSON.parse(await readFile15(path, "utf8"));
|
|
22487
|
+
return { certification: parseCertification(value), path };
|
|
22488
|
+
};
|
|
22489
|
+
var renderAbsoluteMobileReleaseCertification = (certification) => {
|
|
22490
|
+
const { release } = certification;
|
|
22491
|
+
const version = release.platform === "ios" ? `${release.marketingVersion ?? "unknown"} (${release.buildNumber ?? "unallocated"})` : String(release.versionCode ?? "unallocated");
|
|
22492
|
+
const evidence = certification.evidence.map((item) => `| ${item.strength} | ${item.networkUnavailable} | ${item.remote ? "paired Mac" : "local host"} | ${item.reportSha256} |`).join(`
|
|
22493
|
+
`);
|
|
22494
|
+
return `# AbsoluteJS mobile release certification
|
|
22495
|
+
|
|
22496
|
+
- Status: CERTIFIED
|
|
22497
|
+
- Certification: ${certification.certificationId}
|
|
22498
|
+
- Generated: ${certification.generatedAt}
|
|
22499
|
+
- Platform: ${release.platform}
|
|
22500
|
+
- Engine: ${release.engine}
|
|
22501
|
+
- Requirement: ${certification.requirement}
|
|
22502
|
+
- Evidence strength: ${certification.strength}
|
|
22503
|
+
- Release: ${release.releaseId}
|
|
22504
|
+
- Artifact SHA-256: ${release.artifactSha256}
|
|
22505
|
+
- Runtime fingerprint: ${release.runtime}
|
|
22506
|
+
- Embedded app build: ${release.appBuild}
|
|
22507
|
+
- Version/build: ${version}
|
|
22508
|
+
|
|
22509
|
+
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.
|
|
22510
|
+
|
|
22511
|
+
| Strength | Network unavailable | Execution | Report SHA-256 |
|
|
22512
|
+
| --- | --- | --- | --- |
|
|
22513
|
+
${evidence}
|
|
22514
|
+
`;
|
|
22515
|
+
};
|
|
22516
|
+
var verifyAbsoluteMobileReleaseCertification = (certification, release, requirement) => {
|
|
22517
|
+
const parsed = parseCertification(certification);
|
|
22518
|
+
const expected = releaseIdentity(release);
|
|
22519
|
+
if (JSON.stringify(parsed.release) !== JSON.stringify(expected))
|
|
22520
|
+
throw new TypeError("Mobile release certification was invalidated by a release identity, artifact, runtime, signing, version, build, or embedded-bundle change.");
|
|
22521
|
+
if (requirement) {
|
|
22522
|
+
validateRequirement(expected.platform, requirement);
|
|
22523
|
+
if (!satisfiesRequirement(parsed.strength, requirement))
|
|
22524
|
+
throw new TypeError(`Mobile release certification strength ${parsed.strength} does not satisfy required ${requirement} policy.`);
|
|
22525
|
+
}
|
|
22526
|
+
return parsed;
|
|
22527
|
+
};
|
|
22528
|
+
var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification, outputDirectory) => {
|
|
22529
|
+
const root = resolve8(projectRoot);
|
|
22530
|
+
const parent = resolve8(root, outputDirectory ?? join13(".absolutejs", "mobile", "certifications", certification.release.platform, certification.release.releaseId));
|
|
22531
|
+
if (!inside3(root, parent))
|
|
22532
|
+
throw new TypeError("Mobile certification output must remain inside the project.");
|
|
22533
|
+
await mkdir11(parent, { recursive: true });
|
|
22534
|
+
const destination = join13(parent, certification.certificationId);
|
|
22535
|
+
const paths = {
|
|
22536
|
+
directory: destination,
|
|
22537
|
+
jsonPath: join13(destination, "certification.json"),
|
|
22538
|
+
markdownPath: join13(destination, "certification.md")
|
|
22539
|
+
};
|
|
22540
|
+
const json = `${JSON.stringify(certification, null, 2)}
|
|
22541
|
+
`;
|
|
22542
|
+
const markdown = renderAbsoluteMobileReleaseCertification(certification);
|
|
22543
|
+
const verifyExisting = async () => {
|
|
22544
|
+
const [existingJson, existingMarkdown] = await Promise.all([
|
|
22545
|
+
readFile15(paths.jsonPath, "utf8"),
|
|
22546
|
+
readFile15(paths.markdownPath, "utf8")
|
|
22547
|
+
]);
|
|
22548
|
+
if (existingJson !== json || existingMarkdown !== markdown)
|
|
22549
|
+
throw new TypeError(`Immutable mobile certification ${certification.certificationId} already exists with different content.`);
|
|
22550
|
+
};
|
|
22551
|
+
if (await exists4(destination)) {
|
|
22552
|
+
await verifyExisting();
|
|
22553
|
+
return paths;
|
|
22554
|
+
}
|
|
22555
|
+
const staging = await mkdtemp4(join13(parent, ".certification-"));
|
|
22556
|
+
try {
|
|
22557
|
+
await Promise.all([
|
|
22558
|
+
writeFile12(join13(staging, "certification.json"), json, {
|
|
22559
|
+
flag: "wx"
|
|
22560
|
+
}),
|
|
22561
|
+
writeFile12(join13(staging, "certification.md"), markdown, {
|
|
22562
|
+
flag: "wx"
|
|
22563
|
+
})
|
|
22564
|
+
]);
|
|
22565
|
+
await rename10(staging, destination).catch(async (error) => {
|
|
22566
|
+
if (await exists4(destination))
|
|
22567
|
+
return;
|
|
22568
|
+
throw error;
|
|
22569
|
+
});
|
|
22570
|
+
} finally {
|
|
22571
|
+
await rm7(staging, { force: true, recursive: true });
|
|
22572
|
+
}
|
|
22573
|
+
await verifyExisting();
|
|
22574
|
+
return paths;
|
|
22575
|
+
};
|
|
22576
|
+
|
|
22173
22577
|
// src/mobile/releasePublisher.ts
|
|
22174
|
-
import { access as
|
|
22175
|
-
import { isAbsolute as
|
|
22578
|
+
import { access as access9 } from "fs/promises";
|
|
22579
|
+
import { isAbsolute as isAbsolute6, relative as relative8, resolve as resolve9, sep as sep6 } from "path";
|
|
22176
22580
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
22177
22581
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
22178
22582
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -22195,24 +22599,24 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
22195
22599
|
}
|
|
22196
22600
|
return versionCode;
|
|
22197
22601
|
};
|
|
22198
|
-
var
|
|
22199
|
-
var isPublisher = (value) =>
|
|
22602
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22603
|
+
var isPublisher = (value) => isRecord7(value) && typeof value.publish === "function";
|
|
22200
22604
|
var publisherModulePath = (projectRoot, requested) => {
|
|
22201
|
-
const root =
|
|
22202
|
-
const path =
|
|
22203
|
-
const projectRelative =
|
|
22204
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
22605
|
+
const root = resolve9(projectRoot);
|
|
22606
|
+
const path = resolve9(root, requested);
|
|
22607
|
+
const projectRelative = relative8(root, path);
|
|
22608
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute6(projectRelative)) {
|
|
22205
22609
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
22206
22610
|
}
|
|
22207
22611
|
return path;
|
|
22208
22612
|
};
|
|
22209
22613
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
22210
22614
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
22211
|
-
await
|
|
22615
|
+
await access9(modulePath).catch(() => {
|
|
22212
22616
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
22213
22617
|
});
|
|
22214
22618
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
22215
|
-
const publisher =
|
|
22619
|
+
const publisher = isRecord7(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
22216
22620
|
if (!isPublisher(publisher)) {
|
|
22217
22621
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
22218
22622
|
}
|
|
@@ -22270,8 +22674,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
22270
22674
|
};
|
|
22271
22675
|
|
|
22272
22676
|
// src/mobile/mobileInspect.ts
|
|
22273
|
-
import { access as
|
|
22274
|
-
import { join as
|
|
22677
|
+
import { access as access10, readFile as readFile16 } from "fs/promises";
|
|
22678
|
+
import { join as join14, relative as relative9, resolve as resolve10 } from "path";
|
|
22275
22679
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
22276
22680
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
22277
22681
|
"@absolutejs/absolute",
|
|
@@ -22286,19 +22690,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
|
|
|
22286
22690
|
]);
|
|
22287
22691
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22288
22692
|
var portablePath2 = (projectRoot, path) => {
|
|
22289
|
-
const value =
|
|
22693
|
+
const value = relative9(resolve10(projectRoot), resolve10(path)).replaceAll("\\", "/");
|
|
22290
22694
|
return value || ".";
|
|
22291
22695
|
};
|
|
22292
22696
|
var pathExists4 = async (path) => {
|
|
22293
22697
|
try {
|
|
22294
|
-
await
|
|
22698
|
+
await access10(path);
|
|
22295
22699
|
return true;
|
|
22296
22700
|
} catch {
|
|
22297
22701
|
return false;
|
|
22298
22702
|
}
|
|
22299
22703
|
};
|
|
22300
22704
|
var readObject2 = async (path) => {
|
|
22301
|
-
const value = JSON.parse(await
|
|
22705
|
+
const value = JSON.parse(await readFile16(path, "utf8"));
|
|
22302
22706
|
if (!isObject2(value))
|
|
22303
22707
|
throw new TypeError("JSON root must be an object.");
|
|
22304
22708
|
return value;
|
|
@@ -22310,13 +22714,13 @@ var addPackageDeclarations = (declarations, value) => {
|
|
|
22310
22714
|
declarations.set(name, declared);
|
|
22311
22715
|
};
|
|
22312
22716
|
var packageInspections = async (projectRoot, additionalNames) => {
|
|
22313
|
-
const project = await readObject2(
|
|
22717
|
+
const project = await readObject2(join14(projectRoot, "package.json"));
|
|
22314
22718
|
const declarations = new Map;
|
|
22315
22719
|
for (const field2 of ["dependencies", "devDependencies"])
|
|
22316
22720
|
addPackageDeclarations(declarations, project[field2]);
|
|
22317
22721
|
const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
|
|
22318
22722
|
return Promise.all(names.map(async (name) => {
|
|
22319
|
-
const installedManifest = await readObject2(
|
|
22723
|
+
const installedManifest = await readObject2(join14(projectRoot, "node_modules", name, "package.json")).catch(() => {
|
|
22320
22724
|
return;
|
|
22321
22725
|
});
|
|
22322
22726
|
const installed = installedManifest?.version;
|
|
@@ -22375,7 +22779,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
|
|
|
22375
22779
|
},
|
|
22376
22780
|
format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
|
|
22377
22781
|
nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
|
|
22378
|
-
const path =
|
|
22782
|
+
const path = join14(config.nativeProjectDirectory, platform);
|
|
22379
22783
|
return {
|
|
22380
22784
|
initialized: await pathExists4(path),
|
|
22381
22785
|
path: portablePath2(projectRoot, path),
|
|
@@ -22432,8 +22836,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
22432
22836
|
|
|
22433
22837
|
// src/mobile/ciWorkflow.ts
|
|
22434
22838
|
import { existsSync } from "fs";
|
|
22435
|
-
import { access as
|
|
22436
|
-
import { dirname as dirname10, extname as extname2, relative as
|
|
22839
|
+
import { access as access11, mkdir as mkdir12, readFile as readFile17, writeFile as writeFile13 } from "fs/promises";
|
|
22840
|
+
import { dirname as dirname10, extname as extname2, relative as relative10, resolve as resolve11, sep as sep7 } from "path";
|
|
22437
22841
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
22438
22842
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
22439
22843
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -22452,9 +22856,9 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
22452
22856
|
"APP_STORE_CONNECT_KEY_ID",
|
|
22453
22857
|
"APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
|
|
22454
22858
|
]);
|
|
22455
|
-
var
|
|
22859
|
+
var exists5 = async (path) => {
|
|
22456
22860
|
try {
|
|
22457
|
-
await
|
|
22861
|
+
await access11(path);
|
|
22458
22862
|
return true;
|
|
22459
22863
|
} catch {
|
|
22460
22864
|
return false;
|
|
@@ -22462,10 +22866,10 @@ var exists4 = async (path) => {
|
|
|
22462
22866
|
};
|
|
22463
22867
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
22464
22868
|
var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
22465
|
-
const root =
|
|
22466
|
-
const path =
|
|
22467
|
-
const portable =
|
|
22468
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22869
|
+
const root = resolve11(projectRoot);
|
|
22870
|
+
const path = resolve11(root, value);
|
|
22871
|
+
const portable = relative10(root, path).replaceAll("\\", "/");
|
|
22872
|
+
if (portable === ".." || portable.startsWith(`..${sep7}`) || portable.startsWith("../") || portable === "") {
|
|
22469
22873
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
22470
22874
|
}
|
|
22471
22875
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
@@ -22475,11 +22879,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
|
22475
22879
|
return portable;
|
|
22476
22880
|
};
|
|
22477
22881
|
var workflowOutputPath = (projectRoot, value) => {
|
|
22478
|
-
const root =
|
|
22479
|
-
const workflows =
|
|
22480
|
-
const path =
|
|
22481
|
-
const portable =
|
|
22482
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22882
|
+
const root = resolve11(projectRoot);
|
|
22883
|
+
const workflows = resolve11(root, ".github/workflows");
|
|
22884
|
+
const path = resolve11(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
22885
|
+
const portable = relative10(workflows, path);
|
|
22886
|
+
if (portable === ".." || portable.startsWith(`..${sep7}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
|
|
22483
22887
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
22484
22888
|
}
|
|
22485
22889
|
return path;
|
|
@@ -22908,12 +23312,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
22908
23312
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
22909
23313
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
22910
23314
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
22911
|
-
const previous = await
|
|
23315
|
+
const previous = await exists5(path) ? await readFile17(path, "utf8") : undefined;
|
|
22912
23316
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
22913
|
-
throw new TypeError(`${
|
|
23317
|
+
throw new TypeError(`${relative10(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
22914
23318
|
if (previous !== generated.workflow) {
|
|
22915
|
-
await
|
|
22916
|
-
await
|
|
23319
|
+
await mkdir12(dirname10(path), { recursive: true });
|
|
23320
|
+
await writeFile13(path, generated.workflow);
|
|
22917
23321
|
}
|
|
22918
23322
|
return {
|
|
22919
23323
|
changed: previous !== generated.workflow,
|
|
@@ -22926,46 +23330,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
22926
23330
|
};
|
|
22927
23331
|
|
|
22928
23332
|
// src/mobile/updateSigning.ts
|
|
22929
|
-
import { createHash as
|
|
23333
|
+
import { createHash as createHash6, sign, verify } from "crypto";
|
|
22930
23334
|
import {
|
|
22931
23335
|
cp,
|
|
22932
|
-
mkdir as
|
|
22933
|
-
mkdtemp as
|
|
23336
|
+
mkdir as mkdir13,
|
|
23337
|
+
mkdtemp as mkdtemp5,
|
|
22934
23338
|
readdir as readdir6,
|
|
22935
|
-
readFile as
|
|
22936
|
-
rename as
|
|
22937
|
-
rm as
|
|
23339
|
+
readFile as readFile18,
|
|
23340
|
+
rename as rename11,
|
|
23341
|
+
rm as rm8,
|
|
22938
23342
|
stat as stat6,
|
|
22939
|
-
writeFile as
|
|
23343
|
+
writeFile as writeFile14
|
|
22940
23344
|
} from "fs/promises";
|
|
22941
|
-
import { dirname as dirname11, join as
|
|
23345
|
+
import { dirname as dirname11, join as join15, relative as relative11, resolve as resolve12 } from "path";
|
|
22942
23346
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
22943
23347
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
22944
|
-
var
|
|
23348
|
+
var sha2564 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
22945
23349
|
var listFiles = async (root, directory = root) => {
|
|
22946
23350
|
const entries = await readdir6(directory, { withFileTypes: true });
|
|
22947
23351
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
22948
|
-
const path =
|
|
23352
|
+
const path = join15(directory, entry.name);
|
|
22949
23353
|
if (entry.isDirectory())
|
|
22950
23354
|
return listFiles(root, path);
|
|
22951
23355
|
if (!entry.isFile())
|
|
22952
23356
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
22953
|
-
return [
|
|
23357
|
+
return [relative11(root, path).replaceAll("\\", "/")];
|
|
22954
23358
|
}));
|
|
22955
23359
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
22956
23360
|
};
|
|
22957
23361
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
22958
|
-
const source =
|
|
23362
|
+
const source = join15(root, path);
|
|
22959
23363
|
const [metadata, contents] = await Promise.all([
|
|
22960
23364
|
stat6(source),
|
|
22961
|
-
|
|
23365
|
+
readFile18(source)
|
|
22962
23366
|
]);
|
|
22963
|
-
return { bytes: metadata.size, path, sha256:
|
|
23367
|
+
return { bytes: metadata.size, path, sha256: sha2564(contents) };
|
|
22964
23368
|
}));
|
|
22965
|
-
var releaseIdFor = (value) => `amu_${
|
|
23369
|
+
var releaseIdFor = (value) => `amu_${sha2564(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
22966
23370
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
22967
|
-
const bundleDirectory =
|
|
22968
|
-
const outputRoot =
|
|
23371
|
+
const bundleDirectory = resolve12(options.bundleDirectory);
|
|
23372
|
+
const outputRoot = resolve12(options.outputDirectory);
|
|
22969
23373
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
22970
23374
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
22971
23375
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -22993,30 +23397,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
22993
23397
|
value: signature.toString("base64")
|
|
22994
23398
|
}
|
|
22995
23399
|
});
|
|
22996
|
-
await
|
|
22997
|
-
const outputDirectory =
|
|
22998
|
-
const staging = await
|
|
23400
|
+
await mkdir13(outputRoot, { recursive: true });
|
|
23401
|
+
const outputDirectory = join15(outputRoot, manifest.releaseId);
|
|
23402
|
+
const staging = await mkdtemp5(join15(outputRoot, ".stage-"));
|
|
22999
23403
|
try {
|
|
23000
|
-
await cp(bundleDirectory,
|
|
23404
|
+
await cp(bundleDirectory, join15(staging, UPDATE_FILES_DIRECTORY), {
|
|
23001
23405
|
force: true,
|
|
23002
23406
|
recursive: true
|
|
23003
23407
|
});
|
|
23004
|
-
await
|
|
23408
|
+
await writeFile14(join15(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
23005
23409
|
`);
|
|
23006
|
-
await
|
|
23410
|
+
await rename11(staging, outputDirectory);
|
|
23007
23411
|
} catch (error) {
|
|
23008
|
-
await
|
|
23412
|
+
await rm8(staging, { force: true, recursive: true });
|
|
23009
23413
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
23010
23414
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
23011
23415
|
throw error;
|
|
23012
23416
|
}
|
|
23013
23417
|
return {
|
|
23014
23418
|
manifest,
|
|
23015
|
-
manifestPath:
|
|
23419
|
+
manifestPath: join15(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
23016
23420
|
outputDirectory
|
|
23017
23421
|
};
|
|
23018
23422
|
};
|
|
23019
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
23423
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile18(join15(resolve12(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
23020
23424
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
23021
23425
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
23022
23426
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -23026,8 +23430,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
23026
23430
|
};
|
|
23027
23431
|
|
|
23028
23432
|
// src/mobile/expoUpdate.ts
|
|
23029
|
-
import { access as
|
|
23030
|
-
import { dirname as dirname12, extname as extname3, join as
|
|
23433
|
+
import { access as access12, mkdir as mkdir14, readFile as readFile19, writeFile as writeFile15 } from "fs/promises";
|
|
23434
|
+
import { dirname as dirname12, extname as extname3, join as join16 } from "path";
|
|
23031
23435
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
23032
23436
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
23033
23437
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -23093,7 +23497,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
23093
23497
|
};
|
|
23094
23498
|
};
|
|
23095
23499
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
23096
|
-
const metadata = JSON.parse(await
|
|
23500
|
+
const metadata = JSON.parse(await readFile19(join16(options.exportDirectory, "metadata.json"), "utf8"));
|
|
23097
23501
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
23098
23502
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
23099
23503
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -23104,33 +23508,33 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
23104
23508
|
runtimeVersion: options.runtimeVersion
|
|
23105
23509
|
});
|
|
23106
23510
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
23107
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
23511
|
+
await Promise.all(referenced.map(({ path }) => access12(join16(options.exportDirectory, path)).catch(() => {
|
|
23108
23512
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
23109
23513
|
})));
|
|
23110
|
-
const destination =
|
|
23111
|
-
await
|
|
23112
|
-
await
|
|
23514
|
+
const destination = join16(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
23515
|
+
await mkdir14(dirname12(destination), { recursive: true });
|
|
23516
|
+
await writeFile15(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
23113
23517
|
`);
|
|
23114
23518
|
return { descriptor, path: destination };
|
|
23115
23519
|
};
|
|
23116
23520
|
|
|
23117
23521
|
// src/mobile/expoCodeSigning.ts
|
|
23118
23522
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
23119
|
-
import { access as
|
|
23120
|
-
import { dirname as dirname13, isAbsolute as
|
|
23121
|
-
var
|
|
23122
|
-
var
|
|
23123
|
-
const location =
|
|
23124
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
23523
|
+
import { access as access13, mkdir as mkdir15, writeFile as writeFile16 } from "fs/promises";
|
|
23524
|
+
import { dirname as dirname13, isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
|
|
23525
|
+
var exists6 = async (path) => access13(path).then(() => true).catch(() => false);
|
|
23526
|
+
var inside4 = (root, path) => {
|
|
23527
|
+
const location = relative12(root, path);
|
|
23528
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep8}`) && !isAbsolute7(location);
|
|
23125
23529
|
};
|
|
23126
23530
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
23127
|
-
const root =
|
|
23128
|
-
const certificatePath =
|
|
23129
|
-
const privateKeyPath =
|
|
23130
|
-
const publicKeyPath =
|
|
23131
|
-
if (!
|
|
23531
|
+
const root = resolve13(options.projectRoot);
|
|
23532
|
+
const certificatePath = resolve13(root, options.certificatePath);
|
|
23533
|
+
const privateKeyPath = resolve13(root, options.privateKeyPath);
|
|
23534
|
+
const publicKeyPath = resolve13(root, options.publicKeyPath ?? resolve13(dirname13(privateKeyPath), "public-key.pem"));
|
|
23535
|
+
if (!inside4(root, certificatePath))
|
|
23132
23536
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
23133
|
-
if (
|
|
23537
|
+
if (inside4(root, privateKeyPath) || inside4(root, publicKeyPath))
|
|
23134
23538
|
throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
|
|
23135
23539
|
const commonName = options.commonName.trim();
|
|
23136
23540
|
if (!commonName || commonName.length > 64)
|
|
@@ -23139,7 +23543,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
23139
23543
|
if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
|
|
23140
23544
|
throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
|
|
23141
23545
|
const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
|
|
23142
|
-
exists: await
|
|
23546
|
+
exists: await exists6(path),
|
|
23143
23547
|
path
|
|
23144
23548
|
})))).filter((entry) => entry.exists);
|
|
23145
23549
|
if (collisions.length > 0)
|
|
@@ -23157,11 +23561,11 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
23157
23561
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
23158
23562
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
23159
23563
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
23160
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
23564
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir15(dirname13(path), { recursive: true })));
|
|
23161
23565
|
await Promise.all([
|
|
23162
|
-
|
|
23163
|
-
|
|
23164
|
-
|
|
23566
|
+
writeFile16(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
23567
|
+
writeFile16(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
23568
|
+
writeFile16(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
|
|
23165
23569
|
]);
|
|
23166
23570
|
return {
|
|
23167
23571
|
certificatePath,
|
|
@@ -23172,8 +23576,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
23172
23576
|
};
|
|
23173
23577
|
|
|
23174
23578
|
// src/mobile/updatePublisher.ts
|
|
23175
|
-
import { access as
|
|
23176
|
-
import { isAbsolute as
|
|
23579
|
+
import { access as access14 } from "fs/promises";
|
|
23580
|
+
import { isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14, sep as sep9 } from "path";
|
|
23177
23581
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
23178
23582
|
var lifecycleMethod = (publisher, name) => {
|
|
23179
23583
|
const method = publisher[name];
|
|
@@ -23282,16 +23686,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
23282
23686
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23283
23687
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
23284
23688
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
23285
|
-
const root =
|
|
23286
|
-
const path =
|
|
23287
|
-
const projectRelative =
|
|
23288
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
23689
|
+
const root = resolve14(projectRoot);
|
|
23690
|
+
const path = resolve14(root, requested);
|
|
23691
|
+
const projectRelative = relative13(root, path);
|
|
23692
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep9}`) || isAbsolute8(projectRelative))
|
|
23289
23693
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
23290
23694
|
return path;
|
|
23291
23695
|
};
|
|
23292
23696
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
23293
23697
|
const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
|
|
23294
|
-
await
|
|
23698
|
+
await access14(modulePath).catch(() => {
|
|
23295
23699
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
23296
23700
|
});
|
|
23297
23701
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -23346,10 +23750,10 @@ var rollbackAbsoluteMobileUpdate = async (options) => {
|
|
|
23346
23750
|
var NOT_FOUND4 = -1;
|
|
23347
23751
|
var ANDROID_EMULATOR_BOOT_POLL_MS = 500;
|
|
23348
23752
|
var ANDROID_EMULATOR_BOOT_TIMEOUT_MS = 180000;
|
|
23349
|
-
var
|
|
23753
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23350
23754
|
var expoPublicConfig = (config) => {
|
|
23351
23755
|
const result = { ...config };
|
|
23352
|
-
if (!
|
|
23756
|
+
if (!isRecord8(result.updates))
|
|
23353
23757
|
return result;
|
|
23354
23758
|
const {
|
|
23355
23759
|
codeSigningCertificate: _codeSigningCertificate,
|
|
@@ -23389,13 +23793,13 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
23389
23793
|
];
|
|
23390
23794
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
23391
23795
|
var directProjectPackages = async (projectRoot) => {
|
|
23392
|
-
const manifest = JSON.parse(await
|
|
23393
|
-
if (!
|
|
23796
|
+
const manifest = JSON.parse(await readFile20(join17(projectRoot, "package.json"), "utf8"));
|
|
23797
|
+
if (!isRecord8(manifest))
|
|
23394
23798
|
throw new TypeError("Application package.json must contain an object.");
|
|
23395
23799
|
const names = new Set;
|
|
23396
23800
|
for (const field2 of ["dependencies", "devDependencies"]) {
|
|
23397
23801
|
const dependencies = Reflect.get(manifest, field2);
|
|
23398
|
-
if (
|
|
23802
|
+
if (isRecord8(dependencies))
|
|
23399
23803
|
for (const name of Object.keys(dependencies))
|
|
23400
23804
|
names.add(name);
|
|
23401
23805
|
}
|
|
@@ -23403,8 +23807,8 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
23403
23807
|
};
|
|
23404
23808
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
23405
23809
|
try {
|
|
23406
|
-
const manifest = JSON.parse(await
|
|
23407
|
-
return
|
|
23810
|
+
const manifest = JSON.parse(await readFile20(join17(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
23811
|
+
return isRecord8(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
23408
23812
|
} catch {
|
|
23409
23813
|
return;
|
|
23410
23814
|
}
|
|
@@ -23467,9 +23871,9 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
23467
23871
|
};
|
|
23468
23872
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
23469
23873
|
var expoExecutable = async (project) => {
|
|
23470
|
-
const executable =
|
|
23874
|
+
const executable = join17(project, "node_modules", ".bin", "expo");
|
|
23471
23875
|
try {
|
|
23472
|
-
await
|
|
23876
|
+
await access15(executable);
|
|
23473
23877
|
return executable;
|
|
23474
23878
|
} catch {
|
|
23475
23879
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -23501,14 +23905,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
23501
23905
|
};
|
|
23502
23906
|
var ensureExpoPackages = async (project, args) => {
|
|
23503
23907
|
try {
|
|
23504
|
-
const manifest = JSON.parse(await
|
|
23908
|
+
const manifest = JSON.parse(await readFile20(join17(project, "package.json"), "utf8"));
|
|
23505
23909
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
23506
23910
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
23507
23911
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
23508
23912
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
23509
23913
|
if (typeof expected !== "string")
|
|
23510
23914
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
23511
|
-
const installed = JSON.parse(await
|
|
23915
|
+
const installed = JSON.parse(await readFile20(join17(project, "node_modules", name, "package.json"), "utf8"));
|
|
23512
23916
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
23513
23917
|
if (typeof actual !== "string")
|
|
23514
23918
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -23706,7 +24110,7 @@ var sync = async (args) => {
|
|
|
23706
24110
|
};
|
|
23707
24111
|
var associations = async (args) => {
|
|
23708
24112
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
23709
|
-
const outputDirectory =
|
|
24113
|
+
const outputDirectory = resolve15(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
23710
24114
|
if (args.includes("--verify")) {
|
|
23711
24115
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
23712
24116
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -23758,7 +24162,7 @@ var generateGithubCi = async (args) => {
|
|
|
23758
24162
|
const publicResult = {
|
|
23759
24163
|
changed: result.changed,
|
|
23760
24164
|
format: result.format,
|
|
23761
|
-
path:
|
|
24165
|
+
path: relative14(projectRoot, result.path).replaceAll("\\", "/"),
|
|
23762
24166
|
platforms: result.platforms,
|
|
23763
24167
|
publishing: result.publishing,
|
|
23764
24168
|
requiredSecrets: result.requiredSecrets
|
|
@@ -23869,9 +24273,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23869
24273
|
temporaryDirectory: undefined
|
|
23870
24274
|
};
|
|
23871
24275
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
23872
|
-
const temporaryParent =
|
|
23873
|
-
await
|
|
23874
|
-
const temporaryDirectory = await
|
|
24276
|
+
const temporaryParent = join17(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
24277
|
+
await mkdir16(temporaryParent, { recursive: true });
|
|
24278
|
+
const temporaryDirectory = await mkdtemp6(join17(temporaryParent, ".stage-"));
|
|
23875
24279
|
try {
|
|
23876
24280
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
23877
24281
|
"export",
|
|
@@ -23883,9 +24287,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23883
24287
|
temporaryDirectory,
|
|
23884
24288
|
"--clear"
|
|
23885
24289
|
], { production: true });
|
|
23886
|
-
const generatedApp = JSON.parse(await
|
|
23887
|
-
const expoConfig =
|
|
23888
|
-
if (!
|
|
24290
|
+
const generatedApp = JSON.parse(await readFile20(join17(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
24291
|
+
const expoConfig = isRecord8(generatedApp) ? generatedApp.expo : undefined;
|
|
24292
|
+
if (!isRecord8(expoConfig))
|
|
23889
24293
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
23890
24294
|
const publicExpoConfig = expoPublicConfig(expoConfig);
|
|
23891
24295
|
const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
|
|
@@ -23901,7 +24305,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23901
24305
|
temporaryDirectory
|
|
23902
24306
|
};
|
|
23903
24307
|
} catch (error) {
|
|
23904
|
-
await
|
|
24308
|
+
await rm9(temporaryDirectory, { force: true, recursive: true });
|
|
23905
24309
|
throw error;
|
|
23906
24310
|
}
|
|
23907
24311
|
};
|
|
@@ -23926,11 +24330,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
23926
24330
|
let expoExportDirectory;
|
|
23927
24331
|
try {
|
|
23928
24332
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
23929
|
-
const embedded = JSON.parse(await
|
|
23930
|
-
const runtimeFingerprint =
|
|
24333
|
+
const embedded = JSON.parse(await readFile20(join17(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
24334
|
+
const runtimeFingerprint = isRecord8(embedded) ? embedded.nativeRuntime : undefined;
|
|
23931
24335
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23932
24336
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23933
|
-
const privateKey = await
|
|
24337
|
+
const privateKey = await readFile20(resolve15(projectRoot, signingKeyPath));
|
|
23934
24338
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
23935
24339
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
23936
24340
|
format: "der",
|
|
@@ -23951,7 +24355,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23951
24355
|
channel: mobile.updates.channel,
|
|
23952
24356
|
classification,
|
|
23953
24357
|
keyId,
|
|
23954
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
24358
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join17(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
23955
24359
|
privateKey,
|
|
23956
24360
|
runtimeFingerprint
|
|
23957
24361
|
});
|
|
@@ -23963,7 +24367,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23963
24367
|
return result;
|
|
23964
24368
|
} finally {
|
|
23965
24369
|
if (expoExportDirectory)
|
|
23966
|
-
await
|
|
24370
|
+
await rm9(expoExportDirectory, { force: true, recursive: true });
|
|
23967
24371
|
sendTelemetryEvent("mobile:update-build", {
|
|
23968
24372
|
classification: classification ?? "invalid",
|
|
23969
24373
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -23993,7 +24397,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
23993
24397
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
23994
24398
|
...validityYears === undefined ? {} : { validityYears }
|
|
23995
24399
|
});
|
|
23996
|
-
const certificatePath =
|
|
24400
|
+
const certificatePath = relative14(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
23997
24401
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
23998
24402
|
engine: "expo",
|
|
23999
24403
|
validityYears: validityYears ?? 10
|
|
@@ -24055,7 +24459,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
24055
24459
|
publicKeys: mobile.updates.publicKeys,
|
|
24056
24460
|
storage: requestedStorage
|
|
24057
24461
|
});
|
|
24058
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
24462
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative14(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
24059
24463
|
if (requestedStorage === "local")
|
|
24060
24464
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
24061
24465
|
else
|
|
@@ -24532,7 +24936,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
24532
24936
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
24533
24937
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
24534
24938
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
24535
|
-
console.log(`Metadata: ${
|
|
24939
|
+
console.log(`Metadata: ${join17(release.releaseRoot, "release.json")}`);
|
|
24536
24940
|
return release;
|
|
24537
24941
|
} finally {
|
|
24538
24942
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -24692,7 +25096,7 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
24692
25096
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
24693
25097
|
if (release.registeredArtifactPath)
|
|
24694
25098
|
console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
|
|
24695
|
-
console.log(`Metadata: ${
|
|
25099
|
+
console.log(`Metadata: ${join17(release.releaseRoot, "release.json")}`);
|
|
24696
25100
|
return release;
|
|
24697
25101
|
} finally {
|
|
24698
25102
|
stopListeningForCancellation();
|
|
@@ -24804,7 +25208,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24804
25208
|
checks.push({
|
|
24805
25209
|
id: "sync.storage-schema",
|
|
24806
25210
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
24807
|
-
path:
|
|
25211
|
+
path: join17(projectRoot, "package.json"),
|
|
24808
25212
|
platform: "host",
|
|
24809
25213
|
status: "pass"
|
|
24810
25214
|
});
|
|
@@ -24812,7 +25216,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24812
25216
|
checks.push({
|
|
24813
25217
|
id: "sync.storage-schema",
|
|
24814
25218
|
label: "Offline schema metadata is invalid",
|
|
24815
|
-
path:
|
|
25219
|
+
path: join17(projectRoot, "package.json"),
|
|
24816
25220
|
platform: "host",
|
|
24817
25221
|
remediation: error instanceof Error ? error.message : String(error),
|
|
24818
25222
|
status: "fail"
|
|
@@ -24900,7 +25304,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
24900
25304
|
}
|
|
24901
25305
|
return { https: args.includes("--https"), port };
|
|
24902
25306
|
}
|
|
24903
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
25307
|
+
const instances = listLiveInstances().filter((instance2) => resolve15(instance2.cwd) === resolve15(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24904
25308
|
if (instances.length !== 1) {
|
|
24905
25309
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
|
|
24906
25310
|
}
|
|
@@ -24994,8 +25398,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
24994
25398
|
return waitForBoot();
|
|
24995
25399
|
};
|
|
24996
25400
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
24997
|
-
const root =
|
|
24998
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
25401
|
+
const root = resolve15(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
25402
|
+
if (root !== projectRoot && !root.startsWith(`${resolve15(projectRoot)}/`)) {
|
|
24999
25403
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
25000
25404
|
}
|
|
25001
25405
|
return root;
|
|
@@ -25023,12 +25427,12 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
25023
25427
|
});
|
|
25024
25428
|
};
|
|
25025
25429
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
25026
|
-
await
|
|
25027
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
25430
|
+
await mkdir16(options.artifactRoot, { recursive: true });
|
|
25431
|
+
const screenshot = options.session ? await options.session.screenshot(join17(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
25028
25432
|
return;
|
|
25029
25433
|
}) : undefined;
|
|
25030
|
-
const diagnosticPath =
|
|
25031
|
-
await
|
|
25434
|
+
const diagnosticPath = join17(options.artifactRoot, "android-failure.json");
|
|
25435
|
+
await writeFile17(diagnosticPath, `${JSON.stringify({
|
|
25032
25436
|
diagnostics: options.session?.diagnostics ?? [],
|
|
25033
25437
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
25034
25438
|
platform: "android",
|
|
@@ -25049,7 +25453,7 @@ var captureAndroidReleaseScreenshot = async (adb2, serial, destination) => {
|
|
|
25049
25453
|
]);
|
|
25050
25454
|
if (exitCode !== 0)
|
|
25051
25455
|
throw new Error(`Android release screenshot failed: ${stderr.trim() || `status ${exitCode}`}`);
|
|
25052
|
-
await
|
|
25456
|
+
await writeFile17(destination, bytes);
|
|
25053
25457
|
return destination;
|
|
25054
25458
|
};
|
|
25055
25459
|
var printAndroidReleaseAcceptance = (result, json) => {
|
|
@@ -25103,7 +25507,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
25103
25507
|
release,
|
|
25104
25508
|
serial
|
|
25105
25509
|
});
|
|
25106
|
-
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial,
|
|
25510
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join17(reportRoot, "android-release.png")) : undefined;
|
|
25107
25511
|
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
25108
25512
|
durationMs: result.durationMs,
|
|
25109
25513
|
engine: result.engine,
|
|
@@ -25150,9 +25554,9 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
25150
25554
|
platform: "android",
|
|
25151
25555
|
success: false
|
|
25152
25556
|
});
|
|
25153
|
-
await
|
|
25154
|
-
const diagnosticPath =
|
|
25155
|
-
await
|
|
25557
|
+
await mkdir16(artifactRoot, { recursive: true });
|
|
25558
|
+
const diagnosticPath = join17(artifactRoot, "android-release-failure.json");
|
|
25559
|
+
await writeFile17(diagnosticPath, `${JSON.stringify({
|
|
25156
25560
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
25157
25561
|
platform: "android",
|
|
25158
25562
|
provider: mobile.engine,
|
|
@@ -25246,7 +25650,7 @@ var testAndroid = async (args) => {
|
|
|
25246
25650
|
console.log(JSON.stringify(report, null, 2));
|
|
25247
25651
|
else
|
|
25248
25652
|
printAndroidTestReport(report);
|
|
25249
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
25653
|
+
const screenshot = reportRoot ? await session.screenshot(join17(artifactRoot, "android-emulator.png")) : undefined;
|
|
25250
25654
|
await writeRequestedAndroidReport({
|
|
25251
25655
|
adb: adb2,
|
|
25252
25656
|
args,
|
|
@@ -25315,14 +25719,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
25315
25719
|
const port = Number(explicit);
|
|
25316
25720
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
25317
25721
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
25318
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
25722
|
+
const instance2 = listLiveInstances().find((candidate) => resolve15(candidate.cwd) === resolve15(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
25319
25723
|
return {
|
|
25320
25724
|
https: instance2?.https ?? args.includes("--https"),
|
|
25321
25725
|
instance: instance2,
|
|
25322
25726
|
port
|
|
25323
25727
|
};
|
|
25324
25728
|
}
|
|
25325
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
25729
|
+
const instances = listLiveInstances().filter((instance2) => resolve15(instance2.cwd) === resolve15(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
25326
25730
|
if (instances.length !== 1)
|
|
25327
25731
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
|
|
25328
25732
|
const [instance] = instances;
|
|
@@ -25340,7 +25744,7 @@ var waitForIosHmrClient = async (options) => {
|
|
|
25340
25744
|
return;
|
|
25341
25745
|
});
|
|
25342
25746
|
const status = response?.ok ? await response.json().catch(() => null) : null;
|
|
25343
|
-
const targets =
|
|
25747
|
+
const targets = isRecord8(status) && isRecord8(status.connectedTargets) ? status.connectedTargets : undefined;
|
|
25344
25748
|
if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
|
|
25345
25749
|
return;
|
|
25346
25750
|
await Bun.sleep(100);
|
|
@@ -25466,8 +25870,8 @@ var requireCapturedCommand = (command, label) => {
|
|
|
25466
25870
|
return result;
|
|
25467
25871
|
};
|
|
25468
25872
|
var writeIosFailureArtifacts = async (options) => {
|
|
25469
|
-
await
|
|
25470
|
-
const screenshot =
|
|
25873
|
+
await mkdir16(options.artifactRoot, { recursive: true });
|
|
25874
|
+
const screenshot = join17(options.artifactRoot, "ios-failure.png");
|
|
25471
25875
|
const screenshotResult = captureCommand4([
|
|
25472
25876
|
options.xcrun,
|
|
25473
25877
|
"simctl",
|
|
@@ -25476,8 +25880,8 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
25476
25880
|
"screenshot",
|
|
25477
25881
|
screenshot
|
|
25478
25882
|
]);
|
|
25479
|
-
const diagnosticPath =
|
|
25480
|
-
await
|
|
25883
|
+
const diagnosticPath = join17(options.artifactRoot, "ios-failure.json");
|
|
25884
|
+
await writeFile17(diagnosticPath, `${JSON.stringify({
|
|
25481
25885
|
appId: options.appId,
|
|
25482
25886
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
25483
25887
|
platform: "ios",
|
|
@@ -25504,8 +25908,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
25504
25908
|
var absolutejsVersionForReport = async () => {
|
|
25505
25909
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
25506
25910
|
const versions = await Promise.all([
|
|
25507
|
-
|
|
25508
|
-
|
|
25911
|
+
resolve15(import.meta.dir, "..", "..", "package.json"),
|
|
25912
|
+
resolve15(import.meta.dir, "..", "..", "..", "package.json")
|
|
25509
25913
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
25510
25914
|
for (const version of versions) {
|
|
25511
25915
|
if (version === "unknown")
|
|
@@ -25882,9 +26286,9 @@ var testIosRelease = async (args, mobile, projectRoot) => {
|
|
|
25882
26286
|
remote: remoteProfile !== undefined,
|
|
25883
26287
|
success: false
|
|
25884
26288
|
});
|
|
25885
|
-
await
|
|
25886
|
-
const diagnosticPath =
|
|
25887
|
-
await
|
|
26289
|
+
await mkdir16(artifactRoot, { recursive: true });
|
|
26290
|
+
const diagnosticPath = join17(artifactRoot, "ios-release-failure.json");
|
|
26291
|
+
await writeFile17(diagnosticPath, `${JSON.stringify({
|
|
25888
26292
|
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
25889
26293
|
platform: "ios",
|
|
25890
26294
|
provider: mobile.engine,
|
|
@@ -25977,8 +26381,8 @@ var testIos = async (args) => {
|
|
|
25977
26381
|
mobile.appId
|
|
25978
26382
|
], "iOS app launch");
|
|
25979
26383
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
25980
|
-
await
|
|
25981
|
-
const screenshot =
|
|
26384
|
+
await mkdir16(artifactRoot, { recursive: true });
|
|
26385
|
+
const screenshot = join17(artifactRoot, "ios-simulator.png");
|
|
25982
26386
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
25983
26387
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
25984
26388
|
const report = {
|
|
@@ -26062,6 +26466,110 @@ var testIos = async (args) => {
|
|
|
26062
26466
|
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}${screenshot ? `; screenshot: ${screenshot}` : ""}`, { cause: error });
|
|
26063
26467
|
}
|
|
26064
26468
|
};
|
|
26469
|
+
var readCertifiableMobileRelease = async (projectRoot, requested) => {
|
|
26470
|
+
let androidError;
|
|
26471
|
+
try {
|
|
26472
|
+
return await readAbsoluteAndroidRelease(projectRoot, requested);
|
|
26473
|
+
} catch (error) {
|
|
26474
|
+
androidError = error;
|
|
26475
|
+
}
|
|
26476
|
+
try {
|
|
26477
|
+
return await readAbsoluteIosRelease(projectRoot, requested);
|
|
26478
|
+
} catch (iosError) {
|
|
26479
|
+
throw new TypeError(`Unable to read an immutable Android or iOS release from ${requested}. Android: ${androidError instanceof Error ? androidError.message : String(androidError)} iOS: ${iosError instanceof Error ? iosError.message : String(iosError)}`, { cause: iosError });
|
|
26480
|
+
}
|
|
26481
|
+
};
|
|
26482
|
+
var mobileCertificationRequirement = (value) => {
|
|
26483
|
+
if (value === undefined)
|
|
26484
|
+
return;
|
|
26485
|
+
if (value !== "installed" && value !== "simulator" && value !== "device" && value !== "store")
|
|
26486
|
+
throw new TypeError("mobile certify --require must be installed, simulator, device, or store.");
|
|
26487
|
+
return value;
|
|
26488
|
+
};
|
|
26489
|
+
var mobileCertificationValue = (args, flag) => {
|
|
26490
|
+
const value = valueAfter(args, flag);
|
|
26491
|
+
if (args.includes(flag) && (!value || value.startsWith("--")))
|
|
26492
|
+
throw new TypeError(`mobile certify ${flag} requires a value.`);
|
|
26493
|
+
return value;
|
|
26494
|
+
};
|
|
26495
|
+
var verifyMobileCertification = async (options) => {
|
|
26496
|
+
if (valuesAfter(options.args, "--evidence").length > 0)
|
|
26497
|
+
throw new TypeError("mobile certify --verify cannot be combined with --evidence.");
|
|
26498
|
+
const loaded = await readAbsoluteMobileReleaseCertification(options.projectRoot, options.verifyPath);
|
|
26499
|
+
const certification = verifyAbsoluteMobileReleaseCertification(loaded.certification, options.release, options.requirement);
|
|
26500
|
+
sendTelemetryEvent("mobile:release-certification", {
|
|
26501
|
+
durationMs: Math.round(performance.now() - options.startedAt),
|
|
26502
|
+
engine: options.release.metadata.engine,
|
|
26503
|
+
mode: "verify",
|
|
26504
|
+
platform: options.release.metadata.platform,
|
|
26505
|
+
requirement: options.requirement ?? certification.requirement,
|
|
26506
|
+
strength: certification.strength,
|
|
26507
|
+
success: true
|
|
26508
|
+
});
|
|
26509
|
+
if (options.args.includes("--json"))
|
|
26510
|
+
console.log(JSON.stringify(certification, null, 2));
|
|
26511
|
+
else
|
|
26512
|
+
console.log(`\u2713 ${certification.strength} certification ${certification.certificationId} still matches ${certification.release.releaseId}.`);
|
|
26513
|
+
return certification;
|
|
26514
|
+
};
|
|
26515
|
+
var createMobileCertification = async (options) => {
|
|
26516
|
+
const evidencePaths = valuesAfter(options.args, "--evidence");
|
|
26517
|
+
const certification = await createAbsoluteMobileReleaseCertification({
|
|
26518
|
+
evidencePaths,
|
|
26519
|
+
projectRoot: options.projectRoot,
|
|
26520
|
+
release: options.release,
|
|
26521
|
+
...options.requirement ? { requirement: options.requirement } : {}
|
|
26522
|
+
});
|
|
26523
|
+
const output = await writeAbsoluteMobileReleaseCertification(options.projectRoot, certification, mobileCertificationValue(options.args, "--outdir"));
|
|
26524
|
+
sendTelemetryEvent("mobile:release-certification", {
|
|
26525
|
+
durationMs: Math.round(performance.now() - options.startedAt),
|
|
26526
|
+
engine: options.release.metadata.engine,
|
|
26527
|
+
evidenceCount: certification.evidence.length,
|
|
26528
|
+
mode: "create",
|
|
26529
|
+
platform: options.release.metadata.platform,
|
|
26530
|
+
requirement: certification.requirement,
|
|
26531
|
+
strength: certification.strength,
|
|
26532
|
+
success: true
|
|
26533
|
+
});
|
|
26534
|
+
if (options.args.includes("--json"))
|
|
26535
|
+
console.log(JSON.stringify(certification, null, 2));
|
|
26536
|
+
else {
|
|
26537
|
+
console.log(`\u2713 Certified immutable ${certification.release.platform} release ${certification.release.releaseId} at ${certification.strength} strength.`);
|
|
26538
|
+
console.log(`Certification: ${output.jsonPath}`);
|
|
26539
|
+
console.log(`Summary: ${output.markdownPath}`);
|
|
26540
|
+
}
|
|
26541
|
+
return certification;
|
|
26542
|
+
};
|
|
26543
|
+
var certifyMobileRelease = async (args) => {
|
|
26544
|
+
const [requested] = args;
|
|
26545
|
+
if (!requested || requested.startsWith("--"))
|
|
26546
|
+
throw new TypeError("mobile certify requires a release directory or release.json path.");
|
|
26547
|
+
const projectRoot = process.cwd();
|
|
26548
|
+
const release = await readCertifiableMobileRelease(projectRoot, requested);
|
|
26549
|
+
const requirement = mobileCertificationRequirement(mobileCertificationValue(args, "--require"));
|
|
26550
|
+
const verifyPath = mobileCertificationValue(args, "--verify");
|
|
26551
|
+
const startedAt = performance.now();
|
|
26552
|
+
try {
|
|
26553
|
+
const options = {
|
|
26554
|
+
args,
|
|
26555
|
+
projectRoot,
|
|
26556
|
+
release,
|
|
26557
|
+
...requirement ? { requirement } : {},
|
|
26558
|
+
startedAt
|
|
26559
|
+
};
|
|
26560
|
+
return verifyPath ? await verifyMobileCertification({ ...options, verifyPath }) : await createMobileCertification(options);
|
|
26561
|
+
} catch (error) {
|
|
26562
|
+
sendTelemetryEvent("mobile:release-certification", {
|
|
26563
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
26564
|
+
engine: release.metadata.engine,
|
|
26565
|
+
mode: verifyPath ? "verify" : "create",
|
|
26566
|
+
platform: release.metadata.platform,
|
|
26567
|
+
...requirement ? { requirement } : {},
|
|
26568
|
+
success: false
|
|
26569
|
+
});
|
|
26570
|
+
throw error;
|
|
26571
|
+
}
|
|
26572
|
+
};
|
|
26065
26573
|
var runMobile = async (args) => {
|
|
26066
26574
|
const [command] = args;
|
|
26067
26575
|
if (command === "pair") {
|
|
@@ -26100,6 +26608,10 @@ var runMobile = async (args) => {
|
|
|
26100
26608
|
await inspectMobile(args.slice(1));
|
|
26101
26609
|
return;
|
|
26102
26610
|
}
|
|
26611
|
+
if (command === "certify") {
|
|
26612
|
+
await certifyMobileRelease(args.slice(1));
|
|
26613
|
+
return;
|
|
26614
|
+
}
|
|
26103
26615
|
if (command === "test" && args[1] === "android") {
|
|
26104
26616
|
await testAndroid(args.slice(2));
|
|
26105
26617
|
return;
|
|
@@ -26164,7 +26676,7 @@ var runMobile = async (args) => {
|
|
|
26164
26676
|
await publishIos(args.slice(2));
|
|
26165
26677
|
return;
|
|
26166
26678
|
}
|
|
26167
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
|
|
26679
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | certify <release-dir> [--evidence report-dir]... [--require installed|simulator|device|store] [--outdir dir] [--json] | certify <release-dir> --verify certification-dir [--require installed|simulator|device|store] [--json] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
|
|
26168
26680
|
};
|
|
26169
26681
|
export {
|
|
26170
26682
|
runMobile
|