@absolutejs/absolute 0.20.0-beta.104 → 0.20.0-beta.106
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 +14 -0
- package/changelog.json +45 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/{compile-g8vmvr0w.js → compile-hsbs1xf6.js} +2 -2
- package/dist/cli/{dev-m4r4zs2k.js → dev-f6b6hkke.js} +4 -4
- package/dist/cli/{expoProject-780thyfd.js → expoProject-zkprmjjz.js} +1 -1
- package/dist/cli/{index-d5fr1xp3.js → index-0zcebhm4.js} +59 -1
- package/dist/cli/{index-wpfvpsk7.js → index-5skbydh0.js} +1 -1
- package/dist/cli/{index-67kedtn7.js → index-hrzd7e5x.js} +1 -1
- package/dist/cli/{index-3sdvzrv2.js → index-jf1f6npt.js} +233 -4
- package/dist/cli/{index-27hhdgtg.js → index-mazxtfh4.js} +1 -1
- package/dist/cli/{index-qxnptvwn.js → index-q1d4ns4n.js} +100 -9
- package/dist/cli/index.js +5 -5
- package/dist/cli/{iosNativeWatcher-3k715hq0.js → iosNativeWatcher-t6gv0p1w.js} +2 -2
- package/dist/cli/{iosSimulatorController-dr98a0p9.js → iosSimulatorController-5n2a9fxv.js} +2 -2
- package/dist/cli/{mobile-njmnd7vc.js → mobile-w4my2dc7.js} +1008 -173
- package/dist/cli/{remoteMacProtocol-kqvkp0qd.js → remoteMacProtocol-dybpgazw.js} +6 -2
- package/dist/cli/{start-39aqmr5n.js → start-5grq7nfr.js} +3 -3
- package/dist/mobile/index.js +1445 -575
- package/dist/mobile/index.js.map +10 -9
- package/dist/mobile/remoteMacAgentEntry.js +341 -294
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/iosRelease.d.ts +31 -7
- package/dist/src/mobile/iosReleaseAcceptance.d.ts +66 -0
- package/dist/src/mobile/iosTestReport.d.ts +1 -0
- package/dist/src/mobile/nativeReleaseReadiness.d.ts +1 -1
- package/dist/src/mobile/nativeTestReport.d.ts +16 -0
- package/dist/src/mobile/remoteMacProtocol.d.ts +51 -4
- package/dist/src/mobile/remoteMacWire.d.ts +1 -1
- package/dist/src/mobile/shellReleaseReadiness.d.ts +1 -1
- package/package.json +1 -1
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
} from "./index-zh6hhrwy.js";
|
|
8
8
|
import {
|
|
9
9
|
start
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-mazxtfh4.js";
|
|
11
11
|
import {
|
|
12
12
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
13
13
|
ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
|
|
14
14
|
resolveAbsoluteMobileRoute
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-hrzd7e5x.js";
|
|
16
16
|
import"./index-bzs19w3r.js";
|
|
17
17
|
import"./index-s2tazax7.js";
|
|
18
18
|
import {
|
|
@@ -29,16 +29,19 @@ import {
|
|
|
29
29
|
inspectAbsoluteRemoteMacWorkspace,
|
|
30
30
|
listAbsoluteRemoteMacProfiles,
|
|
31
31
|
pairAbsoluteRemoteMac,
|
|
32
|
-
removeAbsoluteRemoteMacProfile
|
|
33
|
-
|
|
32
|
+
removeAbsoluteRemoteMacProfile,
|
|
33
|
+
runAbsoluteRemoteIosReleaseAcceptance
|
|
34
|
+
} from "./index-jf1f6npt.js";
|
|
34
35
|
import {
|
|
35
36
|
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
36
37
|
parseIosSimulators,
|
|
37
38
|
repairAbsoluteIosDevSession
|
|
38
|
-
} from "./index-
|
|
39
|
+
} from "./index-5skbydh0.js";
|
|
39
40
|
import {
|
|
40
|
-
buildAbsoluteIosRelease
|
|
41
|
-
|
|
41
|
+
buildAbsoluteIosRelease,
|
|
42
|
+
requireAbsoluteIosReleaseMetadata,
|
|
43
|
+
resolveAbsoluteIosXcodeProject
|
|
44
|
+
} from "./index-q1d4ns4n.js";
|
|
42
45
|
import"./index-fc9vxw56.js";
|
|
43
46
|
import {
|
|
44
47
|
listLiveInstances
|
|
@@ -67,7 +70,7 @@ import {
|
|
|
67
70
|
syncAbsoluteExpoWebAssets,
|
|
68
71
|
unsignedAbsoluteMobileUpdate,
|
|
69
72
|
writeAbsoluteExpoProject
|
|
70
|
-
} from "./index-
|
|
73
|
+
} from "./index-0zcebhm4.js";
|
|
71
74
|
import {
|
|
72
75
|
buildAbsoluteExpoAndroidWslBundle
|
|
73
76
|
} from "./index-28nxv57v.js";
|
|
@@ -4412,9 +4415,9 @@ var require_sha256 = __commonJS(function(exports, module) {
|
|
|
4412
4415
|
var forge = require_forge();
|
|
4413
4416
|
require_md();
|
|
4414
4417
|
require_util();
|
|
4415
|
-
var
|
|
4416
|
-
forge.md.sha256 = forge.md.algorithms.sha256 =
|
|
4417
|
-
|
|
4418
|
+
var sha2564 = module.exports = forge.sha256 = forge.sha256 || {};
|
|
4419
|
+
forge.md.sha256 = forge.md.algorithms.sha256 = sha2564;
|
|
4420
|
+
sha2564.create = function() {
|
|
4418
4421
|
if (!_initialized) {
|
|
4419
4422
|
_init();
|
|
4420
4423
|
}
|
|
@@ -16776,15 +16779,15 @@ var require_main = __commonJS(function(exports) {
|
|
|
16776
16779
|
|
|
16777
16780
|
// src/cli/scripts/mobile.ts
|
|
16778
16781
|
import {
|
|
16779
|
-
access as
|
|
16780
|
-
mkdir as
|
|
16781
|
-
mkdtemp as
|
|
16782
|
-
readFile as
|
|
16783
|
-
rm as
|
|
16782
|
+
access as access14,
|
|
16783
|
+
mkdir as mkdir15,
|
|
16784
|
+
mkdtemp as mkdtemp5,
|
|
16785
|
+
readFile as readFile19,
|
|
16786
|
+
rm as rm8,
|
|
16784
16787
|
writeFile as writeFile16
|
|
16785
16788
|
} from "fs/promises";
|
|
16786
16789
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16787
|
-
import { join as
|
|
16790
|
+
import { join as join16, relative as relative13, resolve as resolve14 } from "path";
|
|
16788
16791
|
import { createInterface } from "readline/promises";
|
|
16789
16792
|
|
|
16790
16793
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -18459,6 +18462,8 @@ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as
|
|
|
18459
18462
|
import { dirname as dirname3, join as join6 } from "path";
|
|
18460
18463
|
var START3 = "// absolutejs:release-readiness:start";
|
|
18461
18464
|
var END3 = "// absolutejs:release-readiness:end";
|
|
18465
|
+
var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
|
|
18466
|
+
var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
|
|
18462
18467
|
var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
|
|
18463
18468
|
var optionalSource4 = async (path) => {
|
|
18464
18469
|
try {
|
|
@@ -18580,9 +18585,70 @@ public final class AbsoluteReleaseReadinessPlugin extends Plugin {
|
|
|
18580
18585
|
}
|
|
18581
18586
|
}
|
|
18582
18587
|
`;
|
|
18583
|
-
var
|
|
18584
|
-
|
|
18585
|
-
|
|
18588
|
+
var iosPluginRegion3 = `${IOS_PLUGIN_START}
|
|
18589
|
+
@objc(AbsoluteReleaseReadinessPlugin)
|
|
18590
|
+
public final class AbsoluteReleaseReadinessPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
18591
|
+
public let identifier = "AbsoluteReleaseReadinessPlugin"
|
|
18592
|
+
public let jsName = "AbsoluteReleaseReadiness"
|
|
18593
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
18594
|
+
CAPPluginMethod(name: "ready", returnType: CAPPluginReturnPromise)
|
|
18595
|
+
]
|
|
18596
|
+
|
|
18597
|
+
@objc public func ready(_ call: CAPPluginCall) {
|
|
18598
|
+
let info = Bundle.main.infoDictionary ?? [:]
|
|
18599
|
+
let version = info["CFBundleShortVersionString"] as? String ?? "unknown"
|
|
18600
|
+
let build = info["CFBundleVersion"] as? String ?? "unknown"
|
|
18601
|
+
NSLog("AbsoluteJS: Capacitor embedded web content ready; version=%@; build=%@", version, build)
|
|
18602
|
+
call.resolve(["ready": true])
|
|
18603
|
+
}
|
|
18604
|
+
}
|
|
18605
|
+
${IOS_PLUGIN_END}
|
|
18606
|
+
`;
|
|
18607
|
+
var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
|
|
18608
|
+
const from = appDelegate.lastIndexOf(`
|
|
18609
|
+
`, existingStart) + 1;
|
|
18610
|
+
const newline = appDelegate.indexOf(`
|
|
18611
|
+
`, existingEnd + IOS_PLUGIN_END.length);
|
|
18612
|
+
const through = newline < 0 ? appDelegate.length : newline + 1;
|
|
18613
|
+
return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
|
|
18614
|
+
};
|
|
18615
|
+
var configureIos5 = async (config) => {
|
|
18616
|
+
const appDelegatePath = join6(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
18617
|
+
const capacitorConfigPath = join6(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
18618
|
+
const appDelegate = await readFile6(appDelegatePath, "utf8");
|
|
18619
|
+
const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
|
|
18620
|
+
const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
|
|
18621
|
+
if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
|
|
18622
|
+
throw new TypeError("AbsoluteJS iOS release-readiness markers are malformed.");
|
|
18623
|
+
const source = existingStart < 0 ? `${appDelegate.trimEnd()}
|
|
18624
|
+
|
|
18625
|
+
${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
|
|
18626
|
+
let capacitorConfig;
|
|
18627
|
+
try {
|
|
18628
|
+
capacitorConfig = JSON.parse(await readFile6(capacitorConfigPath, "utf8"));
|
|
18629
|
+
} catch {
|
|
18630
|
+
throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
|
|
18631
|
+
}
|
|
18632
|
+
if (typeof capacitorConfig !== "object" || capacitorConfig === null)
|
|
18633
|
+
throw new TypeError("iOS capacitor.config.json must contain an object.");
|
|
18634
|
+
const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
|
|
18635
|
+
if (!Array.isArray(packageClassList))
|
|
18636
|
+
throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting release readiness.");
|
|
18637
|
+
const classes = packageClassList.filter((value) => typeof value === "string");
|
|
18638
|
+
const nextConfig = `${JSON.stringify({
|
|
18639
|
+
...capacitorConfig,
|
|
18640
|
+
packageClassList: [
|
|
18641
|
+
...new Set([...classes, "AbsoluteReleaseReadinessPlugin"])
|
|
18642
|
+
]
|
|
18643
|
+
}, null, "\t")}
|
|
18644
|
+
`;
|
|
18645
|
+
const changed = await Promise.all([
|
|
18646
|
+
writeChanged4(appDelegatePath, source),
|
|
18647
|
+
writeChanged4(capacitorConfigPath, nextConfig)
|
|
18648
|
+
]);
|
|
18649
|
+
return changed.some(Boolean);
|
|
18650
|
+
};
|
|
18651
|
+
var configureAndroid5 = async (config) => {
|
|
18586
18652
|
const mainActivityPath = await activityPath(config);
|
|
18587
18653
|
const activity = await readFile6(mainActivityPath, "utf8");
|
|
18588
18654
|
const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
|
|
@@ -18593,6 +18659,15 @@ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.plat
|
|
|
18593
18659
|
writeChanged4(mainActivityPath, injectRegistration(activity)),
|
|
18594
18660
|
writeChanged4(pluginPath, pluginSource(packageName))
|
|
18595
18661
|
]);
|
|
18662
|
+
return changed.some(Boolean);
|
|
18663
|
+
};
|
|
18664
|
+
var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
|
|
18665
|
+
if (config.engine !== "capacitor")
|
|
18666
|
+
return { changed: false };
|
|
18667
|
+
const changed = await Promise.all([
|
|
18668
|
+
platforms.includes("android") ? configureAndroid5(config) : false,
|
|
18669
|
+
platforms.includes("ios") ? configureIos5(config) : false
|
|
18670
|
+
]);
|
|
18596
18671
|
return { changed: changed.some(Boolean) };
|
|
18597
18672
|
};
|
|
18598
18673
|
|
|
@@ -21083,6 +21158,473 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
|
|
|
21083
21158
|
};
|
|
21084
21159
|
};
|
|
21085
21160
|
|
|
21161
|
+
// src/mobile/iosReleaseAcceptance.ts
|
|
21162
|
+
import { createHash as createHash4 } from "crypto";
|
|
21163
|
+
import {
|
|
21164
|
+
access as access7,
|
|
21165
|
+
mkdir as mkdir9,
|
|
21166
|
+
mkdtemp as mkdtemp3,
|
|
21167
|
+
readFile as readFile12,
|
|
21168
|
+
readdir as readdir5,
|
|
21169
|
+
rm as rm6,
|
|
21170
|
+
stat as stat4
|
|
21171
|
+
} from "fs/promises";
|
|
21172
|
+
import {
|
|
21173
|
+
basename as basename2,
|
|
21174
|
+
dirname as dirname9,
|
|
21175
|
+
isAbsolute as isAbsolute4,
|
|
21176
|
+
join as join11,
|
|
21177
|
+
relative as relative6,
|
|
21178
|
+
resolve as resolve7,
|
|
21179
|
+
sep as sep4
|
|
21180
|
+
} from "path";
|
|
21181
|
+
|
|
21182
|
+
// src/mobile/iosUpgradeConformance.ts
|
|
21183
|
+
var captureCommand3 = async (command) => {
|
|
21184
|
+
const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
|
|
21185
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
21186
|
+
process2.exited,
|
|
21187
|
+
new Response(process2.stderr).text(),
|
|
21188
|
+
new Response(process2.stdout).text()
|
|
21189
|
+
]);
|
|
21190
|
+
return { exitCode, stderr, stdout };
|
|
21191
|
+
};
|
|
21192
|
+
var requiredOutput = (result, message) => {
|
|
21193
|
+
const value = result.stdout.trim();
|
|
21194
|
+
if (result.exitCode !== 0 || !value)
|
|
21195
|
+
throw new Error(`${message}: ${result.stderr.trim() || value || "no output"}`);
|
|
21196
|
+
return value;
|
|
21197
|
+
};
|
|
21198
|
+
var inspectAbsoluteIosInstalledApp = async (xcrun, udid, appId, run = captureCommand3, plutil = "/usr/bin/plutil") => {
|
|
21199
|
+
const dataContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "data"]), `Could not inspect the installed iOS data container for ${appId}`);
|
|
21200
|
+
const appContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "app"]), `Could not inspect the installed iOS application container for ${appId}`);
|
|
21201
|
+
const info = requiredOutput(await run([
|
|
21202
|
+
plutil,
|
|
21203
|
+
"-convert",
|
|
21204
|
+
"json",
|
|
21205
|
+
"-o",
|
|
21206
|
+
"-",
|
|
21207
|
+
`${appContainer}/Info.plist`
|
|
21208
|
+
]), `Could not inspect the installed iOS Info.plist for ${appId}`);
|
|
21209
|
+
return parseAbsoluteIosInstalledAppInfo(appId, appContainer, dataContainer, info);
|
|
21210
|
+
};
|
|
21211
|
+
var parseAbsoluteIosInstalledAppInfo = (appId, appContainer, dataContainer, output) => {
|
|
21212
|
+
let value;
|
|
21213
|
+
try {
|
|
21214
|
+
value = JSON.parse(output);
|
|
21215
|
+
} catch {
|
|
21216
|
+
throw new TypeError("Invalid installed iOS application Info.plist JSON.");
|
|
21217
|
+
}
|
|
21218
|
+
if (!value || typeof value !== "object")
|
|
21219
|
+
throw new TypeError("Invalid installed iOS application Info.plist JSON.");
|
|
21220
|
+
const bundleId = Reflect.get(value, "CFBundleIdentifier");
|
|
21221
|
+
if (bundleId !== appId)
|
|
21222
|
+
throw new Error(`Installed iOS application identifier mismatch (${String(bundleId)} != ${appId}).`);
|
|
21223
|
+
const buildNumber = Reflect.get(value, "CFBundleVersion");
|
|
21224
|
+
const version = Reflect.get(value, "CFBundleShortVersionString");
|
|
21225
|
+
return {
|
|
21226
|
+
appContainer,
|
|
21227
|
+
appId,
|
|
21228
|
+
...typeof buildNumber === "string" || typeof buildNumber === "number" ? { buildNumber: String(buildNumber) } : {},
|
|
21229
|
+
dataContainer,
|
|
21230
|
+
...typeof version === "string" ? { version } : {}
|
|
21231
|
+
};
|
|
21232
|
+
};
|
|
21233
|
+
|
|
21234
|
+
// src/mobile/iosReleaseAcceptance.ts
|
|
21235
|
+
var READY_MARKERS = {
|
|
21236
|
+
capacitor: "Capacitor embedded web content ready",
|
|
21237
|
+
expo: "Expo embedded web content ready"
|
|
21238
|
+
};
|
|
21239
|
+
var READY_TIMEOUT_MS = 30000;
|
|
21240
|
+
var READY_POLL_MS = 250;
|
|
21241
|
+
var defaultRun2 = async (command, options = {}) => {
|
|
21242
|
+
const process2 = Bun.spawn(command, {
|
|
21243
|
+
cwd: options.cwd,
|
|
21244
|
+
env: options.env,
|
|
21245
|
+
signal: options.signal,
|
|
21246
|
+
stderr: "pipe",
|
|
21247
|
+
stdin: "ignore",
|
|
21248
|
+
stdout: "pipe"
|
|
21249
|
+
});
|
|
21250
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
21251
|
+
process2.exited,
|
|
21252
|
+
new Response(process2.stderr).text(),
|
|
21253
|
+
new Response(process2.stdout).text()
|
|
21254
|
+
]);
|
|
21255
|
+
return { exitCode, stderr, stdout };
|
|
21256
|
+
};
|
|
21257
|
+
var exists3 = async (path) => {
|
|
21258
|
+
try {
|
|
21259
|
+
await access7(path);
|
|
21260
|
+
return true;
|
|
21261
|
+
} catch {
|
|
21262
|
+
return false;
|
|
21263
|
+
}
|
|
21264
|
+
};
|
|
21265
|
+
var sha2562 = async (path) => createHash4("sha256").update(await readFile12(path)).digest("hex");
|
|
21266
|
+
var inside2 = (root, candidate) => {
|
|
21267
|
+
const path = relative6(resolve7(root), resolve7(candidate));
|
|
21268
|
+
return path === "" || !isAbsolute4(path) && path !== ".." && !path.startsWith(`..${sep4}`);
|
|
21269
|
+
};
|
|
21270
|
+
var requireSuccess2 = async (run, command, label, options) => {
|
|
21271
|
+
const result = await run(command, options);
|
|
21272
|
+
if (result.exitCode !== 0)
|
|
21273
|
+
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
21274
|
+
return result;
|
|
21275
|
+
};
|
|
21276
|
+
var findByExtension = async (root, extension) => {
|
|
21277
|
+
if (!await exists3(root))
|
|
21278
|
+
return;
|
|
21279
|
+
const entries = await readdir5(root, { withFileTypes: true });
|
|
21280
|
+
const matches = await Promise.all(entries.map(async (entry) => {
|
|
21281
|
+
const path = join11(root, entry.name);
|
|
21282
|
+
if (entry.name.endsWith(extension))
|
|
21283
|
+
return path;
|
|
21284
|
+
return entry.isDirectory() ? findByExtension(path, extension) : undefined;
|
|
21285
|
+
}));
|
|
21286
|
+
return matches.find((match) => match !== undefined);
|
|
21287
|
+
};
|
|
21288
|
+
var readAbsoluteIosRelease = async (projectRoot, requested) => {
|
|
21289
|
+
const root = resolve7(projectRoot);
|
|
21290
|
+
const selected = resolve7(root, requested);
|
|
21291
|
+
if (!inside2(root, selected))
|
|
21292
|
+
throw new TypeError("mobile test ios --release must remain inside the project.");
|
|
21293
|
+
const metadataPath = basename2(selected) === "release.json" ? selected : join11(selected, "release.json");
|
|
21294
|
+
const releaseRoot = dirname9(metadataPath);
|
|
21295
|
+
const metadata = requireAbsoluteIosReleaseMetadata(JSON.parse(await readFile12(metadataPath, "utf8")));
|
|
21296
|
+
const artifactPath = resolve7(releaseRoot, metadata.artifact);
|
|
21297
|
+
if (!inside2(releaseRoot, artifactPath))
|
|
21298
|
+
throw new TypeError("iOS release artifact path is invalid.");
|
|
21299
|
+
const [artifactStat, artifactSha256] = await Promise.all([
|
|
21300
|
+
stat4(artifactPath),
|
|
21301
|
+
sha2562(artifactPath)
|
|
21302
|
+
]);
|
|
21303
|
+
if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
|
|
21304
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} is missing or modified.`);
|
|
21305
|
+
const registered = metadata.registeredDevice;
|
|
21306
|
+
let registeredArtifactPath;
|
|
21307
|
+
if (registered) {
|
|
21308
|
+
registeredArtifactPath = resolve7(releaseRoot, registered.artifact);
|
|
21309
|
+
if (!inside2(releaseRoot, registeredArtifactPath))
|
|
21310
|
+
throw new TypeError("iOS registered-device artifact path is invalid.");
|
|
21311
|
+
const [registeredStat, registeredSha256] = await Promise.all([
|
|
21312
|
+
stat4(registeredArtifactPath),
|
|
21313
|
+
sha2562(registeredArtifactPath)
|
|
21314
|
+
]);
|
|
21315
|
+
if (registeredStat.size !== registered.bytes || registeredSha256 !== registered.sha256)
|
|
21316
|
+
throw new TypeError(`Registered-device export for ${metadata.releaseId} is missing or modified.`);
|
|
21317
|
+
}
|
|
21318
|
+
return {
|
|
21319
|
+
artifactPath,
|
|
21320
|
+
metadata,
|
|
21321
|
+
metadataPath,
|
|
21322
|
+
registeredArtifactPath,
|
|
21323
|
+
releaseRoot
|
|
21324
|
+
};
|
|
21325
|
+
};
|
|
21326
|
+
var releaseMarker = (release) => {
|
|
21327
|
+
const marker = READY_MARKERS[release.metadata.engine];
|
|
21328
|
+
return release.metadata.buildNumber === undefined ? marker : `${marker}; version=${release.metadata.marketingVersion}; build=${release.metadata.buildNumber}`;
|
|
21329
|
+
};
|
|
21330
|
+
var waitForSimulatorMarker = async (options) => {
|
|
21331
|
+
const startedAt = performance.now();
|
|
21332
|
+
const poll = async () => {
|
|
21333
|
+
options.signal?.throwIfAborted();
|
|
21334
|
+
if (performance.now() - startedAt >= READY_TIMEOUT_MS)
|
|
21335
|
+
throw new Error("The installed iOS Release app launched but its embedded content did not become ready.");
|
|
21336
|
+
const logs = await options.run([
|
|
21337
|
+
options.xcrun,
|
|
21338
|
+
"simctl",
|
|
21339
|
+
"spawn",
|
|
21340
|
+
options.udid,
|
|
21341
|
+
"log",
|
|
21342
|
+
"show",
|
|
21343
|
+
"--style",
|
|
21344
|
+
"compact",
|
|
21345
|
+
"--start",
|
|
21346
|
+
options.startedAt.toISOString(),
|
|
21347
|
+
"--predicate",
|
|
21348
|
+
`eventMessage CONTAINS "${releaseMarker(options.release)}"`
|
|
21349
|
+
]);
|
|
21350
|
+
if (logs.exitCode === 0 && logs.stdout.includes(releaseMarker(options.release)))
|
|
21351
|
+
return;
|
|
21352
|
+
await Bun.sleep(READY_POLL_MS);
|
|
21353
|
+
await poll();
|
|
21354
|
+
};
|
|
21355
|
+
await poll();
|
|
21356
|
+
};
|
|
21357
|
+
var launchSimulator = async (options) => {
|
|
21358
|
+
const startedAt = performance.now();
|
|
21359
|
+
const logStartedAt = new Date;
|
|
21360
|
+
await requireSuccess2(options.run, [
|
|
21361
|
+
options.xcrun,
|
|
21362
|
+
"simctl",
|
|
21363
|
+
"launch",
|
|
21364
|
+
"--terminate-running-process",
|
|
21365
|
+
options.udid,
|
|
21366
|
+
options.release.metadata.appId
|
|
21367
|
+
], "iOS Simulator Release launch");
|
|
21368
|
+
await waitForSimulatorMarker({ ...options, startedAt: logStartedAt });
|
|
21369
|
+
return Math.round(performance.now() - startedAt);
|
|
21370
|
+
};
|
|
21371
|
+
var runAbsoluteIosSimulatorReleaseAcceptance = async (options) => {
|
|
21372
|
+
const baseRun = options.run ?? defaultRun2;
|
|
21373
|
+
const run = (command, commandOptions = {}) => baseRun(command, {
|
|
21374
|
+
...commandOptions,
|
|
21375
|
+
...options.signal ? { signal: options.signal } : {}
|
|
21376
|
+
});
|
|
21377
|
+
options.signal?.throwIfAborted();
|
|
21378
|
+
const xcodebuild = options.xcodebuild ?? "/usr/bin/xcodebuild";
|
|
21379
|
+
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
21380
|
+
const startedAt = performance.now();
|
|
21381
|
+
const iosRoot = join11(options.config.nativeProjectDirectory, "ios");
|
|
21382
|
+
const xcode = await resolveAbsoluteIosXcodeProject(iosRoot);
|
|
21383
|
+
const derivedData = join11(options.artifactDirectory, "DerivedData");
|
|
21384
|
+
await mkdir9(options.artifactDirectory, { recursive: true });
|
|
21385
|
+
await requireSuccess2(run, [
|
|
21386
|
+
xcodebuild,
|
|
21387
|
+
"-workspace",
|
|
21388
|
+
xcode.workspacePath,
|
|
21389
|
+
"-scheme",
|
|
21390
|
+
xcode.scheme,
|
|
21391
|
+
"-configuration",
|
|
21392
|
+
"Release",
|
|
21393
|
+
"-destination",
|
|
21394
|
+
`platform=iOS Simulator,id=${options.udid}`,
|
|
21395
|
+
"-derivedDataPath",
|
|
21396
|
+
derivedData,
|
|
21397
|
+
`MARKETING_VERSION=${options.release.metadata.marketingVersion}`,
|
|
21398
|
+
...options.release.metadata.buildNumber === undefined ? [] : [
|
|
21399
|
+
`CURRENT_PROJECT_VERSION=${options.release.metadata.buildNumber}`
|
|
21400
|
+
],
|
|
21401
|
+
"build"
|
|
21402
|
+
], "iOS Simulator Release build", { cwd: iosRoot });
|
|
21403
|
+
const app = await findByExtension(join11(derivedData, "Build", "Products"), ".app");
|
|
21404
|
+
if (!app)
|
|
21405
|
+
throw new Error("Xcode did not produce a Simulator Release app.");
|
|
21406
|
+
const installStartedAt = performance.now();
|
|
21407
|
+
await requireSuccess2(run, [xcrun, "simctl", "install", options.udid, app], "iOS Simulator Release installation");
|
|
21408
|
+
const installMs = Math.round(performance.now() - installStartedAt);
|
|
21409
|
+
const installed = await inspectAbsoluteIosInstalledApp(xcrun, options.udid, options.release.metadata.appId, (command) => run(command));
|
|
21410
|
+
if (installed.version !== options.release.metadata.marketingVersion)
|
|
21411
|
+
throw new Error(`Installed iOS version ${installed.version ?? "unknown"} does not match release ${options.release.metadata.marketingVersion}.`);
|
|
21412
|
+
if (options.release.metadata.buildNumber !== undefined && installed.buildNumber !== String(options.release.metadata.buildNumber))
|
|
21413
|
+
throw new Error(`Installed iOS build ${installed.buildNumber ?? "unknown"} does not match release ${options.release.metadata.buildNumber}.`);
|
|
21414
|
+
const launchMs = await launchSimulator({
|
|
21415
|
+
release: options.release,
|
|
21416
|
+
run,
|
|
21417
|
+
udid: options.udid,
|
|
21418
|
+
xcrun
|
|
21419
|
+
});
|
|
21420
|
+
await requireSuccess2(run, [
|
|
21421
|
+
xcrun,
|
|
21422
|
+
"simctl",
|
|
21423
|
+
"terminate",
|
|
21424
|
+
options.udid,
|
|
21425
|
+
options.release.metadata.appId
|
|
21426
|
+
], "iOS Simulator Release termination");
|
|
21427
|
+
const relaunchMs = await launchSimulator({
|
|
21428
|
+
release: options.release,
|
|
21429
|
+
run,
|
|
21430
|
+
udid: options.udid,
|
|
21431
|
+
xcrun
|
|
21432
|
+
});
|
|
21433
|
+
return {
|
|
21434
|
+
artifactBytes: options.release.metadata.bytes,
|
|
21435
|
+
artifactExactness: "source-equivalent",
|
|
21436
|
+
artifactSha256: options.release.metadata.sha256,
|
|
21437
|
+
...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
|
|
21438
|
+
distribution: "simulator-release",
|
|
21439
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
21440
|
+
embeddedLocal: true,
|
|
21441
|
+
engine: options.release.metadata.engine,
|
|
21442
|
+
installMs,
|
|
21443
|
+
launchMs,
|
|
21444
|
+
marketingVersion: options.release.metadata.marketingVersion,
|
|
21445
|
+
networkUnavailable: "not-proven",
|
|
21446
|
+
relaunchMs,
|
|
21447
|
+
releaseId: options.release.metadata.releaseId,
|
|
21448
|
+
signed: options.release.metadata.signed,
|
|
21449
|
+
status: "pass",
|
|
21450
|
+
target: "simulator"
|
|
21451
|
+
};
|
|
21452
|
+
};
|
|
21453
|
+
var readUntilMarker = async (reader, marker, output = "") => {
|
|
21454
|
+
const { done, value } = await reader.read();
|
|
21455
|
+
if (done)
|
|
21456
|
+
return { found: false, output };
|
|
21457
|
+
const next = `${output}${new TextDecoder().decode(value)}`;
|
|
21458
|
+
if (next.includes(marker))
|
|
21459
|
+
return { found: true, output: next };
|
|
21460
|
+
return readUntilMarker(reader, marker, next);
|
|
21461
|
+
};
|
|
21462
|
+
var observeDeviceLaunch = async (options) => {
|
|
21463
|
+
const startedAt = performance.now();
|
|
21464
|
+
const process2 = Bun.spawn([
|
|
21465
|
+
options.xcrun,
|
|
21466
|
+
"devicectl",
|
|
21467
|
+
"device",
|
|
21468
|
+
"process",
|
|
21469
|
+
"launch",
|
|
21470
|
+
"--console",
|
|
21471
|
+
"--terminate-existing",
|
|
21472
|
+
"--device",
|
|
21473
|
+
options.device,
|
|
21474
|
+
options.appId
|
|
21475
|
+
], {
|
|
21476
|
+
signal: options.signal,
|
|
21477
|
+
stderr: "pipe",
|
|
21478
|
+
stdin: "ignore",
|
|
21479
|
+
stdout: "pipe"
|
|
21480
|
+
});
|
|
21481
|
+
const stdoutReader = process2.stdout.getReader();
|
|
21482
|
+
const stderrReader = process2.stderr.getReader();
|
|
21483
|
+
const requireMarker = async (reader) => {
|
|
21484
|
+
const result = await readUntilMarker(reader, options.marker);
|
|
21485
|
+
if (!result.found)
|
|
21486
|
+
throw new Error(`Console stream closed before readiness.${result.output.trim() ? ` Output: ${result.output.trim()}` : ""}`);
|
|
21487
|
+
return result;
|
|
21488
|
+
};
|
|
21489
|
+
const observed = Promise.any([
|
|
21490
|
+
requireMarker(stdoutReader),
|
|
21491
|
+
requireMarker(stderrReader)
|
|
21492
|
+
]);
|
|
21493
|
+
try {
|
|
21494
|
+
const result = await Promise.race([
|
|
21495
|
+
observed.then((value) => ({ type: "output", value })).catch(() => ({ type: "closed" })),
|
|
21496
|
+
Bun.sleep(options.timeoutMs ?? READY_TIMEOUT_MS).then(() => ({
|
|
21497
|
+
type: "timeout"
|
|
21498
|
+
}))
|
|
21499
|
+
]);
|
|
21500
|
+
if (result.type === "output" && result.value.found)
|
|
21501
|
+
return Math.round(performance.now() - startedAt);
|
|
21502
|
+
if (result.type === "closed")
|
|
21503
|
+
throw new Error("The physical iOS app exited before embedded content became ready.");
|
|
21504
|
+
throw new Error("The physical iOS app did not report embedded readiness before the timeout.");
|
|
21505
|
+
} finally {
|
|
21506
|
+
process2.kill();
|
|
21507
|
+
await Promise.all([
|
|
21508
|
+
stdoutReader.cancel().catch(() => {
|
|
21509
|
+
return;
|
|
21510
|
+
}),
|
|
21511
|
+
stderrReader.cancel().catch(() => {
|
|
21512
|
+
return;
|
|
21513
|
+
})
|
|
21514
|
+
]);
|
|
21515
|
+
stdoutReader.releaseLock();
|
|
21516
|
+
stderrReader.releaseLock();
|
|
21517
|
+
}
|
|
21518
|
+
};
|
|
21519
|
+
var installRegisteredDeviceRelease = async (options) => {
|
|
21520
|
+
if (!options.release.registeredArtifactPath)
|
|
21521
|
+
throw new TypeError("This release has no same-archive registered-device IPA. Rebuild with `absolute mobile build ios --registered-device-artifact`.");
|
|
21522
|
+
const staging = await mkdtemp3(join11(dirname9(options.release.releaseRoot), ".ios-device-"));
|
|
21523
|
+
try {
|
|
21524
|
+
await requireSuccess2(options.run, [
|
|
21525
|
+
"/usr/bin/ditto",
|
|
21526
|
+
"-x",
|
|
21527
|
+
"-k",
|
|
21528
|
+
options.release.registeredArtifactPath,
|
|
21529
|
+
staging
|
|
21530
|
+
], "iOS registered-device IPA extraction");
|
|
21531
|
+
const app = await findByExtension(staging, ".app");
|
|
21532
|
+
if (!app)
|
|
21533
|
+
throw new Error("The registered-device IPA contains no installable app.");
|
|
21534
|
+
const startedAt = performance.now();
|
|
21535
|
+
await requireSuccess2(options.run, [
|
|
21536
|
+
options.xcrun,
|
|
21537
|
+
"devicectl",
|
|
21538
|
+
"device",
|
|
21539
|
+
"install",
|
|
21540
|
+
"app",
|
|
21541
|
+
"--device",
|
|
21542
|
+
options.device,
|
|
21543
|
+
app
|
|
21544
|
+
], "iOS registered-device Release installation");
|
|
21545
|
+
return Math.round(performance.now() - startedAt);
|
|
21546
|
+
} finally {
|
|
21547
|
+
await rm6(staging, { force: true, recursive: true });
|
|
21548
|
+
}
|
|
21549
|
+
};
|
|
21550
|
+
var inspectTestFlightInstallation = async (options) => {
|
|
21551
|
+
const apps = await requireSuccess2(options.run, [
|
|
21552
|
+
options.xcrun,
|
|
21553
|
+
"devicectl",
|
|
21554
|
+
"device",
|
|
21555
|
+
"info",
|
|
21556
|
+
"apps",
|
|
21557
|
+
"--device",
|
|
21558
|
+
options.device
|
|
21559
|
+
], "TestFlight installation inspection");
|
|
21560
|
+
if (!apps.stdout.includes(options.release.metadata.appId))
|
|
21561
|
+
throw new Error("The selected device does not have the requested TestFlight app installed.");
|
|
21562
|
+
return 0;
|
|
21563
|
+
};
|
|
21564
|
+
var runAbsoluteIosDeviceReleaseAcceptance = async (options) => {
|
|
21565
|
+
if (!options.networkUnavailableConfirmed)
|
|
21566
|
+
throw new TypeError("Physical iOS offline acceptance requires explicit confirmation that Airplane Mode is enabled and Wi-Fi is disabled in Settings.");
|
|
21567
|
+
const baseRun = options.run ?? defaultRun2;
|
|
21568
|
+
const run = (command, commandOptions = {}) => baseRun(command, {
|
|
21569
|
+
...commandOptions,
|
|
21570
|
+
...options.signal ? { signal: options.signal } : {}
|
|
21571
|
+
});
|
|
21572
|
+
options.signal?.throwIfAborted();
|
|
21573
|
+
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
21574
|
+
const startedAt = performance.now();
|
|
21575
|
+
await requireSuccess2(run, [
|
|
21576
|
+
xcrun,
|
|
21577
|
+
"devicectl",
|
|
21578
|
+
"device",
|
|
21579
|
+
"info",
|
|
21580
|
+
"details",
|
|
21581
|
+
"--device",
|
|
21582
|
+
options.device
|
|
21583
|
+
], "iOS physical-device availability inspection");
|
|
21584
|
+
const installation = {
|
|
21585
|
+
device: options.device,
|
|
21586
|
+
release: options.release,
|
|
21587
|
+
run,
|
|
21588
|
+
xcrun
|
|
21589
|
+
};
|
|
21590
|
+
const installMs = options.distribution === "registered-device" ? await installRegisteredDeviceRelease(installation) : await inspectTestFlightInstallation(installation);
|
|
21591
|
+
const observe = options.observeLaunch ?? ((launch2) => observeDeviceLaunch({
|
|
21592
|
+
...launch2,
|
|
21593
|
+
...options.signal ? { signal: options.signal } : {},
|
|
21594
|
+
xcrun
|
|
21595
|
+
}));
|
|
21596
|
+
const marker = releaseMarker(options.release);
|
|
21597
|
+
const launchMs = await observe({
|
|
21598
|
+
appId: options.release.metadata.appId,
|
|
21599
|
+
device: options.device,
|
|
21600
|
+
marker
|
|
21601
|
+
});
|
|
21602
|
+
const relaunchMs = await observe({
|
|
21603
|
+
appId: options.release.metadata.appId,
|
|
21604
|
+
device: options.device,
|
|
21605
|
+
marker
|
|
21606
|
+
});
|
|
21607
|
+
return {
|
|
21608
|
+
artifactBytes: options.release.metadata.bytes,
|
|
21609
|
+
artifactExactness: options.distribution === "testflight" ? "store-delivered" : "archive-equivalent",
|
|
21610
|
+
artifactSha256: options.release.metadata.sha256,
|
|
21611
|
+
...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
|
|
21612
|
+
distribution: options.distribution === "testflight" ? "apple-processed" : "registered-device",
|
|
21613
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
21614
|
+
embeddedLocal: true,
|
|
21615
|
+
engine: options.release.metadata.engine,
|
|
21616
|
+
installMs,
|
|
21617
|
+
launchMs,
|
|
21618
|
+
marketingVersion: options.release.metadata.marketingVersion,
|
|
21619
|
+
networkUnavailable: "user-confirmed",
|
|
21620
|
+
relaunchMs,
|
|
21621
|
+
releaseId: options.release.metadata.releaseId,
|
|
21622
|
+
signed: options.release.metadata.signed,
|
|
21623
|
+
status: "pass",
|
|
21624
|
+
target: "device"
|
|
21625
|
+
};
|
|
21626
|
+
};
|
|
21627
|
+
|
|
21086
21628
|
// src/mobile/iosDeviceAcceptance.ts
|
|
21087
21629
|
var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
21088
21630
|
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
@@ -21101,20 +21643,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
|
21101
21643
|
]
|
|
21102
21644
|
};
|
|
21103
21645
|
};
|
|
21104
|
-
var
|
|
21646
|
+
var requireSuccess3 = (result, message) => {
|
|
21105
21647
|
if (result.exitCode !== 0)
|
|
21106
21648
|
throw new Error(message);
|
|
21107
21649
|
return result;
|
|
21108
21650
|
};
|
|
21109
21651
|
var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
21110
21652
|
const commands = absoluteIosDeviceAcceptanceCommands(options);
|
|
21111
|
-
|
|
21112
|
-
const apps =
|
|
21653
|
+
requireSuccess3(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
|
|
21654
|
+
const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
|
|
21113
21655
|
if (!apps.stdout.includes(options.appId))
|
|
21114
21656
|
throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
|
|
21115
21657
|
const now = options.now ?? performance.now.bind(performance);
|
|
21116
21658
|
const startedAt = now();
|
|
21117
|
-
|
|
21659
|
+
requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
|
|
21118
21660
|
await options.waitForHmr();
|
|
21119
21661
|
return {
|
|
21120
21662
|
hmrConnected: true,
|
|
@@ -21124,7 +21666,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
|
21124
21666
|
};
|
|
21125
21667
|
|
|
21126
21668
|
// src/mobile/iosConformance.ts
|
|
21127
|
-
import { readFile as
|
|
21669
|
+
import { readFile as readFile13, stat as stat5 } from "fs/promises";
|
|
21128
21670
|
var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
|
|
21129
21671
|
var parseAbsoluteIosHmrLog = (line) => {
|
|
21130
21672
|
const match = HMR_LINE.exec(line);
|
|
@@ -21156,13 +21698,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21156
21698
|
const sleep = options.sleep ?? Bun.sleep;
|
|
21157
21699
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
21158
21700
|
const deadline = Date.now() + timeoutMs;
|
|
21159
|
-
let offset = options.startOffset ?? await
|
|
21701
|
+
let offset = options.startOffset ?? await stat5(options.logPath).then(({ size }) => size).catch(() => 0);
|
|
21160
21702
|
let buffered = "";
|
|
21161
21703
|
const poll = async () => {
|
|
21162
21704
|
if (Date.now() > deadline)
|
|
21163
21705
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
21164
21706
|
options.signal?.throwIfAborted();
|
|
21165
|
-
const contents = await
|
|
21707
|
+
const contents = await readFile13(options.logPath).catch(() => Buffer.alloc(0));
|
|
21166
21708
|
if (contents.byteLength < offset) {
|
|
21167
21709
|
offset = 0;
|
|
21168
21710
|
buffered = "";
|
|
@@ -21183,8 +21725,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21183
21725
|
};
|
|
21184
21726
|
|
|
21185
21727
|
// src/mobile/nativeTestReport.ts
|
|
21186
|
-
import { mkdir as
|
|
21187
|
-
import { join as
|
|
21728
|
+
import { mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
|
|
21729
|
+
import { join as join12 } from "path";
|
|
21188
21730
|
var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
|
|
21189
21731
|
var bearerPattern = /bearer\s+[^\s,;]+/giu;
|
|
21190
21732
|
var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
|
|
@@ -21211,8 +21753,10 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
21211
21753
|
let developmentDetails = run.status === "pass" ? `The app launched and connected to native HMR in ${run.durationMs}ms.${routeDetails}` : `The automated native run failed after ${run.durationMs}ms: ${run.error ?? "No error detail was available."}`;
|
|
21212
21754
|
if (run.release)
|
|
21213
21755
|
developmentDetails = run.status === "pass" ? `The ${run.release.engine} release was installed from its generated APK set, launched offline in ${run.release.launchMs}ms, and relaunched in ${run.release.relaunchMs}ms.` : `Installed ${run.release.engine} release acceptance failed after ${run.durationMs}ms.`;
|
|
21756
|
+
if (run.iosRelease)
|
|
21757
|
+
developmentDetails = run.status === "pass" ? `The ${run.iosRelease.engine} iOS release rendered embedded local content in ${run.iosRelease.launchMs}ms and relaunched in ${run.iosRelease.relaunchMs}ms.` : `Installed ${run.iosRelease.engine} iOS release acceptance failed after ${run.durationMs}ms.`;
|
|
21214
21758
|
let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
|
|
21215
|
-
if (run.release)
|
|
21759
|
+
if (run.release || run.iosRelease)
|
|
21216
21760
|
hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
|
|
21217
21761
|
if (run.hmr)
|
|
21218
21762
|
hmrDetails = `Observed native HMR ${run.hmr.outcome} in ${run.hmr.durationMs}ms${run.hmr.serverMs === undefined ? "" : ` (server ${run.hmr.serverMs}ms, client ${run.hmr.clientMs}ms)`}.`;
|
|
@@ -21251,6 +21795,24 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
21251
21795
|
result: run.release.embeddedOffline ? "PASS" : "FAIL"
|
|
21252
21796
|
});
|
|
21253
21797
|
}
|
|
21798
|
+
if (run.iosRelease) {
|
|
21799
|
+
const release = run.iosRelease;
|
|
21800
|
+
checks.push({
|
|
21801
|
+
details: `Validated immutable release ${release.releaseId} (${release.artifactBytes} bytes, SHA-256 ${release.artifactSha256}); ${release.distribution} evidence is ${release.artifactExactness}, executed on the ${release.remote ? "paired Remote Mac" : "local Mac"}, and installed in ${release.installMs}ms.`,
|
|
21802
|
+
id: "AUTO-IOS-RELEASE-01",
|
|
21803
|
+
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
21804
|
+
}, {
|
|
21805
|
+
details: release.networkUnavailable === "user-confirmed" ? "The tester explicitly confirmed Airplane Mode was enabled and Wi-Fi was disabled before both physical-device launches." : "Simulator Release proved embedded local rendering, but does not claim physical-device network-unavailable behavior.",
|
|
21806
|
+
id: "AUTO-IOS-RELEASE-OFFLINE-01",
|
|
21807
|
+
result: release.networkUnavailable === "user-confirmed" ? "PASS" : "SKIPPED"
|
|
21808
|
+
});
|
|
21809
|
+
if (release.remote)
|
|
21810
|
+
checks.push({
|
|
21811
|
+
details: "The installed-release lane executed through the paired Remote Mac protocol; immutable inputs and returned evidence were verified on the initiating host.",
|
|
21812
|
+
id: "AUTO-IOS-REMOTE-01",
|
|
21813
|
+
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
21814
|
+
});
|
|
21815
|
+
}
|
|
21254
21816
|
if (run.upgrade) {
|
|
21255
21817
|
const { upgrade } = run;
|
|
21256
21818
|
checks.push({
|
|
@@ -21313,7 +21875,7 @@ var createAbsoluteNativeTestReport = (options) => ({
|
|
|
21313
21875
|
run: options.run
|
|
21314
21876
|
});
|
|
21315
21877
|
var readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
21316
|
-
const manifest = JSON.parse(await
|
|
21878
|
+
const manifest = JSON.parse(await readFile14(packageJsonPath, "utf8"));
|
|
21317
21879
|
if (typeof manifest !== "object" || manifest === null)
|
|
21318
21880
|
return "unknown";
|
|
21319
21881
|
const version = Reflect.get(manifest, "version");
|
|
@@ -21351,9 +21913,9 @@ ${table(report.manualChecks)}
|
|
|
21351
21913
|
`;
|
|
21352
21914
|
};
|
|
21353
21915
|
var writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
21354
|
-
await
|
|
21355
|
-
const jsonPath =
|
|
21356
|
-
const markdownPath =
|
|
21916
|
+
await mkdir10(directory, { recursive: true });
|
|
21917
|
+
const jsonPath = join12(directory, "report.json");
|
|
21918
|
+
const markdownPath = join12(directory, "report.md");
|
|
21357
21919
|
await Promise.all([
|
|
21358
21920
|
writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
|
|
21359
21921
|
`),
|
|
@@ -21468,7 +22030,7 @@ var createAbsoluteIosPartnerReport = (options) => {
|
|
|
21468
22030
|
absolutejsVersion: options.absolutejsVersion,
|
|
21469
22031
|
bunVersion: options.bunVersion,
|
|
21470
22032
|
macosVersion: options.macosVersion,
|
|
21471
|
-
provider: "capacitor",
|
|
22033
|
+
provider: options.provider ?? "capacitor",
|
|
21472
22034
|
xcodeVersion: options.xcodeVersion
|
|
21473
22035
|
},
|
|
21474
22036
|
run: {
|
|
@@ -21609,8 +22171,8 @@ var createAbsoluteAndroidTestReport = (options) => {
|
|
|
21609
22171
|
};
|
|
21610
22172
|
|
|
21611
22173
|
// src/mobile/releasePublisher.ts
|
|
21612
|
-
import { access as
|
|
21613
|
-
import { isAbsolute as
|
|
22174
|
+
import { access as access8 } from "fs/promises";
|
|
22175
|
+
import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
|
|
21614
22176
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
21615
22177
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
21616
22178
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -21636,17 +22198,17 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
21636
22198
|
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21637
22199
|
var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
|
|
21638
22200
|
var publisherModulePath = (projectRoot, requested) => {
|
|
21639
|
-
const root =
|
|
21640
|
-
const path =
|
|
21641
|
-
const projectRelative =
|
|
21642
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
22201
|
+
const root = resolve8(projectRoot);
|
|
22202
|
+
const path = resolve8(root, requested);
|
|
22203
|
+
const projectRelative = relative7(root, path);
|
|
22204
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
|
|
21643
22205
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
21644
22206
|
}
|
|
21645
22207
|
return path;
|
|
21646
22208
|
};
|
|
21647
22209
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
21648
22210
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
21649
|
-
await
|
|
22211
|
+
await access8(modulePath).catch(() => {
|
|
21650
22212
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
21651
22213
|
});
|
|
21652
22214
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
@@ -21708,8 +22270,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
21708
22270
|
};
|
|
21709
22271
|
|
|
21710
22272
|
// src/mobile/mobileInspect.ts
|
|
21711
|
-
import { access as
|
|
21712
|
-
import { join as
|
|
22273
|
+
import { access as access9, readFile as readFile15 } from "fs/promises";
|
|
22274
|
+
import { join as join13, relative as relative8, resolve as resolve9 } from "path";
|
|
21713
22275
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
21714
22276
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
21715
22277
|
"@absolutejs/absolute",
|
|
@@ -21724,19 +22286,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
|
|
|
21724
22286
|
]);
|
|
21725
22287
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21726
22288
|
var portablePath2 = (projectRoot, path) => {
|
|
21727
|
-
const value =
|
|
22289
|
+
const value = relative8(resolve9(projectRoot), resolve9(path)).replaceAll("\\", "/");
|
|
21728
22290
|
return value || ".";
|
|
21729
22291
|
};
|
|
21730
22292
|
var pathExists4 = async (path) => {
|
|
21731
22293
|
try {
|
|
21732
|
-
await
|
|
22294
|
+
await access9(path);
|
|
21733
22295
|
return true;
|
|
21734
22296
|
} catch {
|
|
21735
22297
|
return false;
|
|
21736
22298
|
}
|
|
21737
22299
|
};
|
|
21738
22300
|
var readObject2 = async (path) => {
|
|
21739
|
-
const value = JSON.parse(await
|
|
22301
|
+
const value = JSON.parse(await readFile15(path, "utf8"));
|
|
21740
22302
|
if (!isObject2(value))
|
|
21741
22303
|
throw new TypeError("JSON root must be an object.");
|
|
21742
22304
|
return value;
|
|
@@ -21748,13 +22310,13 @@ var addPackageDeclarations = (declarations, value) => {
|
|
|
21748
22310
|
declarations.set(name, declared);
|
|
21749
22311
|
};
|
|
21750
22312
|
var packageInspections = async (projectRoot, additionalNames) => {
|
|
21751
|
-
const project = await readObject2(
|
|
22313
|
+
const project = await readObject2(join13(projectRoot, "package.json"));
|
|
21752
22314
|
const declarations = new Map;
|
|
21753
22315
|
for (const field2 of ["dependencies", "devDependencies"])
|
|
21754
22316
|
addPackageDeclarations(declarations, project[field2]);
|
|
21755
22317
|
const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
|
|
21756
22318
|
return Promise.all(names.map(async (name) => {
|
|
21757
|
-
const installedManifest = await readObject2(
|
|
22319
|
+
const installedManifest = await readObject2(join13(projectRoot, "node_modules", name, "package.json")).catch(() => {
|
|
21758
22320
|
return;
|
|
21759
22321
|
});
|
|
21760
22322
|
const installed = installedManifest?.version;
|
|
@@ -21813,7 +22375,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
|
|
|
21813
22375
|
},
|
|
21814
22376
|
format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
|
|
21815
22377
|
nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
|
|
21816
|
-
const path =
|
|
22378
|
+
const path = join13(config.nativeProjectDirectory, platform);
|
|
21817
22379
|
return {
|
|
21818
22380
|
initialized: await pathExists4(path),
|
|
21819
22381
|
path: portablePath2(projectRoot, path),
|
|
@@ -21870,8 +22432,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
21870
22432
|
|
|
21871
22433
|
// src/mobile/ciWorkflow.ts
|
|
21872
22434
|
import { existsSync } from "fs";
|
|
21873
|
-
import { access as
|
|
21874
|
-
import { dirname as
|
|
22435
|
+
import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
|
|
22436
|
+
import { dirname as dirname10, extname as extname2, relative as relative9, resolve as resolve10, sep as sep6 } from "path";
|
|
21875
22437
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
21876
22438
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
21877
22439
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -21890,9 +22452,9 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
21890
22452
|
"APP_STORE_CONNECT_KEY_ID",
|
|
21891
22453
|
"APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
|
|
21892
22454
|
]);
|
|
21893
|
-
var
|
|
22455
|
+
var exists4 = async (path) => {
|
|
21894
22456
|
try {
|
|
21895
|
-
await
|
|
22457
|
+
await access10(path);
|
|
21896
22458
|
return true;
|
|
21897
22459
|
} catch {
|
|
21898
22460
|
return false;
|
|
@@ -21900,10 +22462,10 @@ var exists3 = async (path) => {
|
|
|
21900
22462
|
};
|
|
21901
22463
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
21902
22464
|
var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
21903
|
-
const root =
|
|
21904
|
-
const path =
|
|
21905
|
-
const portable =
|
|
21906
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22465
|
+
const root = resolve10(projectRoot);
|
|
22466
|
+
const path = resolve10(root, value);
|
|
22467
|
+
const portable = relative9(root, path).replaceAll("\\", "/");
|
|
22468
|
+
if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
|
|
21907
22469
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
21908
22470
|
}
|
|
21909
22471
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
@@ -21913,11 +22475,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
|
21913
22475
|
return portable;
|
|
21914
22476
|
};
|
|
21915
22477
|
var workflowOutputPath = (projectRoot, value) => {
|
|
21916
|
-
const root =
|
|
21917
|
-
const workflows =
|
|
21918
|
-
const path =
|
|
21919
|
-
const portable =
|
|
21920
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22478
|
+
const root = resolve10(projectRoot);
|
|
22479
|
+
const workflows = resolve10(root, ".github/workflows");
|
|
22480
|
+
const path = resolve10(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
22481
|
+
const portable = relative9(workflows, path);
|
|
22482
|
+
if (portable === ".." || portable.startsWith(`..${sep6}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
|
|
21921
22483
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
21922
22484
|
}
|
|
21923
22485
|
return path;
|
|
@@ -22346,11 +22908,11 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
22346
22908
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
22347
22909
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
22348
22910
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
22349
|
-
const previous = await
|
|
22911
|
+
const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
|
|
22350
22912
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
22351
|
-
throw new TypeError(`${
|
|
22913
|
+
throw new TypeError(`${relative9(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
22352
22914
|
if (previous !== generated.workflow) {
|
|
22353
|
-
await
|
|
22915
|
+
await mkdir11(dirname10(path), { recursive: true });
|
|
22354
22916
|
await writeFile12(path, generated.workflow);
|
|
22355
22917
|
}
|
|
22356
22918
|
return {
|
|
@@ -22364,46 +22926,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
22364
22926
|
};
|
|
22365
22927
|
|
|
22366
22928
|
// src/mobile/updateSigning.ts
|
|
22367
|
-
import { createHash as
|
|
22929
|
+
import { createHash as createHash5, sign, verify } from "crypto";
|
|
22368
22930
|
import {
|
|
22369
22931
|
cp,
|
|
22370
|
-
mkdir as
|
|
22371
|
-
mkdtemp as
|
|
22372
|
-
readdir as
|
|
22373
|
-
readFile as
|
|
22932
|
+
mkdir as mkdir12,
|
|
22933
|
+
mkdtemp as mkdtemp4,
|
|
22934
|
+
readdir as readdir6,
|
|
22935
|
+
readFile as readFile17,
|
|
22374
22936
|
rename as rename10,
|
|
22375
|
-
rm as
|
|
22376
|
-
stat as
|
|
22937
|
+
rm as rm7,
|
|
22938
|
+
stat as stat6,
|
|
22377
22939
|
writeFile as writeFile13
|
|
22378
22940
|
} from "fs/promises";
|
|
22379
|
-
import { dirname as
|
|
22941
|
+
import { dirname as dirname11, join as join14, relative as relative10, resolve as resolve11 } from "path";
|
|
22380
22942
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
22381
22943
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
22382
|
-
var
|
|
22944
|
+
var sha2563 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
22383
22945
|
var listFiles = async (root, directory = root) => {
|
|
22384
|
-
const entries = await
|
|
22946
|
+
const entries = await readdir6(directory, { withFileTypes: true });
|
|
22385
22947
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
22386
|
-
const path =
|
|
22948
|
+
const path = join14(directory, entry.name);
|
|
22387
22949
|
if (entry.isDirectory())
|
|
22388
22950
|
return listFiles(root, path);
|
|
22389
22951
|
if (!entry.isFile())
|
|
22390
22952
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
22391
|
-
return [
|
|
22953
|
+
return [relative10(root, path).replaceAll("\\", "/")];
|
|
22392
22954
|
}));
|
|
22393
22955
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
22394
22956
|
};
|
|
22395
22957
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
22396
|
-
const source =
|
|
22958
|
+
const source = join14(root, path);
|
|
22397
22959
|
const [metadata, contents] = await Promise.all([
|
|
22398
|
-
|
|
22399
|
-
|
|
22960
|
+
stat6(source),
|
|
22961
|
+
readFile17(source)
|
|
22400
22962
|
]);
|
|
22401
|
-
return { bytes: metadata.size, path, sha256:
|
|
22963
|
+
return { bytes: metadata.size, path, sha256: sha2563(contents) };
|
|
22402
22964
|
}));
|
|
22403
|
-
var releaseIdFor = (value) => `amu_${
|
|
22965
|
+
var releaseIdFor = (value) => `amu_${sha2563(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
22404
22966
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
22405
|
-
const bundleDirectory =
|
|
22406
|
-
const outputRoot =
|
|
22967
|
+
const bundleDirectory = resolve11(options.bundleDirectory);
|
|
22968
|
+
const outputRoot = resolve11(options.outputDirectory);
|
|
22407
22969
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
22408
22970
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
22409
22971
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -22431,30 +22993,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
22431
22993
|
value: signature.toString("base64")
|
|
22432
22994
|
}
|
|
22433
22995
|
});
|
|
22434
|
-
await
|
|
22435
|
-
const outputDirectory =
|
|
22436
|
-
const staging = await
|
|
22996
|
+
await mkdir12(outputRoot, { recursive: true });
|
|
22997
|
+
const outputDirectory = join14(outputRoot, manifest.releaseId);
|
|
22998
|
+
const staging = await mkdtemp4(join14(outputRoot, ".stage-"));
|
|
22437
22999
|
try {
|
|
22438
|
-
await cp(bundleDirectory,
|
|
23000
|
+
await cp(bundleDirectory, join14(staging, UPDATE_FILES_DIRECTORY), {
|
|
22439
23001
|
force: true,
|
|
22440
23002
|
recursive: true
|
|
22441
23003
|
});
|
|
22442
|
-
await writeFile13(
|
|
23004
|
+
await writeFile13(join14(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
22443
23005
|
`);
|
|
22444
23006
|
await rename10(staging, outputDirectory);
|
|
22445
23007
|
} catch (error) {
|
|
22446
|
-
await
|
|
23008
|
+
await rm7(staging, { force: true, recursive: true });
|
|
22447
23009
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
22448
23010
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
22449
23011
|
throw error;
|
|
22450
23012
|
}
|
|
22451
23013
|
return {
|
|
22452
23014
|
manifest,
|
|
22453
|
-
manifestPath:
|
|
23015
|
+
manifestPath: join14(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
22454
23016
|
outputDirectory
|
|
22455
23017
|
};
|
|
22456
23018
|
};
|
|
22457
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
23019
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile17(join14(resolve11(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
22458
23020
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
22459
23021
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
22460
23022
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -22464,8 +23026,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
22464
23026
|
};
|
|
22465
23027
|
|
|
22466
23028
|
// src/mobile/expoUpdate.ts
|
|
22467
|
-
import { access as
|
|
22468
|
-
import { dirname as
|
|
23029
|
+
import { access as access11, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
|
|
23030
|
+
import { dirname as dirname12, extname as extname3, join as join15 } from "path";
|
|
22469
23031
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
22470
23032
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
22471
23033
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -22531,7 +23093,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
22531
23093
|
};
|
|
22532
23094
|
};
|
|
22533
23095
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
22534
|
-
const metadata = JSON.parse(await
|
|
23096
|
+
const metadata = JSON.parse(await readFile18(join15(options.exportDirectory, "metadata.json"), "utf8"));
|
|
22535
23097
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
22536
23098
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
22537
23099
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -22542,11 +23104,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22542
23104
|
runtimeVersion: options.runtimeVersion
|
|
22543
23105
|
});
|
|
22544
23106
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
22545
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
23107
|
+
await Promise.all(referenced.map(({ path }) => access11(join15(options.exportDirectory, path)).catch(() => {
|
|
22546
23108
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
22547
23109
|
})));
|
|
22548
|
-
const destination =
|
|
22549
|
-
await
|
|
23110
|
+
const destination = join15(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
23111
|
+
await mkdir13(dirname12(destination), { recursive: true });
|
|
22550
23112
|
await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
22551
23113
|
`);
|
|
22552
23114
|
return { descriptor, path: destination };
|
|
@@ -22554,21 +23116,21 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22554
23116
|
|
|
22555
23117
|
// src/mobile/expoCodeSigning.ts
|
|
22556
23118
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
22557
|
-
import { access as
|
|
22558
|
-
import { dirname as
|
|
22559
|
-
var
|
|
22560
|
-
var
|
|
22561
|
-
const location =
|
|
22562
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
23119
|
+
import { access as access12, mkdir as mkdir14, writeFile as writeFile15 } from "fs/promises";
|
|
23120
|
+
import { dirname as dirname13, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
|
|
23121
|
+
var exists5 = async (path) => access12(path).then(() => true).catch(() => false);
|
|
23122
|
+
var inside3 = (root, path) => {
|
|
23123
|
+
const location = relative11(root, path);
|
|
23124
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep7}`) && !isAbsolute6(location);
|
|
22563
23125
|
};
|
|
22564
23126
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
22565
|
-
const root =
|
|
22566
|
-
const certificatePath =
|
|
22567
|
-
const privateKeyPath =
|
|
22568
|
-
const publicKeyPath =
|
|
22569
|
-
if (!
|
|
23127
|
+
const root = resolve12(options.projectRoot);
|
|
23128
|
+
const certificatePath = resolve12(root, options.certificatePath);
|
|
23129
|
+
const privateKeyPath = resolve12(root, options.privateKeyPath);
|
|
23130
|
+
const publicKeyPath = resolve12(root, options.publicKeyPath ?? resolve12(dirname13(privateKeyPath), "public-key.pem"));
|
|
23131
|
+
if (!inside3(root, certificatePath))
|
|
22570
23132
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
22571
|
-
if (
|
|
23133
|
+
if (inside3(root, privateKeyPath) || inside3(root, publicKeyPath))
|
|
22572
23134
|
throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
|
|
22573
23135
|
const commonName = options.commonName.trim();
|
|
22574
23136
|
if (!commonName || commonName.length > 64)
|
|
@@ -22577,7 +23139,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22577
23139
|
if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
|
|
22578
23140
|
throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
|
|
22579
23141
|
const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
|
|
22580
|
-
exists: await
|
|
23142
|
+
exists: await exists5(path),
|
|
22581
23143
|
path
|
|
22582
23144
|
})))).filter((entry) => entry.exists);
|
|
22583
23145
|
if (collisions.length > 0)
|
|
@@ -22595,7 +23157,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22595
23157
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
22596
23158
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
22597
23159
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
22598
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
23160
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir14(dirname13(path), { recursive: true })));
|
|
22599
23161
|
await Promise.all([
|
|
22600
23162
|
writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
22601
23163
|
writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
@@ -22610,8 +23172,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22610
23172
|
};
|
|
22611
23173
|
|
|
22612
23174
|
// src/mobile/updatePublisher.ts
|
|
22613
|
-
import { access as
|
|
22614
|
-
import { isAbsolute as
|
|
23175
|
+
import { access as access13 } from "fs/promises";
|
|
23176
|
+
import { isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
|
|
22615
23177
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
22616
23178
|
var lifecycleMethod = (publisher, name) => {
|
|
22617
23179
|
const method = publisher[name];
|
|
@@ -22720,16 +23282,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
22720
23282
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22721
23283
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
22722
23284
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
22723
|
-
const root =
|
|
22724
|
-
const path =
|
|
22725
|
-
const projectRelative =
|
|
22726
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
23285
|
+
const root = resolve13(projectRoot);
|
|
23286
|
+
const path = resolve13(root, requested);
|
|
23287
|
+
const projectRelative = relative12(root, path);
|
|
23288
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute7(projectRelative))
|
|
22727
23289
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
22728
23290
|
return path;
|
|
22729
23291
|
};
|
|
22730
23292
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
22731
23293
|
const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
|
|
22732
|
-
await
|
|
23294
|
+
await access13(modulePath).catch(() => {
|
|
22733
23295
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
22734
23296
|
});
|
|
22735
23297
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -22827,7 +23389,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
22827
23389
|
];
|
|
22828
23390
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
22829
23391
|
var directProjectPackages = async (projectRoot) => {
|
|
22830
|
-
const manifest = JSON.parse(await
|
|
23392
|
+
const manifest = JSON.parse(await readFile19(join16(projectRoot, "package.json"), "utf8"));
|
|
22831
23393
|
if (!isRecord7(manifest))
|
|
22832
23394
|
throw new TypeError("Application package.json must contain an object.");
|
|
22833
23395
|
const names = new Set;
|
|
@@ -22841,7 +23403,7 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
22841
23403
|
};
|
|
22842
23404
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
22843
23405
|
try {
|
|
22844
|
-
const manifest = JSON.parse(await
|
|
23406
|
+
const manifest = JSON.parse(await readFile19(join16(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
22845
23407
|
return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
22846
23408
|
} catch {
|
|
22847
23409
|
return;
|
|
@@ -22905,9 +23467,9 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
22905
23467
|
};
|
|
22906
23468
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
22907
23469
|
var expoExecutable = async (project) => {
|
|
22908
|
-
const executable =
|
|
23470
|
+
const executable = join16(project, "node_modules", ".bin", "expo");
|
|
22909
23471
|
try {
|
|
22910
|
-
await
|
|
23472
|
+
await access14(executable);
|
|
22911
23473
|
return executable;
|
|
22912
23474
|
} catch {
|
|
22913
23475
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -22939,14 +23501,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
22939
23501
|
};
|
|
22940
23502
|
var ensureExpoPackages = async (project, args) => {
|
|
22941
23503
|
try {
|
|
22942
|
-
const manifest = JSON.parse(await
|
|
23504
|
+
const manifest = JSON.parse(await readFile19(join16(project, "package.json"), "utf8"));
|
|
22943
23505
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
22944
23506
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
22945
23507
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
22946
23508
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
22947
23509
|
if (typeof expected !== "string")
|
|
22948
23510
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
22949
|
-
const installed = JSON.parse(await
|
|
23511
|
+
const installed = JSON.parse(await readFile19(join16(project, "node_modules", name, "package.json"), "utf8"));
|
|
22950
23512
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
22951
23513
|
if (typeof actual !== "string")
|
|
22952
23514
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -23144,7 +23706,7 @@ var sync = async (args) => {
|
|
|
23144
23706
|
};
|
|
23145
23707
|
var associations = async (args) => {
|
|
23146
23708
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
23147
|
-
const outputDirectory =
|
|
23709
|
+
const outputDirectory = resolve14(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
23148
23710
|
if (args.includes("--verify")) {
|
|
23149
23711
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
23150
23712
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -23196,7 +23758,7 @@ var generateGithubCi = async (args) => {
|
|
|
23196
23758
|
const publicResult = {
|
|
23197
23759
|
changed: result.changed,
|
|
23198
23760
|
format: result.format,
|
|
23199
|
-
path:
|
|
23761
|
+
path: relative13(projectRoot, result.path).replaceAll("\\", "/"),
|
|
23200
23762
|
platforms: result.platforms,
|
|
23201
23763
|
publishing: result.publishing,
|
|
23202
23764
|
requiredSecrets: result.requiredSecrets
|
|
@@ -23307,9 +23869,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23307
23869
|
temporaryDirectory: undefined
|
|
23308
23870
|
};
|
|
23309
23871
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
23310
|
-
const temporaryParent =
|
|
23311
|
-
await
|
|
23312
|
-
const temporaryDirectory = await
|
|
23872
|
+
const temporaryParent = join16(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
23873
|
+
await mkdir15(temporaryParent, { recursive: true });
|
|
23874
|
+
const temporaryDirectory = await mkdtemp5(join16(temporaryParent, ".stage-"));
|
|
23313
23875
|
try {
|
|
23314
23876
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
23315
23877
|
"export",
|
|
@@ -23321,7 +23883,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23321
23883
|
temporaryDirectory,
|
|
23322
23884
|
"--clear"
|
|
23323
23885
|
], { production: true });
|
|
23324
|
-
const generatedApp = JSON.parse(await
|
|
23886
|
+
const generatedApp = JSON.parse(await readFile19(join16(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
23325
23887
|
const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
|
|
23326
23888
|
if (!isRecord7(expoConfig))
|
|
23327
23889
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
@@ -23339,7 +23901,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23339
23901
|
temporaryDirectory
|
|
23340
23902
|
};
|
|
23341
23903
|
} catch (error) {
|
|
23342
|
-
await
|
|
23904
|
+
await rm8(temporaryDirectory, { force: true, recursive: true });
|
|
23343
23905
|
throw error;
|
|
23344
23906
|
}
|
|
23345
23907
|
};
|
|
@@ -23364,11 +23926,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
23364
23926
|
let expoExportDirectory;
|
|
23365
23927
|
try {
|
|
23366
23928
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
23367
|
-
const embedded = JSON.parse(await
|
|
23929
|
+
const embedded = JSON.parse(await readFile19(join16(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23368
23930
|
const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
|
|
23369
23931
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23370
23932
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23371
|
-
const privateKey = await
|
|
23933
|
+
const privateKey = await readFile19(resolve14(projectRoot, signingKeyPath));
|
|
23372
23934
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
23373
23935
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
23374
23936
|
format: "der",
|
|
@@ -23389,7 +23951,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23389
23951
|
channel: mobile.updates.channel,
|
|
23390
23952
|
classification,
|
|
23391
23953
|
keyId,
|
|
23392
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
23954
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join16(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
23393
23955
|
privateKey,
|
|
23394
23956
|
runtimeFingerprint
|
|
23395
23957
|
});
|
|
@@ -23401,7 +23963,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23401
23963
|
return result;
|
|
23402
23964
|
} finally {
|
|
23403
23965
|
if (expoExportDirectory)
|
|
23404
|
-
await
|
|
23966
|
+
await rm8(expoExportDirectory, { force: true, recursive: true });
|
|
23405
23967
|
sendTelemetryEvent("mobile:update-build", {
|
|
23406
23968
|
classification: classification ?? "invalid",
|
|
23407
23969
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -23431,7 +23993,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
23431
23993
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
23432
23994
|
...validityYears === undefined ? {} : { validityYears }
|
|
23433
23995
|
});
|
|
23434
|
-
const certificatePath =
|
|
23996
|
+
const certificatePath = relative13(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
23435
23997
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
23436
23998
|
engine: "expo",
|
|
23437
23999
|
validityYears: validityYears ?? 10
|
|
@@ -23493,7 +24055,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
23493
24055
|
publicKeys: mobile.updates.publicKeys,
|
|
23494
24056
|
storage: requestedStorage
|
|
23495
24057
|
});
|
|
23496
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
24058
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative13(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
23497
24059
|
if (requestedStorage === "local")
|
|
23498
24060
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
23499
24061
|
else
|
|
@@ -23905,6 +24467,7 @@ var prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
|
|
|
23905
24467
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
|
|
23906
24468
|
await applyAbsoluteNativeUpdates(mobile, ["ios"]);
|
|
23907
24469
|
await applyAbsoluteNativeObservability(mobile, ["ios"]);
|
|
24470
|
+
await applyAbsoluteNativeReleaseReadiness(mobile, ["ios"]);
|
|
23908
24471
|
};
|
|
23909
24472
|
var prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot);
|
|
23910
24473
|
var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
|
|
@@ -23969,7 +24532,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
23969
24532
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23970
24533
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
23971
24534
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23972
|
-
console.log(`Metadata: ${
|
|
24535
|
+
console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
|
|
23973
24536
|
return release;
|
|
23974
24537
|
} finally {
|
|
23975
24538
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -24057,6 +24620,7 @@ var buildLocalIosRelease = async (options) => {
|
|
|
24057
24620
|
developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
|
|
24058
24621
|
...options.mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
|
|
24059
24622
|
outputDirectory: valueAfter(options.args, "--outdir"),
|
|
24623
|
+
registeredDeviceArtifact: options.args.includes("--registered-device-artifact"),
|
|
24060
24624
|
...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
|
|
24061
24625
|
projectRoot: options.projectRoot
|
|
24062
24626
|
});
|
|
@@ -24114,6 +24678,7 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
24114
24678
|
},
|
|
24115
24679
|
...prepareBuildNumber === undefined ? {} : { prepareBuildNumber },
|
|
24116
24680
|
project: createAbsoluteRemoteIosDevProject(mobile, projectRoot, remoteProfile),
|
|
24681
|
+
registeredDeviceArtifact: args.includes("--registered-device-artifact"),
|
|
24117
24682
|
signal: cancellation?.signal
|
|
24118
24683
|
}) : await buildLocalIosRelease({
|
|
24119
24684
|
args,
|
|
@@ -24125,7 +24690,9 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
24125
24690
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
24126
24691
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
24127
24692
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
24128
|
-
|
|
24693
|
+
if (release.registeredArtifactPath)
|
|
24694
|
+
console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
|
|
24695
|
+
console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
|
|
24129
24696
|
return release;
|
|
24130
24697
|
} finally {
|
|
24131
24698
|
stopListeningForCancellation();
|
|
@@ -24237,7 +24804,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24237
24804
|
checks.push({
|
|
24238
24805
|
id: "sync.storage-schema",
|
|
24239
24806
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
24240
|
-
path:
|
|
24807
|
+
path: join16(projectRoot, "package.json"),
|
|
24241
24808
|
platform: "host",
|
|
24242
24809
|
status: "pass"
|
|
24243
24810
|
});
|
|
@@ -24245,7 +24812,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24245
24812
|
checks.push({
|
|
24246
24813
|
id: "sync.storage-schema",
|
|
24247
24814
|
label: "Offline schema metadata is invalid",
|
|
24248
|
-
path:
|
|
24815
|
+
path: join16(projectRoot, "package.json"),
|
|
24249
24816
|
platform: "host",
|
|
24250
24817
|
remediation: error instanceof Error ? error.message : String(error),
|
|
24251
24818
|
status: "fail"
|
|
@@ -24312,7 +24879,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
|
|
|
24312
24879
|
Emulator setup verification:`);
|
|
24313
24880
|
printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
|
|
24314
24881
|
};
|
|
24315
|
-
var
|
|
24882
|
+
var captureCommand4 = (command) => {
|
|
24316
24883
|
const result = Bun.spawnSync(command, {
|
|
24317
24884
|
stderr: "pipe",
|
|
24318
24885
|
stdin: "ignore",
|
|
@@ -24333,7 +24900,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
24333
24900
|
}
|
|
24334
24901
|
return { https: args.includes("--https"), port };
|
|
24335
24902
|
}
|
|
24336
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
24903
|
+
const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24337
24904
|
if (instances.length !== 1) {
|
|
24338
24905
|
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>.");
|
|
24339
24906
|
}
|
|
@@ -24362,7 +24929,7 @@ var requireAndroidAdb = async () => {
|
|
|
24362
24929
|
return adb2;
|
|
24363
24930
|
};
|
|
24364
24931
|
var selectAndroidSerial = (adb2, explicitSerial) => {
|
|
24365
|
-
const devices =
|
|
24932
|
+
const devices = captureCommand4([adb2, "devices"]);
|
|
24366
24933
|
if (devices.exitCode !== 0) {
|
|
24367
24934
|
throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
|
|
24368
24935
|
}
|
|
@@ -24382,7 +24949,7 @@ var selectAndroidSerial = (adb2, explicitSerial) => {
|
|
|
24382
24949
|
var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
|
|
24383
24950
|
if (explicitSerial)
|
|
24384
24951
|
return selectAndroidSerial(adb2, explicitSerial);
|
|
24385
|
-
const current =
|
|
24952
|
+
const current = captureCommand4([adb2, "devices"]);
|
|
24386
24953
|
const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
|
|
24387
24954
|
if (ready)
|
|
24388
24955
|
return ready;
|
|
@@ -24401,11 +24968,11 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
24401
24968
|
launched.unref();
|
|
24402
24969
|
const startedAt = performance.now();
|
|
24403
24970
|
const bootedSerial = () => {
|
|
24404
|
-
const devices =
|
|
24971
|
+
const devices = captureCommand4([adb2, "devices"]);
|
|
24405
24972
|
const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
|
|
24406
24973
|
if (!serial)
|
|
24407
24974
|
return;
|
|
24408
|
-
const booted =
|
|
24975
|
+
const booted = captureCommand4([
|
|
24409
24976
|
adb2,
|
|
24410
24977
|
"-s",
|
|
24411
24978
|
serial,
|
|
@@ -24427,8 +24994,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
24427
24994
|
return waitForBoot();
|
|
24428
24995
|
};
|
|
24429
24996
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
24430
|
-
const root =
|
|
24431
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
24997
|
+
const root = resolve14(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
24998
|
+
if (root !== projectRoot && !root.startsWith(`${resolve14(projectRoot)}/`)) {
|
|
24432
24999
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
24433
25000
|
}
|
|
24434
25001
|
return root;
|
|
@@ -24456,11 +25023,11 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
24456
25023
|
});
|
|
24457
25024
|
};
|
|
24458
25025
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
24459
|
-
await
|
|
24460
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
25026
|
+
await mkdir15(options.artifactRoot, { recursive: true });
|
|
25027
|
+
const screenshot = options.session ? await options.session.screenshot(join16(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
24461
25028
|
return;
|
|
24462
25029
|
}) : undefined;
|
|
24463
|
-
const diagnosticPath =
|
|
25030
|
+
const diagnosticPath = join16(options.artifactRoot, "android-failure.json");
|
|
24464
25031
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24465
25032
|
diagnostics: options.session?.diagnostics ?? [],
|
|
24466
25033
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -24536,7 +25103,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24536
25103
|
release,
|
|
24537
25104
|
serial
|
|
24538
25105
|
});
|
|
24539
|
-
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial,
|
|
25106
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join16(reportRoot, "android-release.png")) : undefined;
|
|
24540
25107
|
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
24541
25108
|
durationMs: result.durationMs,
|
|
24542
25109
|
engine: result.engine,
|
|
@@ -24583,8 +25150,8 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24583
25150
|
platform: "android",
|
|
24584
25151
|
success: false
|
|
24585
25152
|
});
|
|
24586
|
-
await
|
|
24587
|
-
const diagnosticPath =
|
|
25153
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25154
|
+
const diagnosticPath = join16(artifactRoot, "android-release-failure.json");
|
|
24588
25155
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24589
25156
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
24590
25157
|
platform: "android",
|
|
@@ -24679,7 +25246,7 @@ var testAndroid = async (args) => {
|
|
|
24679
25246
|
console.log(JSON.stringify(report, null, 2));
|
|
24680
25247
|
else
|
|
24681
25248
|
printAndroidTestReport(report);
|
|
24682
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
25249
|
+
const screenshot = reportRoot ? await session.screenshot(join16(artifactRoot, "android-emulator.png")) : undefined;
|
|
24683
25250
|
await writeRequestedAndroidReport({
|
|
24684
25251
|
adb: adb2,
|
|
24685
25252
|
args,
|
|
@@ -24748,14 +25315,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
24748
25315
|
const port = Number(explicit);
|
|
24749
25316
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
24750
25317
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
24751
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
25318
|
+
const instance2 = listLiveInstances().find((candidate) => resolve14(candidate.cwd) === resolve14(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
24752
25319
|
return {
|
|
24753
25320
|
https: instance2?.https ?? args.includes("--https"),
|
|
24754
25321
|
instance: instance2,
|
|
24755
25322
|
port
|
|
24756
25323
|
};
|
|
24757
25324
|
}
|
|
24758
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
25325
|
+
const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24759
25326
|
if (instances.length !== 1)
|
|
24760
25327
|
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>.");
|
|
24761
25328
|
const [instance] = instances;
|
|
@@ -24845,11 +25412,11 @@ var physicalIosCapture = async (options) => {
|
|
|
24845
25412
|
remote: false,
|
|
24846
25413
|
xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
|
|
24847
25414
|
xcrun: "/usr/bin/xcrun",
|
|
24848
|
-
capture: async (command) =>
|
|
25415
|
+
capture: async (command) => captureCommand4(command)
|
|
24849
25416
|
};
|
|
24850
25417
|
};
|
|
24851
25418
|
var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
24852
|
-
const result =
|
|
25419
|
+
const result = captureCommand4([
|
|
24853
25420
|
xcrun,
|
|
24854
25421
|
"simctl",
|
|
24855
25422
|
"list",
|
|
@@ -24871,16 +25438,37 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
|
24871
25438
|
throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
|
|
24872
25439
|
return selected;
|
|
24873
25440
|
};
|
|
25441
|
+
var selectOrStartIosReleaseSimulator = async (xcrun, explicitUdid) => {
|
|
25442
|
+
const result = captureCommand4([
|
|
25443
|
+
xcrun,
|
|
25444
|
+
"simctl",
|
|
25445
|
+
"list",
|
|
25446
|
+
"devices",
|
|
25447
|
+
"available",
|
|
25448
|
+
"-j"
|
|
25449
|
+
]);
|
|
25450
|
+
if (result.exitCode !== 0)
|
|
25451
|
+
throw new Error(`Could not list iOS simulators: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
25452
|
+
const devices = parseIosSimulators(result.stdout).filter((device) => device.isAvailable);
|
|
25453
|
+
const selected = explicitUdid ? devices.find((device) => device.udid === explicitUdid) : devices.find((device) => device.name === ABSOLUTE_IOS_SIMULATOR_NAME);
|
|
25454
|
+
if (!selected)
|
|
25455
|
+
throw new TypeError(explicitUdid ? `iOS simulator ${explicitUdid} is unavailable.` : "No managed AbsoluteJS iOS simulator was found. Run `absolute mobile doctor ios --fix`.");
|
|
25456
|
+
if (selected.state !== "Booted") {
|
|
25457
|
+
requireCapturedCommand([xcrun, "simctl", "boot", selected.udid], "iOS Simulator boot");
|
|
25458
|
+
requireCapturedCommand([xcrun, "simctl", "bootstatus", selected.udid, "-b"], "iOS Simulator readiness");
|
|
25459
|
+
}
|
|
25460
|
+
return { ...selected, state: "Booted" };
|
|
25461
|
+
};
|
|
24874
25462
|
var requireCapturedCommand = (command, label) => {
|
|
24875
|
-
const result =
|
|
25463
|
+
const result = captureCommand4(command);
|
|
24876
25464
|
if (result.exitCode !== 0)
|
|
24877
25465
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
24878
25466
|
return result;
|
|
24879
25467
|
};
|
|
24880
25468
|
var writeIosFailureArtifacts = async (options) => {
|
|
24881
|
-
await
|
|
24882
|
-
const screenshot =
|
|
24883
|
-
const screenshotResult =
|
|
25469
|
+
await mkdir15(options.artifactRoot, { recursive: true });
|
|
25470
|
+
const screenshot = join16(options.artifactRoot, "ios-failure.png");
|
|
25471
|
+
const screenshotResult = captureCommand4([
|
|
24884
25472
|
options.xcrun,
|
|
24885
25473
|
"simctl",
|
|
24886
25474
|
"io",
|
|
@@ -24888,7 +25476,7 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
24888
25476
|
"screenshot",
|
|
24889
25477
|
screenshot
|
|
24890
25478
|
]);
|
|
24891
|
-
const diagnosticPath =
|
|
25479
|
+
const diagnosticPath = join16(options.artifactRoot, "ios-failure.json");
|
|
24892
25480
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24893
25481
|
appId: options.appId,
|
|
24894
25482
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -24916,8 +25504,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
24916
25504
|
var absolutejsVersionForReport = async () => {
|
|
24917
25505
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
24918
25506
|
const versions = await Promise.all([
|
|
24919
|
-
|
|
24920
|
-
|
|
25507
|
+
resolve14(import.meta.dir, "..", "..", "package.json"),
|
|
25508
|
+
resolve14(import.meta.dir, "..", "..", "..", "package.json")
|
|
24921
25509
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
24922
25510
|
for (const version of versions) {
|
|
24923
25511
|
if (version === "unknown")
|
|
@@ -24964,7 +25552,11 @@ var writeRequestedIosReport = async (options) => {
|
|
|
24964
25552
|
const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
|
|
24965
25553
|
if (!metadata)
|
|
24966
25554
|
throw new Error("iOS report metadata could not be inspected.");
|
|
24967
|
-
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
|
|
25555
|
+
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
|
|
25556
|
+
...metadata,
|
|
25557
|
+
...options.provider ? { provider: options.provider } : {},
|
|
25558
|
+
run: options.run
|
|
25559
|
+
}));
|
|
24968
25560
|
const print = options.args.includes("--json") ? console.error : console.log;
|
|
24969
25561
|
print(`iOS partner report: ${paths.markdownPath}`);
|
|
24970
25562
|
print(`Return this report directory: ${paths.directory}`);
|
|
@@ -25081,8 +25673,251 @@ var testPhysicalIos = async (options) => {
|
|
|
25081
25673
|
throw error;
|
|
25082
25674
|
}
|
|
25083
25675
|
};
|
|
25676
|
+
var printIosReleaseAcceptance = (result, json) => {
|
|
25677
|
+
if (json) {
|
|
25678
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25679
|
+
return;
|
|
25680
|
+
}
|
|
25681
|
+
console.log(`\u2713 ${result.artifactExactness} ${result.engine} iOS release evidence passed via ${result.distribution}.`);
|
|
25682
|
+
console.log(`\u2713 Embedded local content launched in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
|
|
25683
|
+
console.log(result.networkUnavailable === "user-confirmed" ? "\u2713 Physical-device network-unavailable state was explicitly confirmed." : "\u2139 Simulator evidence does not claim physical-device offline behavior.");
|
|
25684
|
+
};
|
|
25685
|
+
var iosReleaseRun = (result, appId, targetId, remote) => ({
|
|
25686
|
+
appId,
|
|
25687
|
+
durationMs: result.durationMs,
|
|
25688
|
+
hmrConnected: false,
|
|
25689
|
+
iosRelease: {
|
|
25690
|
+
artifactBytes: result.artifactBytes,
|
|
25691
|
+
artifactExactness: result.artifactExactness,
|
|
25692
|
+
artifactSha256: result.artifactSha256,
|
|
25693
|
+
distribution: result.distribution,
|
|
25694
|
+
embeddedLocal: result.embeddedLocal,
|
|
25695
|
+
engine: result.engine,
|
|
25696
|
+
installMs: result.installMs,
|
|
25697
|
+
launchMs: result.launchMs,
|
|
25698
|
+
networkUnavailable: result.networkUnavailable,
|
|
25699
|
+
relaunchMs: result.relaunchMs,
|
|
25700
|
+
releaseId: result.releaseId,
|
|
25701
|
+
remote,
|
|
25702
|
+
signed: result.signed
|
|
25703
|
+
},
|
|
25704
|
+
status: "pass",
|
|
25705
|
+
targetId,
|
|
25706
|
+
targetKind: result.target
|
|
25707
|
+
});
|
|
25708
|
+
var runIosPhysicalReleaseTarget = async (options) => {
|
|
25709
|
+
const confirmed = options.args.includes("--yes") || await confirmInstall("On the selected iPhone, enable Airplane Mode and then disable Wi-Fi in Settings. Confirm both are still disabled and continue with two release launches?");
|
|
25710
|
+
if (!confirmed)
|
|
25711
|
+
throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
|
|
25712
|
+
const targetId = normalizeAbsoluteIosDeviceIdentifier(options.device);
|
|
25713
|
+
const result = await runAbsoluteIosDeviceReleaseAcceptance({
|
|
25714
|
+
device: targetId,
|
|
25715
|
+
distribution: options.args.includes("--testflight") ? "testflight" : "registered-device",
|
|
25716
|
+
networkUnavailableConfirmed: true,
|
|
25717
|
+
release: options.release,
|
|
25718
|
+
xcrun: options.xcrun
|
|
25719
|
+
});
|
|
25720
|
+
return { result, targetId };
|
|
25721
|
+
};
|
|
25722
|
+
var runIosSimulatorReleaseTarget = async (options) => {
|
|
25723
|
+
const simulator = await selectOrStartIosReleaseSimulator(options.xcrun, valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial"));
|
|
25724
|
+
const xcodebuild = requireCapturedCommand([options.xcrun, "--find", "xcodebuild"], "Xcode build-tool inspection").stdout.trim();
|
|
25725
|
+
const result = await runAbsoluteIosSimulatorReleaseAcceptance({
|
|
25726
|
+
artifactDirectory: options.artifactRoot,
|
|
25727
|
+
config: options.mobile,
|
|
25728
|
+
release: options.release,
|
|
25729
|
+
udid: simulator.udid,
|
|
25730
|
+
xcodebuild,
|
|
25731
|
+
xcrun: options.xcrun
|
|
25732
|
+
});
|
|
25733
|
+
return { result, targetId: simulator.udid };
|
|
25734
|
+
};
|
|
25735
|
+
var runRemoteIosReleaseTarget = async (options) => {
|
|
25736
|
+
const physical = options.requestedDevice !== undefined;
|
|
25737
|
+
const confirmed = !physical || options.args.includes("--yes") || await confirmInstall("On the selected iPhone, enable Airplane Mode and then disable Wi-Fi in Settings. Confirm both are still disabled and continue with two release launches on the Remote Mac?");
|
|
25738
|
+
if (!confirmed)
|
|
25739
|
+
throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
|
|
25740
|
+
let distribution = "simulator-release";
|
|
25741
|
+
if (options.requestedDevice)
|
|
25742
|
+
distribution = "registered-device";
|
|
25743
|
+
if (options.args.includes("--testflight"))
|
|
25744
|
+
distribution = "testflight";
|
|
25745
|
+
const simulatorUdid = valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial");
|
|
25746
|
+
const remoteAcceptance = await runAbsoluteRemoteIosReleaseAcceptance({
|
|
25747
|
+
...options.requestedDevice ? {
|
|
25748
|
+
deviceIdentifier: normalizeAbsoluteIosDeviceIdentifier(options.requestedDevice),
|
|
25749
|
+
networkUnavailableConfirmed: true
|
|
25750
|
+
} : {},
|
|
25751
|
+
distribution,
|
|
25752
|
+
project: createAbsoluteRemoteIosDevProject(options.mobile, options.projectRoot, options.profile),
|
|
25753
|
+
release: options.release,
|
|
25754
|
+
signal: options.cancellation?.signal,
|
|
25755
|
+
log: (message) => console.log(`[remote] ${message}`),
|
|
25756
|
+
onPhaseTiming: ({ durationMs, phase }) => {
|
|
25757
|
+
console.log(`[mobile:ios-release-test] ${phase} ${getDurationString(durationMs)}`);
|
|
25758
|
+
sendTelemetryEvent("mobile:ios-release-test-phase", {
|
|
25759
|
+
durationMs: Math.round(durationMs),
|
|
25760
|
+
engine: options.mobile.engine,
|
|
25761
|
+
phase,
|
|
25762
|
+
platform: "ios",
|
|
25763
|
+
provider: "remote-mac"
|
|
25764
|
+
});
|
|
25765
|
+
},
|
|
25766
|
+
...simulatorUdid ? { simulatorUdid } : {}
|
|
25767
|
+
});
|
|
25768
|
+
return {
|
|
25769
|
+
result: remoteAcceptance.result,
|
|
25770
|
+
targetId: remoteAcceptance.targetId
|
|
25771
|
+
};
|
|
25772
|
+
};
|
|
25773
|
+
var testIosRelease = async (args, mobile, projectRoot) => {
|
|
25774
|
+
const requested = valueAfter(args, "--release");
|
|
25775
|
+
if (!requested || requested.startsWith("--"))
|
|
25776
|
+
throw new TypeError("mobile test ios --release requires a release directory or release.json path.");
|
|
25777
|
+
if (!mobile.platforms.includes("ios"))
|
|
25778
|
+
throw new TypeError("mobile test ios requires ios in mobile.platforms.");
|
|
25779
|
+
const requestedRemote = valueAfter(args, "--remote");
|
|
25780
|
+
if (args.includes("--remote") && (!requestedRemote || requestedRemote.startsWith("-")))
|
|
25781
|
+
throw new TypeError("mobile test ios --release requires --remote <name>.");
|
|
25782
|
+
const remoteProfile = requestedRemote !== undefined || process.platform !== "darwin" ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
|
|
25783
|
+
if (process.platform !== "darwin" && !remoteProfile)
|
|
25784
|
+
throw new TypeError("iOS release acceptance requires macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
|
|
25785
|
+
const release = await readAbsoluteIosRelease(projectRoot, requested);
|
|
25786
|
+
if (release.metadata.appId !== mobile.appId)
|
|
25787
|
+
throw new TypeError(`iOS release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
|
|
25788
|
+
if (release.metadata.engine !== mobile.engine)
|
|
25789
|
+
throw new TypeError(`iOS release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
|
|
25790
|
+
if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
|
|
25791
|
+
throw new TypeError("Embedded-local Expo release acceptance requires mobile.entry to be a web route; native Expo routes may depend on trusted-server page data.");
|
|
25792
|
+
const requestedDevice = valueAfter(args, "--device");
|
|
25793
|
+
if (args.includes("--device") && !requestedDevice)
|
|
25794
|
+
throw new TypeError("mobile test ios --release --device requires a device identifier or name.");
|
|
25795
|
+
if (args.includes("--testflight") && !requestedDevice)
|
|
25796
|
+
throw new TypeError("mobile test ios --release --testflight requires --device.");
|
|
25797
|
+
if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
|
|
25798
|
+
throw new TypeError("mobile test ios --release --device cannot be combined with a simulator selector.");
|
|
25799
|
+
const xcrun = remoteProfile ? undefined : await requireIosXcrun();
|
|
25800
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "ios");
|
|
25801
|
+
const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
|
|
25802
|
+
const startedAt = performance.now();
|
|
25803
|
+
let targetId = requestedDevice ?? "ios-simulator";
|
|
25804
|
+
let remoteReportMetadata;
|
|
25805
|
+
if (remoteProfile && reportRoot) {
|
|
25806
|
+
const macos = await captureAbsoluteRemoteMacCommand(remoteProfile, [
|
|
25807
|
+
"/usr/bin/sw_vers",
|
|
25808
|
+
"-productVersion"
|
|
25809
|
+
]);
|
|
25810
|
+
if (macos.exitCode !== 0)
|
|
25811
|
+
throw new Error("Remote Mac version inspection failed.");
|
|
25812
|
+
remoteReportMetadata = {
|
|
25813
|
+
absolutejsVersion: await absolutejsVersionForReport(),
|
|
25814
|
+
bunVersion: Bun.version,
|
|
25815
|
+
macosVersion: macos.stdout.trim(),
|
|
25816
|
+
xcodeVersion: remoteProfile.xcodeVersion
|
|
25817
|
+
};
|
|
25818
|
+
}
|
|
25819
|
+
const cancellation = remoteProfile ? new AbortController : undefined;
|
|
25820
|
+
const stopListeningForCancellation = remoteProfile ? listenForRemoteReleaseCancellation(cancellation) : () => {
|
|
25821
|
+
return;
|
|
25822
|
+
};
|
|
25823
|
+
try {
|
|
25824
|
+
let acceptance;
|
|
25825
|
+
if (remoteProfile)
|
|
25826
|
+
acceptance = await runRemoteIosReleaseTarget({
|
|
25827
|
+
args,
|
|
25828
|
+
...cancellation ? { cancellation } : {},
|
|
25829
|
+
mobile,
|
|
25830
|
+
profile: remoteProfile,
|
|
25831
|
+
projectRoot,
|
|
25832
|
+
release,
|
|
25833
|
+
...requestedDevice ? { requestedDevice } : {}
|
|
25834
|
+
});
|
|
25835
|
+
else if (!xcrun)
|
|
25836
|
+
throw new Error("Local iOS release acceptance requires xcrun.");
|
|
25837
|
+
else if (requestedDevice)
|
|
25838
|
+
acceptance = await runIosPhysicalReleaseTarget({
|
|
25839
|
+
args,
|
|
25840
|
+
device: requestedDevice,
|
|
25841
|
+
release,
|
|
25842
|
+
xcrun
|
|
25843
|
+
});
|
|
25844
|
+
else
|
|
25845
|
+
acceptance = await runIosSimulatorReleaseTarget({
|
|
25846
|
+
args,
|
|
25847
|
+
artifactRoot,
|
|
25848
|
+
mobile,
|
|
25849
|
+
release,
|
|
25850
|
+
xcrun
|
|
25851
|
+
});
|
|
25852
|
+
const { result, targetId: acceptedTargetId } = acceptance;
|
|
25853
|
+
targetId = acceptedTargetId;
|
|
25854
|
+
sendTelemetryEvent("mobile:ios-release-conformance", {
|
|
25855
|
+
artifactExactness: result.artifactExactness,
|
|
25856
|
+
distribution: result.distribution,
|
|
25857
|
+
durationMs: result.durationMs,
|
|
25858
|
+
engine: result.engine,
|
|
25859
|
+
installMs: result.installMs,
|
|
25860
|
+
launchMs: result.launchMs,
|
|
25861
|
+
platform: "ios",
|
|
25862
|
+
relaunchMs: result.relaunchMs,
|
|
25863
|
+
remote: remoteProfile !== undefined,
|
|
25864
|
+
success: true
|
|
25865
|
+
});
|
|
25866
|
+
await writeRequestedIosReport({
|
|
25867
|
+
args,
|
|
25868
|
+
...remoteReportMetadata ? { metadata: remoteReportMetadata } : {},
|
|
25869
|
+
projectRoot,
|
|
25870
|
+
provider: mobile.engine,
|
|
25871
|
+
run: iosReleaseRun(result, mobile.appId, targetId, remoteProfile !== undefined),
|
|
25872
|
+
...xcrun ? { xcrun } : {}
|
|
25873
|
+
});
|
|
25874
|
+
printIosReleaseAcceptance(result, args.includes("--json"));
|
|
25875
|
+
return result;
|
|
25876
|
+
} catch (error) {
|
|
25877
|
+
const durationMs = Math.round(performance.now() - startedAt);
|
|
25878
|
+
sendTelemetryEvent("mobile:ios-release-conformance", {
|
|
25879
|
+
durationMs,
|
|
25880
|
+
engine: mobile.engine,
|
|
25881
|
+
platform: "ios",
|
|
25882
|
+
remote: remoteProfile !== undefined,
|
|
25883
|
+
success: false
|
|
25884
|
+
});
|
|
25885
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25886
|
+
const diagnosticPath = join16(artifactRoot, "ios-release-failure.json");
|
|
25887
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
25888
|
+
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
25889
|
+
platform: "ios",
|
|
25890
|
+
provider: mobile.engine,
|
|
25891
|
+
releaseId: release.metadata.releaseId,
|
|
25892
|
+
status: "fail"
|
|
25893
|
+
}, null, 2)}
|
|
25894
|
+
`);
|
|
25895
|
+
if (reportRoot)
|
|
25896
|
+
await writeRequestedIosReport({
|
|
25897
|
+
args,
|
|
25898
|
+
...remoteReportMetadata ? { metadata: remoteReportMetadata } : {},
|
|
25899
|
+
projectRoot,
|
|
25900
|
+
provider: mobile.engine,
|
|
25901
|
+
run: {
|
|
25902
|
+
appId: mobile.appId,
|
|
25903
|
+
durationMs,
|
|
25904
|
+
error: "Installed iOS release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
|
|
25905
|
+
hmrConnected: false,
|
|
25906
|
+
status: "fail",
|
|
25907
|
+
targetId,
|
|
25908
|
+
targetKind: requestedDevice ? "device" : "simulator"
|
|
25909
|
+
},
|
|
25910
|
+
...xcrun ? { xcrun } : {}
|
|
25911
|
+
});
|
|
25912
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
|
|
25913
|
+
} finally {
|
|
25914
|
+
stopListeningForCancellation();
|
|
25915
|
+
}
|
|
25916
|
+
};
|
|
25084
25917
|
var testIos = async (args) => {
|
|
25085
25918
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
25919
|
+
if (args.includes("--release"))
|
|
25920
|
+
return testIosRelease(args, mobile, projectRoot);
|
|
25086
25921
|
requireCapacitorEngine(mobile, "mobile test ios");
|
|
25087
25922
|
const { https, instance, port } = requireIosTestContext(args, projectRoot);
|
|
25088
25923
|
if (!mobile.platforms.includes("ios"))
|
|
@@ -25142,8 +25977,8 @@ var testIos = async (args) => {
|
|
|
25142
25977
|
mobile.appId
|
|
25143
25978
|
], "iOS app launch");
|
|
25144
25979
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
25145
|
-
await
|
|
25146
|
-
const screenshot =
|
|
25980
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25981
|
+
const screenshot = join16(artifactRoot, "ios-simulator.png");
|
|
25147
25982
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
25148
25983
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
25149
25984
|
const report = {
|
|
@@ -25329,7 +26164,7 @@ var runMobile = async (args) => {
|
|
|
25329
26164
|
await publishIos(args.slice(2));
|
|
25330
26165
|
return;
|
|
25331
26166
|
}
|
|
25332
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update 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]");
|
|
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]");
|
|
25333
26168
|
};
|
|
25334
26169
|
export {
|
|
25335
26170
|
runMobile
|