@absolutejs/absolute 0.20.0-beta.21 → 0.20.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +605 -58
- package/dist/mobile/index.js +210 -16
- package/dist/mobile/index.js.map +4 -4
- package/dist/src/mobile/androidTestReport.d.ts +14 -0
- package/dist/src/mobile/deviceCapabilities.d.ts +7 -1
- package/dist/src/mobile/iosTestReport.d.ts +23 -0
- package/dist/src/mobile/nativeTestReport.d.ts +72 -0
- package/package.json +11 -9
package/dist/cli/index.js
CHANGED
|
@@ -6763,7 +6763,7 @@ var init_syncSchema = __esm(() => {
|
|
|
6763
6763
|
import { readFileSync as readFileSync12 } from "fs";
|
|
6764
6764
|
import { extname as extname5, join as join20, relative as relative11, resolve as resolve16 } from "path";
|
|
6765
6765
|
import ts4 from "typescript";
|
|
6766
|
-
var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/devices-capacitor", SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, CAPACITOR_MODULE_PATTERN, CAPACITOR_PACKAGE_PATTERN, ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
6766
|
+
var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/devices-capacitor", SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, CAPACITOR_MODULE_PATTERN, CAPACITOR_PACKAGE_PATTERN, ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
|
|
6767
6767
|
const value = JSON.parse(readFileSync12(path, "utf8"));
|
|
6768
6768
|
if (!object2(value))
|
|
6769
6769
|
throw new TypeError(`${path} must contain an object.`);
|
|
@@ -6781,15 +6781,37 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
6781
6781
|
if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
|
|
6782
6782
|
throw new TypeError(`${field}.permissions must contain Android permission names.`);
|
|
6783
6783
|
return [...permissions];
|
|
6784
|
-
},
|
|
6784
|
+
}, iosPrivacyAccessedApis = (value, field) => {
|
|
6785
6785
|
if (value === undefined)
|
|
6786
6786
|
return;
|
|
6787
6787
|
if (!object2(value))
|
|
6788
6788
|
throw new TypeError(`${field} must be an object.`);
|
|
6789
|
-
const
|
|
6790
|
-
|
|
6789
|
+
const privacy = {};
|
|
6790
|
+
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
6791
|
+
const reasons = value[api];
|
|
6792
|
+
if (reasons === undefined)
|
|
6793
|
+
continue;
|
|
6794
|
+
const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
|
|
6795
|
+
if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
|
|
6796
|
+
throw new TypeError(`${field} contains an unsupported API or reason.`);
|
|
6797
|
+
privacy[api] = [...reasons];
|
|
6798
|
+
}
|
|
6799
|
+
if (Object.keys(value).some((api) => !IOS_PRIVACY_ACCESSED_APIS.some((known) => known === api)))
|
|
6800
|
+
throw new TypeError(`${field} contains an unsupported API or reason.`);
|
|
6801
|
+
return privacy;
|
|
6802
|
+
}, iosNativeRequirements = (value, field) => {
|
|
6803
|
+
if (value === undefined)
|
|
6804
|
+
return;
|
|
6805
|
+
if (!object2(value))
|
|
6806
|
+
throw new TypeError(`${field} must be an object.`);
|
|
6807
|
+
const { privacyAccessedApis, usageDescriptions } = value;
|
|
6808
|
+
if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
|
|
6791
6809
|
throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
|
|
6792
|
-
|
|
6810
|
+
const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
|
|
6811
|
+
return {
|
|
6812
|
+
...privacy === undefined ? {} : { privacyAccessedApis: privacy },
|
|
6813
|
+
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
6814
|
+
};
|
|
6793
6815
|
}, parseProvider = (name, value) => {
|
|
6794
6816
|
if (!IDENTIFIER_PATTERN.test(name))
|
|
6795
6817
|
throw new TypeError("Device capability names must be identifiers.");
|
|
@@ -6810,10 +6832,10 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
6810
6832
|
throw new TypeError(`${name}.native must be an object.`);
|
|
6811
6833
|
const { android, ios } = nativeMetadata;
|
|
6812
6834
|
const permissions = androidPermissions(android, `${name}.native.android`);
|
|
6813
|
-
const
|
|
6835
|
+
const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
|
|
6814
6836
|
native = {
|
|
6815
6837
|
...permissions === undefined ? {} : { android: { permissions } },
|
|
6816
|
-
...
|
|
6838
|
+
...iosRequirements === undefined ? {} : { ios: iosRequirements }
|
|
6817
6839
|
};
|
|
6818
6840
|
}
|
|
6819
6841
|
return {
|
|
@@ -6822,14 +6844,32 @@ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/de
|
|
|
6822
6844
|
...native === undefined ? {} : { native },
|
|
6823
6845
|
packages: [...value.packages]
|
|
6824
6846
|
};
|
|
6825
|
-
}, absoluteDeviceNativeRequirements = (plan) =>
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6847
|
+
}, absoluteDeviceNativeRequirements = (plan) => {
|
|
6848
|
+
const privacy = plan.capabilities.reduce((requirements, name) => {
|
|
6849
|
+
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
6850
|
+
const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
|
|
6851
|
+
if (reasons.length === 0)
|
|
6852
|
+
continue;
|
|
6853
|
+
const current = requirements[api] ?? new Set;
|
|
6854
|
+
for (const reason of reasons)
|
|
6855
|
+
current.add(reason);
|
|
6856
|
+
requirements[api] = current;
|
|
6857
|
+
}
|
|
6858
|
+
return requirements;
|
|
6859
|
+
}, {});
|
|
6860
|
+
return {
|
|
6861
|
+
androidPermissions: [
|
|
6862
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
|
|
6863
|
+
].sort(),
|
|
6864
|
+
iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
|
|
6865
|
+
const reasons = privacy[api];
|
|
6866
|
+
return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
|
|
6867
|
+
}),
|
|
6868
|
+
iosUsageDescriptions: [
|
|
6869
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
6870
|
+
].sort()
|
|
6871
|
+
};
|
|
6872
|
+
}, loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
|
|
6833
6873
|
const path = join20(resolve16(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
|
|
6834
6874
|
const manifest = readJson(path);
|
|
6835
6875
|
const { absolutejs } = manifest;
|
|
@@ -6953,6 +6993,12 @@ var init_deviceCapabilities = __esm(() => {
|
|
|
6953
6993
|
"photo-library",
|
|
6954
6994
|
"photo-library-add"
|
|
6955
6995
|
]);
|
|
6996
|
+
IOS_PRIVACY_ACCESSED_API_REASONS = {
|
|
6997
|
+
NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
|
|
6998
|
+
};
|
|
6999
|
+
IOS_PRIVACY_ACCESSED_APIS = [
|
|
7000
|
+
"NSPrivacyAccessedAPICategoryFileTimestamp"
|
|
7001
|
+
];
|
|
6956
7002
|
});
|
|
6957
7003
|
|
|
6958
7004
|
// src/mobile/buildPipeline.ts
|
|
@@ -16469,7 +16515,7 @@ var init_nativeDeepLinks = () => {};
|
|
|
16469
16515
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
16470
16516
|
import { readFile as readFile12, rename as rename9, writeFile as writeFile10 } from "fs/promises";
|
|
16471
16517
|
import { join as join47 } from "path";
|
|
16472
|
-
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->", NOT_FOUND2 = -1, escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">"), writeChangedFile2 = async (path, source) => {
|
|
16518
|
+
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->", NOT_FOUND2 = -1, IOS_PRIVACY_FILE_REFERENCE = "A85D0C000000000000000001", IOS_PRIVACY_BUILD_FILE = "A85D0C000000000000000002", escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">"), writeChangedFile2 = async (path, source) => {
|
|
16473
16519
|
const current = await readFile12(path, "utf8");
|
|
16474
16520
|
if (current === source)
|
|
16475
16521
|
return false;
|
|
@@ -16477,6 +16523,14 @@ var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2
|
|
|
16477
16523
|
await writeFile10(temporary, source, { flag: "wx" });
|
|
16478
16524
|
await rename9(temporary, path);
|
|
16479
16525
|
return true;
|
|
16526
|
+
}, optionalSource = async (path) => {
|
|
16527
|
+
try {
|
|
16528
|
+
return await readFile12(path, "utf8");
|
|
16529
|
+
} catch (error) {
|
|
16530
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
16531
|
+
return null;
|
|
16532
|
+
throw error;
|
|
16533
|
+
}
|
|
16480
16534
|
}, managed = (source, region, insertion) => {
|
|
16481
16535
|
const start2 = source.indexOf(START_MARKER2);
|
|
16482
16536
|
const end = source.indexOf(END_MARKER2);
|
|
@@ -16505,6 +16559,129 @@ var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2
|
|
|
16505
16559
|
if (purpose === "location-always")
|
|
16506
16560
|
return `${appName} does not track location in the background; this description supports the foreground location provider required by the native runtime.`;
|
|
16507
16561
|
return `${appName} adds to your photo library only for photo actions you choose.`;
|
|
16562
|
+
}, privacyEntries = (requirements) => requirements.iosPrivacyAccessedApis.map(({ api, reasons }) => ` <dict>
|
|
16563
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
16564
|
+
<string>${escapeXml2(api)}</string>
|
|
16565
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
16566
|
+
<array>
|
|
16567
|
+
${reasons.map((reason) => ` <string>${escapeXml2(reason)}</string>`).join(`
|
|
16568
|
+
`)}
|
|
16569
|
+
</array>
|
|
16570
|
+
</dict>`).join(`
|
|
16571
|
+
`), privacyManifestSource = (source, requirements) => {
|
|
16572
|
+
const entries = privacyEntries(requirements);
|
|
16573
|
+
const wholeRegion = entries ? ` ${START_MARKER2}
|
|
16574
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
16575
|
+
<array>
|
|
16576
|
+
${entries}
|
|
16577
|
+
</array>
|
|
16578
|
+
${END_MARKER2}
|
|
16579
|
+
` : "";
|
|
16580
|
+
if (source === null)
|
|
16581
|
+
return entries ? `<?xml version="1.0" encoding="UTF-8"?>
|
|
16582
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
16583
|
+
<plist version="1.0">
|
|
16584
|
+
<dict>
|
|
16585
|
+
${wholeRegion} <key>NSPrivacyCollectedDataTypes</key>
|
|
16586
|
+
<array/>
|
|
16587
|
+
<key>NSPrivacyTracking</key>
|
|
16588
|
+
<false/>
|
|
16589
|
+
</dict>
|
|
16590
|
+
</plist>
|
|
16591
|
+
` : null;
|
|
16592
|
+
const start2 = source.indexOf(START_MARKER2);
|
|
16593
|
+
if (start2 !== NOT_FOUND2) {
|
|
16594
|
+
const end = source.indexOf(END_MARKER2);
|
|
16595
|
+
if (end === NOT_FOUND2)
|
|
16596
|
+
throw new TypeError("AbsoluteJS device-capability ownership markers are malformed.");
|
|
16597
|
+
const owned = source.slice(start2, end);
|
|
16598
|
+
const ownsWholeKey = owned.includes("NSPrivacyAccessedAPITypes");
|
|
16599
|
+
let region = "";
|
|
16600
|
+
if (ownsWholeKey)
|
|
16601
|
+
region = wholeRegion;
|
|
16602
|
+
else if (entries)
|
|
16603
|
+
region = ` ${START_MARKER2}
|
|
16604
|
+
${entries}
|
|
16605
|
+
${END_MARKER2}
|
|
16606
|
+
`;
|
|
16607
|
+
return managed(source, region, source.lastIndexOf("</dict>"));
|
|
16608
|
+
}
|
|
16609
|
+
const key = source.indexOf("<key>NSPrivacyAccessedAPITypes</key>");
|
|
16610
|
+
if (key === NOT_FOUND2)
|
|
16611
|
+
return managed(source, wholeRegion, source.lastIndexOf("</dict>"));
|
|
16612
|
+
if (!entries)
|
|
16613
|
+
return source;
|
|
16614
|
+
const array = source.indexOf("<array>", key);
|
|
16615
|
+
if (array === NOT_FOUND2)
|
|
16616
|
+
throw new TypeError("iOS PrivacyInfo.xcprivacy has a malformed NSPrivacyAccessedAPITypes value.");
|
|
16617
|
+
const insertion = source.indexOf(`
|
|
16618
|
+
`, array);
|
|
16619
|
+
if (insertion === NOT_FOUND2)
|
|
16620
|
+
throw new TypeError("iOS PrivacyInfo.xcprivacy array is malformed.");
|
|
16621
|
+
return managed(source, ` ${START_MARKER2}
|
|
16622
|
+
${entries}
|
|
16623
|
+
${END_MARKER2}
|
|
16624
|
+
`, insertion + 1);
|
|
16625
|
+
}, writeIosPrivacyManifest = async (path, current, source) => {
|
|
16626
|
+
if (source === null)
|
|
16627
|
+
return false;
|
|
16628
|
+
if (current !== null)
|
|
16629
|
+
return writeChangedFile2(path, source);
|
|
16630
|
+
await writeFile10(path, source, { flag: "wx" });
|
|
16631
|
+
return true;
|
|
16632
|
+
}, configureIosPrivacyProject = async (config, requirements) => {
|
|
16633
|
+
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
16634
|
+
return false;
|
|
16635
|
+
const projectPath = join47(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
16636
|
+
const project = await readFile12(projectPath, "utf8");
|
|
16637
|
+
return writeChangedFile2(projectPath, addIosPrivacyProjectReference(project));
|
|
16638
|
+
}, addIosPrivacyProjectReference = (source) => {
|
|
16639
|
+
const fileMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy \*\/ = \{isa = PBXFileReference;/u);
|
|
16640
|
+
const fileReference = fileMatch?.[1] ?? IOS_PRIVACY_FILE_REFERENCE;
|
|
16641
|
+
const buildMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy in Resources \*\/ = \{isa = PBXBuildFile;/u);
|
|
16642
|
+
const buildFile = buildMatch?.[1] ?? IOS_PRIVACY_BUILD_FILE;
|
|
16643
|
+
if (!fileMatch && source.includes(fileReference) || !buildMatch && source.includes(buildFile))
|
|
16644
|
+
throw new TypeError("AbsoluteJS iOS privacy-manifest identifiers collide.");
|
|
16645
|
+
let next = source;
|
|
16646
|
+
if (!buildMatch) {
|
|
16647
|
+
const marker = "/* End PBXBuildFile section */";
|
|
16648
|
+
const index = next.indexOf(marker);
|
|
16649
|
+
if (index === NOT_FOUND2)
|
|
16650
|
+
throw new TypeError("Could not find the iOS PBXBuildFile section.");
|
|
16651
|
+
next = `${next.slice(0, index)} ${buildFile} /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = ${fileReference} /* PrivacyInfo.xcprivacy */; };
|
|
16652
|
+
${next.slice(index)}`;
|
|
16653
|
+
}
|
|
16654
|
+
if (!fileMatch) {
|
|
16655
|
+
const marker = "/* End PBXFileReference section */";
|
|
16656
|
+
const index = next.indexOf(marker);
|
|
16657
|
+
if (index === NOT_FOUND2)
|
|
16658
|
+
throw new TypeError("Could not find the iOS PBXFileReference section.");
|
|
16659
|
+
next = `${next.slice(0, index)} ${fileReference} /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
|
16660
|
+
${next.slice(index)}`;
|
|
16661
|
+
}
|
|
16662
|
+
const groupsStart = next.indexOf("/* Begin PBXGroup section */");
|
|
16663
|
+
const groupsEnd = next.indexOf("/* End PBXGroup section */");
|
|
16664
|
+
const groups = next.slice(groupsStart, groupsEnd);
|
|
16665
|
+
if (!groups.includes(`${fileReference} /* PrivacyInfo.xcprivacy */`)) {
|
|
16666
|
+
const appGroup = groups.match(/[A-F0-9]{24} \/\* App \*\/ = \{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = \(\n/u);
|
|
16667
|
+
if (!appGroup || appGroup.index === undefined)
|
|
16668
|
+
throw new TypeError("Could not find the iOS App PBXGroup.");
|
|
16669
|
+
const index = groupsStart + appGroup.index + appGroup[0].length;
|
|
16670
|
+
next = `${next.slice(0, index)} ${fileReference} /* PrivacyInfo.xcprivacy */,
|
|
16671
|
+
${next.slice(index)}`;
|
|
16672
|
+
}
|
|
16673
|
+
const resourcesStart = next.indexOf("/* Begin PBXResourcesBuildPhase section */");
|
|
16674
|
+
const resourcesEnd = next.indexOf("/* End PBXResourcesBuildPhase section */");
|
|
16675
|
+
const resources = next.slice(resourcesStart, resourcesEnd);
|
|
16676
|
+
if (!resources.includes(`${buildFile} /* PrivacyInfo.xcprivacy in Resources */`)) {
|
|
16677
|
+
const files = resources.match(/isa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = \d+;\n\t\t\tfiles = \(\n/u);
|
|
16678
|
+
if (!files || files.index === undefined)
|
|
16679
|
+
throw new TypeError("Could not find the iOS Resources build phase.");
|
|
16680
|
+
const index = resourcesStart + files.index + files[0].length;
|
|
16681
|
+
next = `${next.slice(0, index)} ${buildFile} /* PrivacyInfo.xcprivacy in Resources */,
|
|
16682
|
+
${next.slice(index)}`;
|
|
16683
|
+
}
|
|
16684
|
+
return next;
|
|
16508
16685
|
}, configureIos2 = async (config, plan) => {
|
|
16509
16686
|
const path = join47(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
16510
16687
|
const source = await readFile12(path, "utf8");
|
|
@@ -16516,7 +16693,15 @@ var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2
|
|
|
16516
16693
|
${content}
|
|
16517
16694
|
${END_MARKER2}
|
|
16518
16695
|
` : "";
|
|
16519
|
-
|
|
16696
|
+
const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
|
|
16697
|
+
const privacyPath = join47(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
|
|
16698
|
+
const privacyCurrent = await optionalSource(privacyPath);
|
|
16699
|
+
const privacySource = privacyManifestSource(privacyCurrent, requirements);
|
|
16700
|
+
const [privacyChanged, projectChanged] = await Promise.all([
|
|
16701
|
+
writeIosPrivacyManifest(privacyPath, privacyCurrent, privacySource),
|
|
16702
|
+
configureIosPrivacyProject(config, requirements)
|
|
16703
|
+
]);
|
|
16704
|
+
return infoChanged || privacyChanged || projectChanged;
|
|
16520
16705
|
}, configureAndroid2 = async (config, plan) => {
|
|
16521
16706
|
const path = join47(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
16522
16707
|
const source = await readFile12(path, "utf8");
|
|
@@ -17706,6 +17891,228 @@ var init_iosConformance = __esm(() => {
|
|
|
17706
17891
|
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");
|
|
17707
17892
|
});
|
|
17708
17893
|
|
|
17894
|
+
// src/mobile/nativeTestReport.ts
|
|
17895
|
+
import { mkdir as mkdir13, readFile as readFile18, writeFile as writeFile15 } from "fs/promises";
|
|
17896
|
+
import { join as join51 } from "path";
|
|
17897
|
+
var secretPattern, bearerPattern, coordinatePattern, sanitizeNativeReportText = (value) => value.replace(bearerPattern, "Bearer [REDACTED]").replace(secretPattern, "$1$2[REDACTED]").replace(coordinatePattern, "$1$2[REDACTED]").replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/giu, "$1?[REDACTED]"), markdownCell = (value) => sanitizeNativeReportText(value).replaceAll("|", "\\|").replaceAll(`
|
|
17898
|
+
`, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
|
|
17899
|
+
const target = `${run.targetKind} ${run.targetId}`;
|
|
17900
|
+
const evidence = run.screenshot ? `Screenshot: ${run.screenshot}` : undefined;
|
|
17901
|
+
let hmrResult = "NOT_RUN";
|
|
17902
|
+
if (run.hmr)
|
|
17903
|
+
hmrResult = run.hmr.outcome === "failed" ? "FAIL" : "PASS";
|
|
17904
|
+
const routeDetails = run.routes?.length ? ` Routes: ${run.routes.join(", ")}.` : "";
|
|
17905
|
+
return [
|
|
17906
|
+
{
|
|
17907
|
+
details: `Captured host, toolchain, Bun, and AbsoluteJS metadata for ${target}.`,
|
|
17908
|
+
id: "AUTO-SETUP-01",
|
|
17909
|
+
result: "PASS"
|
|
17910
|
+
},
|
|
17911
|
+
{
|
|
17912
|
+
details: 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."}`,
|
|
17913
|
+
...evidence ? { evidence } : {},
|
|
17914
|
+
id: "AUTO-DEV-01",
|
|
17915
|
+
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
17916
|
+
},
|
|
17917
|
+
{
|
|
17918
|
+
details: run.hmr ? `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)`}.` : "Correlated edit timing was not requested. Rerun with --wait-for-hmr.",
|
|
17919
|
+
id: "AUTO-HMR-01",
|
|
17920
|
+
result: hmrResult
|
|
17921
|
+
},
|
|
17922
|
+
{
|
|
17923
|
+
details: run.screenshot ? "A target screenshot was captured. Visually review it before sharing this directory." : "No target screenshot was captured.",
|
|
17924
|
+
...evidence ? { evidence } : {},
|
|
17925
|
+
id: "AUTO-ARTIFACT-01",
|
|
17926
|
+
result: run.screenshot ? "PASS" : "FAIL"
|
|
17927
|
+
}
|
|
17928
|
+
];
|
|
17929
|
+
}, createAbsoluteNativeTestReport = (options) => ({
|
|
17930
|
+
automatedChecks: options.automatedChecks ?? createAbsoluteNativeAutomatedChecks(options.run),
|
|
17931
|
+
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
17932
|
+
manualChecks: options.manualChecks.map(([id, details]) => ({
|
|
17933
|
+
details,
|
|
17934
|
+
id,
|
|
17935
|
+
result: "NOT_RUN"
|
|
17936
|
+
})),
|
|
17937
|
+
metadata: options.metadata,
|
|
17938
|
+
overallResult: options.run.status === "fail" ? "FAIL" : "INCOMPLETE",
|
|
17939
|
+
platform: options.run.platform,
|
|
17940
|
+
reportVersion: 1,
|
|
17941
|
+
run: options.run
|
|
17942
|
+
}), readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
17943
|
+
const manifest = JSON.parse(await readFile18(packageJsonPath, "utf8"));
|
|
17944
|
+
if (typeof manifest !== "object" || manifest === null)
|
|
17945
|
+
return "unknown";
|
|
17946
|
+
const version2 = Reflect.get(manifest, "version");
|
|
17947
|
+
return typeof version2 === "string" ? version2 : "unknown";
|
|
17948
|
+
}, renderAbsoluteNativeTestReport = (report) => {
|
|
17949
|
+
const table = (checks) => checks.map((check2) => `| ${check2.id} | ${check2.result} | ${markdownCell(check2.details)} | ${markdownCell(check2.evidence ?? "")} |`).join(`
|
|
17950
|
+
`);
|
|
17951
|
+
const metadata = Object.entries(report.metadata).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `- ${key}: ${markdownCell(value)}`).join(`
|
|
17952
|
+
`);
|
|
17953
|
+
return `# AbsoluteJS ${report.platform} test report
|
|
17954
|
+
|
|
17955
|
+
- Overall result: ${report.overallResult}
|
|
17956
|
+
- Generated: ${report.generatedAt}
|
|
17957
|
+
- App bundle ID: ${markdownCell(report.run.appId)}
|
|
17958
|
+
- Target: ${report.run.targetKind} ${markdownCell(report.run.targetId)}
|
|
17959
|
+
- Automated run: ${report.run.status.toUpperCase()} (${report.run.durationMs}ms)
|
|
17960
|
+
${metadata}
|
|
17961
|
+
|
|
17962
|
+
This report is local and is never uploaded by AbsoluteJS. Before sharing its directory, visually inspect screenshots and logs. Never add passwords, signing material, tokens, cookies, private Sync data, or exact coordinates.
|
|
17963
|
+
|
|
17964
|
+
## Automated checks
|
|
17965
|
+
|
|
17966
|
+
| Test ID | Result | Observed result / timing | Evidence |
|
|
17967
|
+
| --- | --- | --- | --- |
|
|
17968
|
+
${table(report.automatedChecks)}
|
|
17969
|
+
|
|
17970
|
+
## Manual checklist
|
|
17971
|
+
|
|
17972
|
+
Replace each \`NOT_RUN\` with \`PASS\`, \`FAIL\`, or \`SKIPPED\` after completing the platform runbook. A failure must name sanitized evidence and state actual versus expected behavior.
|
|
17973
|
+
|
|
17974
|
+
| Test ID | Result | Observed result / timing | Evidence or failure details |
|
|
17975
|
+
| --- | --- | --- | --- |
|
|
17976
|
+
${table(report.manualChecks)}
|
|
17977
|
+
`;
|
|
17978
|
+
}, writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
17979
|
+
await mkdir13(directory, { recursive: true });
|
|
17980
|
+
const jsonPath = join51(directory, "report.json");
|
|
17981
|
+
const markdownPath = join51(directory, "report.md");
|
|
17982
|
+
await Promise.all([
|
|
17983
|
+
writeFile15(jsonPath, `${JSON.stringify(report, null, 2)}
|
|
17984
|
+
`),
|
|
17985
|
+
writeFile15(markdownPath, renderAbsoluteNativeTestReport(report))
|
|
17986
|
+
]);
|
|
17987
|
+
return { directory, jsonPath, markdownPath };
|
|
17988
|
+
};
|
|
17989
|
+
var init_nativeTestReport = __esm(() => {
|
|
17990
|
+
secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
|
|
17991
|
+
bearerPattern = /bearer\s+[^\s,;]+/giu;
|
|
17992
|
+
coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
|
|
17993
|
+
});
|
|
17994
|
+
|
|
17995
|
+
// src/mobile/iosTestReport.ts
|
|
17996
|
+
var MANUAL_CHECKS, readPackageVersionForIosReport, sanitizeIosReportText, writeAbsoluteIosPartnerReport, createAbsoluteIosPartnerReport = (options) => {
|
|
17997
|
+
const { udid, ...run } = options.run;
|
|
17998
|
+
return createAbsoluteNativeTestReport({
|
|
17999
|
+
...options.generatedAt ? { generatedAt: options.generatedAt } : {},
|
|
18000
|
+
manualChecks: MANUAL_CHECKS,
|
|
18001
|
+
metadata: {
|
|
18002
|
+
absolutejsVersion: options.absolutejsVersion,
|
|
18003
|
+
bunVersion: options.bunVersion,
|
|
18004
|
+
macosVersion: options.macosVersion,
|
|
18005
|
+
provider: "capacitor",
|
|
18006
|
+
xcodeVersion: options.xcodeVersion
|
|
18007
|
+
},
|
|
18008
|
+
run: {
|
|
18009
|
+
...run,
|
|
18010
|
+
platform: "ios",
|
|
18011
|
+
targetId: udid,
|
|
18012
|
+
targetKind: "simulator"
|
|
18013
|
+
}
|
|
18014
|
+
});
|
|
18015
|
+
};
|
|
18016
|
+
var init_iosTestReport = __esm(() => {
|
|
18017
|
+
init_nativeTestReport();
|
|
18018
|
+
MANUAL_CHECKS = [
|
|
18019
|
+
[
|
|
18020
|
+
"SETUP-01",
|
|
18021
|
+
"Confirm and record all Mac, Xcode, Bun, device, and package versions."
|
|
18022
|
+
],
|
|
18023
|
+
["SETUP-02", "Confirm Xcode setup and the required iOS runtime."],
|
|
18024
|
+
["SETUP-03", "Confirm the runbook package versions are installed."],
|
|
18025
|
+
["SETUP-04", "Confirm the staging bundle ID and production server origin."],
|
|
18026
|
+
["SETUP-05", "Confirm generated iOS project signing and Xcode warnings."],
|
|
18027
|
+
["DEV-01", "Record cold and warm bun dev startup timings."],
|
|
18028
|
+
["DEV-02", "Complete route traversal, HMR, relaunch, and recovery checks."],
|
|
18029
|
+
["CAP-01", "Complete automatic device-capability provisioning checks."],
|
|
18030
|
+
...Array.from({ length: 8 }, (_, index) => [
|
|
18031
|
+
`FILES-${String(index + 1).padStart(2, "0")}`,
|
|
18032
|
+
`Complete provider-neutral Documents runbook check FILES-${String(index + 1).padStart(2, "0")}.`
|
|
18033
|
+
]),
|
|
18034
|
+
...Array.from({ length: 14 }, (_, index) => [
|
|
18035
|
+
`LOC-${String(index + 1).padStart(2, "0")}`,
|
|
18036
|
+
`Complete foreground-location runbook check LOC-${String(index + 1).padStart(2, "0")} without recording exact coordinates.`
|
|
18037
|
+
]),
|
|
18038
|
+
[
|
|
18039
|
+
"AUTH-01",
|
|
18040
|
+
"Complete system-browser sign-in, callback, restore, and sign-out checks."
|
|
18041
|
+
],
|
|
18042
|
+
[
|
|
18043
|
+
"SYNC-01",
|
|
18044
|
+
"Complete online, offline, reconnect, isolation, and conflict checks."
|
|
18045
|
+
],
|
|
18046
|
+
["BGSYNC-01", "Complete physical-device background Sync acceptance."],
|
|
18047
|
+
["REMOTE-01", "Complete remote-Mac acceptance, or mark SKIPPED."],
|
|
18048
|
+
["BUILD-01", "Pass release doctor and produce a signed IPA."],
|
|
18049
|
+
[
|
|
18050
|
+
"SHIP-01",
|
|
18051
|
+
"Upload, process, assign, install, and launch the TestFlight build."
|
|
18052
|
+
],
|
|
18053
|
+
["UPDATE-01", "Prove upload retry reuse and a subsequent web-only update."],
|
|
18054
|
+
[
|
|
18055
|
+
"REPORT-01",
|
|
18056
|
+
"Review this directory for sensitive content and complete every row."
|
|
18057
|
+
]
|
|
18058
|
+
];
|
|
18059
|
+
readPackageVersionForIosReport = readPackageVersionForNativeReport;
|
|
18060
|
+
sanitizeIosReportText = sanitizeNativeReportText;
|
|
18061
|
+
writeAbsoluteIosPartnerReport = writeAbsoluteNativeTestReport;
|
|
18062
|
+
});
|
|
18063
|
+
|
|
18064
|
+
// src/mobile/androidTestReport.ts
|
|
18065
|
+
var MANUAL_CHECKS2, createAbsoluteAndroidTestReport = (options) => {
|
|
18066
|
+
const { serial, ...run } = options.run;
|
|
18067
|
+
return createAbsoluteNativeTestReport({
|
|
18068
|
+
...options.generatedAt ? { generatedAt: options.generatedAt } : {},
|
|
18069
|
+
manualChecks: MANUAL_CHECKS2,
|
|
18070
|
+
metadata: {
|
|
18071
|
+
absolutejsVersion: options.absolutejsVersion,
|
|
18072
|
+
adbVersion: options.adbVersion,
|
|
18073
|
+
bunVersion: options.bunVersion,
|
|
18074
|
+
host: options.host,
|
|
18075
|
+
provider: "capacitor"
|
|
18076
|
+
},
|
|
18077
|
+
run: {
|
|
18078
|
+
...run,
|
|
18079
|
+
platform: "android",
|
|
18080
|
+
targetId: serial,
|
|
18081
|
+
targetKind: serial.startsWith("emulator-") ? "emulator" : "device"
|
|
18082
|
+
}
|
|
18083
|
+
});
|
|
18084
|
+
};
|
|
18085
|
+
var init_androidTestReport = __esm(() => {
|
|
18086
|
+
init_nativeTestReport();
|
|
18087
|
+
MANUAL_CHECKS2 = [
|
|
18088
|
+
[
|
|
18089
|
+
"SETUP-01",
|
|
18090
|
+
"Confirm Android SDK, emulator/device, Bun, and package versions."
|
|
18091
|
+
],
|
|
18092
|
+
["DEV-01", "Record cold and warm native startup timings."],
|
|
18093
|
+
["DEV-02", "Complete route traversal, HMR, relaunch, and recovery checks."],
|
|
18094
|
+
["CAP-01", "Complete automatic device-capability provisioning checks."],
|
|
18095
|
+
[
|
|
18096
|
+
"FILES-01",
|
|
18097
|
+
"Complete provider-neutral file pick, export, open, and cleanup checks."
|
|
18098
|
+
],
|
|
18099
|
+
[
|
|
18100
|
+
"AUTH-01",
|
|
18101
|
+
"Complete system-browser sign-in, callback, restore, and sign-out checks."
|
|
18102
|
+
],
|
|
18103
|
+
[
|
|
18104
|
+
"SYNC-01",
|
|
18105
|
+
"Complete online, offline, reconnect, isolation, and conflict checks."
|
|
18106
|
+
],
|
|
18107
|
+
["BGSYNC-01", "Complete WorkManager background Sync acceptance."],
|
|
18108
|
+
["BUILD-01", "Pass release doctor and produce a signed AAB."],
|
|
18109
|
+
[
|
|
18110
|
+
"REPORT-01",
|
|
18111
|
+
"Review this directory for sensitive content and complete every row."
|
|
18112
|
+
]
|
|
18113
|
+
];
|
|
18114
|
+
});
|
|
18115
|
+
|
|
17709
18116
|
// src/mobile/releasePublisher.ts
|
|
17710
18117
|
import { access as access10 } from "fs/promises";
|
|
17711
18118
|
import { isAbsolute as isAbsolute8, relative as relative26, resolve as resolve40, sep as sep7 } from "path";
|
|
@@ -17804,11 +18211,11 @@ var exports_mobile = {};
|
|
|
17804
18211
|
__export(exports_mobile, {
|
|
17805
18212
|
runMobile: () => runMobile
|
|
17806
18213
|
});
|
|
17807
|
-
import { access as access11, mkdir as
|
|
17808
|
-
import { join as
|
|
18214
|
+
import { access as access11, mkdir as mkdir14, readFile as readFile19, writeFile as writeFile16 } from "fs/promises";
|
|
18215
|
+
import { join as join52, resolve as resolve41 } from "path";
|
|
17809
18216
|
import { createInterface } from "readline/promises";
|
|
17810
18217
|
var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
|
|
17811
|
-
const manifest = JSON.parse(await
|
|
18218
|
+
const manifest = JSON.parse(await readFile19(join52(projectRoot, "package.json"), "utf8"));
|
|
17812
18219
|
if (!isRecord15(manifest))
|
|
17813
18220
|
throw new TypeError("Application package.json must contain an object.");
|
|
17814
18221
|
const names = new Set;
|
|
@@ -17821,7 +18228,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
|
|
|
17821
18228
|
return names;
|
|
17822
18229
|
}, resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
17823
18230
|
try {
|
|
17824
|
-
const manifest = JSON.parse(await
|
|
18231
|
+
const manifest = JSON.parse(await readFile19(join52(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
17825
18232
|
return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
17826
18233
|
} catch {
|
|
17827
18234
|
return;
|
|
@@ -17862,7 +18269,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
|
|
|
17862
18269
|
}
|
|
17863
18270
|
return value;
|
|
17864
18271
|
}, capacitorExecutable = async (projectRoot) => {
|
|
17865
|
-
const executable =
|
|
18272
|
+
const executable = join52(projectRoot, "node_modules", ".bin", "cap");
|
|
17866
18273
|
try {
|
|
17867
18274
|
await access11(executable);
|
|
17868
18275
|
return executable;
|
|
@@ -18183,7 +18590,7 @@ Mobile release transport checks failed.`);
|
|
|
18183
18590
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
18184
18591
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
18185
18592
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
18186
|
-
console.log(`Metadata: ${
|
|
18593
|
+
console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
|
|
18187
18594
|
return release;
|
|
18188
18595
|
} finally {
|
|
18189
18596
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -18286,7 +18693,7 @@ Mobile release transport checks failed.`);
|
|
|
18286
18693
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
18287
18694
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
18288
18695
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
18289
|
-
console.log(`Metadata: ${
|
|
18696
|
+
console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
|
|
18290
18697
|
return release;
|
|
18291
18698
|
} finally {
|
|
18292
18699
|
sendTelemetryEvent("mobile:ios-release-build", {
|
|
@@ -18393,7 +18800,7 @@ Mobile release transport checks failed.`);
|
|
|
18393
18800
|
checks.push({
|
|
18394
18801
|
id: "sync.storage-schema",
|
|
18395
18802
|
label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
|
|
18396
|
-
path:
|
|
18803
|
+
path: join52(projectRoot, "package.json"),
|
|
18397
18804
|
platform: "host",
|
|
18398
18805
|
status: "pass"
|
|
18399
18806
|
});
|
|
@@ -18401,7 +18808,7 @@ Mobile release transport checks failed.`);
|
|
|
18401
18808
|
checks.push({
|
|
18402
18809
|
id: "sync.storage-schema",
|
|
18403
18810
|
label: "Offline schema metadata is invalid",
|
|
18404
|
-
path:
|
|
18811
|
+
path: join52(projectRoot, "package.json"),
|
|
18405
18812
|
platform: "host",
|
|
18406
18813
|
remediation: error instanceof Error ? error.message : String(error),
|
|
18407
18814
|
status: "fail"
|
|
@@ -18554,12 +18961,12 @@ Emulator setup verification:`);
|
|
|
18554
18961
|
timeoutMs
|
|
18555
18962
|
});
|
|
18556
18963
|
}, writeAndroidFailureArtifacts = async (options) => {
|
|
18557
|
-
await
|
|
18558
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
18964
|
+
await mkdir14(options.artifactRoot, { recursive: true });
|
|
18965
|
+
const screenshot = options.session ? await options.session.screenshot(join52(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
18559
18966
|
return;
|
|
18560
18967
|
}) : undefined;
|
|
18561
|
-
const diagnosticPath =
|
|
18562
|
-
await
|
|
18968
|
+
const diagnosticPath = join52(options.artifactRoot, "android-failure.json");
|
|
18969
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
18563
18970
|
diagnostics: options.session?.diagnostics ?? [],
|
|
18564
18971
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
18565
18972
|
platform: "android",
|
|
@@ -18582,7 +18989,8 @@ Emulator setup verification:`);
|
|
|
18582
18989
|
const routes = valuesAfter(args, "--route");
|
|
18583
18990
|
if (routes.length === 0)
|
|
18584
18991
|
routes.push(mobile.entry);
|
|
18585
|
-
const
|
|
18992
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "android");
|
|
18993
|
+
const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts"));
|
|
18586
18994
|
const startedAt = performance.now();
|
|
18587
18995
|
let session;
|
|
18588
18996
|
try {
|
|
@@ -18621,6 +19029,30 @@ Emulator setup verification:`);
|
|
|
18621
19029
|
console.log(JSON.stringify(report, null, 2));
|
|
18622
19030
|
else
|
|
18623
19031
|
printAndroidTestReport(report);
|
|
19032
|
+
const screenshot = reportRoot ? await session.screenshot(join52(artifactRoot, "android-emulator.png")) : undefined;
|
|
19033
|
+
await writeRequestedAndroidReport({
|
|
19034
|
+
adb,
|
|
19035
|
+
args,
|
|
19036
|
+
projectRoot,
|
|
19037
|
+
run: {
|
|
19038
|
+
appId: mobile.appId,
|
|
19039
|
+
durationMs: report.durationMs,
|
|
19040
|
+
...hmrApply ? {
|
|
19041
|
+
hmr: {
|
|
19042
|
+
clientMs: hmrApply.clientMs,
|
|
19043
|
+
durationMs: hmrApply.duration,
|
|
19044
|
+
outcome: hmrApply.outcome,
|
|
19045
|
+
serverMs: hmrApply.serverMs
|
|
19046
|
+
}
|
|
19047
|
+
} : {},
|
|
19048
|
+
hmrConnected: true,
|
|
19049
|
+
port,
|
|
19050
|
+
routes: checks.map(({ route }) => route),
|
|
19051
|
+
...screenshot ? { screenshot } : {},
|
|
19052
|
+
serial,
|
|
19053
|
+
status: "pass"
|
|
19054
|
+
}
|
|
19055
|
+
});
|
|
18624
19056
|
return report;
|
|
18625
19057
|
} catch (error) {
|
|
18626
19058
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
@@ -18639,6 +19071,22 @@ Emulator setup verification:`);
|
|
|
18639
19071
|
serial,
|
|
18640
19072
|
session
|
|
18641
19073
|
});
|
|
19074
|
+
await writeRequestedAndroidReport({
|
|
19075
|
+
adb,
|
|
19076
|
+
args,
|
|
19077
|
+
projectRoot,
|
|
19078
|
+
run: {
|
|
19079
|
+
appId: mobile.appId,
|
|
19080
|
+
durationMs,
|
|
19081
|
+
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
19082
|
+
hmrConnected: false,
|
|
19083
|
+
port,
|
|
19084
|
+
routes,
|
|
19085
|
+
...screenshot ? { screenshot } : {},
|
|
19086
|
+
serial,
|
|
19087
|
+
status: "fail"
|
|
19088
|
+
}
|
|
19089
|
+
});
|
|
18642
19090
|
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}${screenshot ? `; screenshot: ${screenshot}` : ""}`, { cause: error });
|
|
18643
19091
|
} finally {
|
|
18644
19092
|
await session?.close();
|
|
@@ -18729,14 +19177,14 @@ Emulator setup verification:`);
|
|
|
18729
19177
|
if (!selected)
|
|
18730
19178
|
throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
|
|
18731
19179
|
return selected;
|
|
18732
|
-
},
|
|
19180
|
+
}, requireCapturedCommand = (command, label) => {
|
|
18733
19181
|
const result = captureCommand4(command);
|
|
18734
19182
|
if (result.exitCode !== 0)
|
|
18735
19183
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
18736
19184
|
return result;
|
|
18737
19185
|
}, writeIosFailureArtifacts = async (options) => {
|
|
18738
|
-
await
|
|
18739
|
-
const screenshot =
|
|
19186
|
+
await mkdir14(options.artifactRoot, { recursive: true });
|
|
19187
|
+
const screenshot = join52(options.artifactRoot, "ios-failure.png");
|
|
18740
19188
|
const screenshotResult = captureCommand4([
|
|
18741
19189
|
options.xcrun,
|
|
18742
19190
|
"simctl",
|
|
@@ -18745,8 +19193,8 @@ Emulator setup verification:`);
|
|
|
18745
19193
|
"screenshot",
|
|
18746
19194
|
screenshot
|
|
18747
19195
|
]);
|
|
18748
|
-
const diagnosticPath =
|
|
18749
|
-
await
|
|
19196
|
+
const diagnosticPath = join52(options.artifactRoot, "ios-failure.json");
|
|
19197
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
18750
19198
|
appId: options.appId,
|
|
18751
19199
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
18752
19200
|
platform: "ios",
|
|
@@ -18760,6 +19208,64 @@ Emulator setup verification:`);
|
|
|
18760
19208
|
diagnosticPath,
|
|
18761
19209
|
screenshot: screenshotResult.exitCode === 0 ? screenshot : undefined
|
|
18762
19210
|
};
|
|
19211
|
+
}, nativeReportRoot = (args, projectRoot, platform6) => {
|
|
19212
|
+
const index = args.indexOf("--report");
|
|
19213
|
+
if (index === NOT_FOUND3)
|
|
19214
|
+
return;
|
|
19215
|
+
const candidate = args[index + 1];
|
|
19216
|
+
const explicit = candidate?.startsWith("--") ? undefined : candidate;
|
|
19217
|
+
const timestamp = new Date().toISOString().replaceAll(":", "-");
|
|
19218
|
+
return safeArtifactRoot(projectRoot, explicit ?? `.absolutejs/mobile/test-reports/${platform6}-${timestamp}`);
|
|
19219
|
+
}, absolutejsVersionForReport = async () => {
|
|
19220
|
+
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
19221
|
+
const versions = await Promise.all([
|
|
19222
|
+
resolve41(import.meta.dir, "..", "..", "package.json"),
|
|
19223
|
+
resolve41(import.meta.dir, "..", "..", "..", "package.json")
|
|
19224
|
+
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
19225
|
+
for (const version2 of versions) {
|
|
19226
|
+
if (version2 === "unknown")
|
|
19227
|
+
continue;
|
|
19228
|
+
absolutejsVersion = version2;
|
|
19229
|
+
break;
|
|
19230
|
+
}
|
|
19231
|
+
return absolutejsVersion;
|
|
19232
|
+
}, writeRequestedAndroidReport = async (options) => {
|
|
19233
|
+
const reportRoot = nativeReportRoot(options.args, options.projectRoot, "android");
|
|
19234
|
+
if (!reportRoot)
|
|
19235
|
+
return;
|
|
19236
|
+
const adbVersion = requireCapturedCommand([options.adb, "version"], "ADB version inspection").stdout.trim();
|
|
19237
|
+
const report = createAbsoluteAndroidTestReport({
|
|
19238
|
+
absolutejsVersion: await absolutejsVersionForReport(),
|
|
19239
|
+
adbVersion,
|
|
19240
|
+
bunVersion: Bun.version,
|
|
19241
|
+
host: `${process.platform}-${process.arch}`,
|
|
19242
|
+
run: options.run
|
|
19243
|
+
});
|
|
19244
|
+
const paths = await writeAbsoluteNativeTestReport(reportRoot, report);
|
|
19245
|
+
const print = options.args.includes("--json") ? console.error : console.log;
|
|
19246
|
+
print(`Android test report: ${paths.markdownPath}`);
|
|
19247
|
+
print(`Return this report directory: ${paths.directory}`);
|
|
19248
|
+
return paths;
|
|
19249
|
+
}, iosReportMetadata = async (xcrun) => {
|
|
19250
|
+
const xcodebuildPath = requireCapturedCommand([xcrun, "--find", "xcodebuild"], "Xcode version inspection").stdout.trim();
|
|
19251
|
+
const xcodeVersion = requireCapturedCommand([xcodebuildPath, "-version"], "Xcode version inspection").stdout.trim();
|
|
19252
|
+
const macosVersion = requireCapturedCommand(["/usr/bin/sw_vers", "-productVersion"], "macOS version inspection").stdout.trim();
|
|
19253
|
+
return {
|
|
19254
|
+
absolutejsVersion: await absolutejsVersionForReport(),
|
|
19255
|
+
bunVersion: Bun.version,
|
|
19256
|
+
macosVersion,
|
|
19257
|
+
xcodeVersion
|
|
19258
|
+
};
|
|
19259
|
+
}, writeRequestedIosReport = async (options) => {
|
|
19260
|
+
const reportRoot = nativeReportRoot(options.args, options.projectRoot, "ios");
|
|
19261
|
+
if (!reportRoot)
|
|
19262
|
+
return;
|
|
19263
|
+
const metadata = await iosReportMetadata(options.xcrun);
|
|
19264
|
+
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({ ...metadata, run: options.run }));
|
|
19265
|
+
const print = options.args.includes("--json") ? console.error : console.log;
|
|
19266
|
+
print(`iOS partner report: ${paths.markdownPath}`);
|
|
19267
|
+
print(`Return this report directory: ${paths.directory}`);
|
|
19268
|
+
return paths;
|
|
18763
19269
|
}, testIos = async (args) => {
|
|
18764
19270
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
18765
19271
|
const { https, instance, port } = requireIosTestContext(args, projectRoot);
|
|
@@ -18770,10 +19276,11 @@ Emulator setup verification:`);
|
|
|
18770
19276
|
const timeoutMs = androidTestTimeout(args);
|
|
18771
19277
|
const xcrun = await requireIosXcrun();
|
|
18772
19278
|
const simulator = selectIosSimulator(xcrun, valueAfter(args, "--udid") ?? valueAfter(args, "--serial"));
|
|
18773
|
-
const
|
|
19279
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "ios");
|
|
19280
|
+
const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts"));
|
|
18774
19281
|
const startedAt = performance.now();
|
|
18775
19282
|
try {
|
|
18776
|
-
|
|
19283
|
+
requireCapturedCommand([
|
|
18777
19284
|
xcrun,
|
|
18778
19285
|
"simctl",
|
|
18779
19286
|
"get_app_container",
|
|
@@ -18781,7 +19288,7 @@ Emulator setup verification:`);
|
|
|
18781
19288
|
mobile.appId,
|
|
18782
19289
|
"app"
|
|
18783
19290
|
], "iOS installed-app inspection");
|
|
18784
|
-
|
|
19291
|
+
requireCapturedCommand([
|
|
18785
19292
|
xcrun,
|
|
18786
19293
|
"simctl",
|
|
18787
19294
|
"launch",
|
|
@@ -18790,9 +19297,9 @@ Emulator setup verification:`);
|
|
|
18790
19297
|
mobile.appId
|
|
18791
19298
|
], "iOS app launch");
|
|
18792
19299
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
18793
|
-
await
|
|
18794
|
-
const screenshot =
|
|
18795
|
-
|
|
19300
|
+
await mkdir14(artifactRoot, { recursive: true });
|
|
19301
|
+
const screenshot = join52(artifactRoot, "ios-simulator.png");
|
|
19302
|
+
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
18796
19303
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
18797
19304
|
const report = {
|
|
18798
19305
|
appId: mobile.appId,
|
|
@@ -18814,6 +19321,28 @@ Emulator setup verification:`);
|
|
|
18814
19321
|
waitedForHmr: args.includes("--wait-for-hmr")
|
|
18815
19322
|
});
|
|
18816
19323
|
printIosTestReport(report, args.includes("--json"));
|
|
19324
|
+
await writeRequestedIosReport({
|
|
19325
|
+
args,
|
|
19326
|
+
projectRoot,
|
|
19327
|
+
run: {
|
|
19328
|
+
appId: report.appId,
|
|
19329
|
+
durationMs: report.durationMs,
|
|
19330
|
+
...report.hmrApply ? {
|
|
19331
|
+
hmr: {
|
|
19332
|
+
...report.hmrApply.clientMs === undefined ? {} : { clientMs: report.hmrApply.clientMs },
|
|
19333
|
+
durationMs: report.hmrApply.duration,
|
|
19334
|
+
outcome: report.hmrApply.outcome,
|
|
19335
|
+
...report.hmrApply.serverMs === undefined ? {} : { serverMs: report.hmrApply.serverMs }
|
|
19336
|
+
}
|
|
19337
|
+
} : {},
|
|
19338
|
+
hmrConnected: report.hmrConnected,
|
|
19339
|
+
port: report.port,
|
|
19340
|
+
screenshot: report.screenshot,
|
|
19341
|
+
status: report.status,
|
|
19342
|
+
udid: report.udid
|
|
19343
|
+
},
|
|
19344
|
+
xcrun
|
|
19345
|
+
});
|
|
18817
19346
|
return report;
|
|
18818
19347
|
} catch (error) {
|
|
18819
19348
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
@@ -18832,6 +19361,21 @@ Emulator setup verification:`);
|
|
|
18832
19361
|
udid: simulator.udid,
|
|
18833
19362
|
xcrun
|
|
18834
19363
|
});
|
|
19364
|
+
await writeRequestedIosReport({
|
|
19365
|
+
args,
|
|
19366
|
+
projectRoot,
|
|
19367
|
+
run: {
|
|
19368
|
+
appId: mobile.appId,
|
|
19369
|
+
durationMs,
|
|
19370
|
+
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
19371
|
+
hmrConnected: false,
|
|
19372
|
+
port,
|
|
19373
|
+
...screenshot ? { screenshot } : {},
|
|
19374
|
+
status: "fail",
|
|
19375
|
+
udid: simulator.udid
|
|
19376
|
+
},
|
|
19377
|
+
xcrun
|
|
19378
|
+
});
|
|
18835
19379
|
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}${screenshot ? `; screenshot: ${screenshot}` : ""}`, { cause: error });
|
|
18836
19380
|
}
|
|
18837
19381
|
}, runMobile = async (args) => {
|
|
@@ -18888,7 +19432,7 @@ Emulator setup verification:`);
|
|
|
18888
19432
|
await publishIos(args.slice(2));
|
|
18889
19433
|
return;
|
|
18890
19434
|
}
|
|
18891
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | 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] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--wait-for-hmr] [--timeout ms] [--port n] [--udid id] [--artifacts dir] [--json]> [--config path]");
|
|
19435
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | 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] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--udid id] [--artifacts dir] [--json]> [--config path]");
|
|
18892
19436
|
};
|
|
18893
19437
|
var init_mobile = __esm(() => {
|
|
18894
19438
|
init_dependencies();
|
|
@@ -18910,6 +19454,9 @@ var init_mobile = __esm(() => {
|
|
|
18910
19454
|
init_iosRelease();
|
|
18911
19455
|
init_iosSimulatorController();
|
|
18912
19456
|
init_iosConformance();
|
|
19457
|
+
init_iosTestReport();
|
|
19458
|
+
init_androidTestReport();
|
|
19459
|
+
init_nativeTestReport();
|
|
18913
19460
|
init_releasePublisher();
|
|
18914
19461
|
init_start();
|
|
18915
19462
|
init_utils();
|
|
@@ -18939,8 +19486,8 @@ var init_mobile = __esm(() => {
|
|
|
18939
19486
|
"@capacitor/cli@8.5.0",
|
|
18940
19487
|
"@capacitor/android@8.5.0",
|
|
18941
19488
|
"@capacitor/ios@8.5.0",
|
|
18942
|
-
"@absolutejs/devices@0.
|
|
18943
|
-
"@absolutejs/devices-capacitor@0.
|
|
19489
|
+
"@absolutejs/devices@0.4.0",
|
|
19490
|
+
"@absolutejs/devices-capacitor@0.5.0"
|
|
18944
19491
|
];
|
|
18945
19492
|
CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
18946
19493
|
"@absolutejs/sync-capacitor@0.9.1",
|
|
@@ -18953,9 +19500,9 @@ var exports_typecheck = {};
|
|
|
18953
19500
|
__export(exports_typecheck, {
|
|
18954
19501
|
typecheck: () => typecheck
|
|
18955
19502
|
});
|
|
18956
|
-
import { resolve as resolve42, join as
|
|
19503
|
+
import { resolve as resolve42, join as join53 } from "path";
|
|
18957
19504
|
import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
|
|
18958
|
-
import { mkdir as
|
|
19505
|
+
import { mkdir as mkdir15, writeFile as writeFile17 } from "fs/promises";
|
|
18959
19506
|
var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve42(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
|
|
18960
19507
|
if (!existsSync42(resolveConfigPath(configPath2))) {
|
|
18961
19508
|
const defaultService = {};
|
|
@@ -19062,8 +19609,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19062
19609
|
console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
|
|
19063
19610
|
process.exit(1);
|
|
19064
19611
|
}
|
|
19065
|
-
const vueTsconfigPath =
|
|
19066
|
-
await
|
|
19612
|
+
const vueTsconfigPath = join53(cacheDir, "tsconfig.vue-check.json");
|
|
19613
|
+
await writeFile17(vueTsconfigPath, JSON.stringify({
|
|
19067
19614
|
compilerOptions: {
|
|
19068
19615
|
rootDir: ".."
|
|
19069
19616
|
},
|
|
@@ -19082,7 +19629,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19082
19629
|
...base,
|
|
19083
19630
|
"--incremental",
|
|
19084
19631
|
"--tsBuildInfoFile",
|
|
19085
|
-
|
|
19632
|
+
join53(cacheDir, "vue-tsc.tsbuildinfo")
|
|
19086
19633
|
]);
|
|
19087
19634
|
if (cached.exitCode === 0 || cached.output.length > 0)
|
|
19088
19635
|
return cached;
|
|
@@ -19093,8 +19640,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19093
19640
|
console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
|
|
19094
19641
|
process.exit(1);
|
|
19095
19642
|
}
|
|
19096
|
-
const angularTsconfigPath =
|
|
19097
|
-
await
|
|
19643
|
+
const angularTsconfigPath = join53(cacheDir, "tsconfig.angular-check.json");
|
|
19644
|
+
await writeFile17(angularTsconfigPath, JSON.stringify({
|
|
19098
19645
|
angularCompilerOptions: {
|
|
19099
19646
|
strictTemplates: true
|
|
19100
19647
|
},
|
|
@@ -19113,8 +19660,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19113
19660
|
console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
|
|
19114
19661
|
process.exit(1);
|
|
19115
19662
|
}
|
|
19116
|
-
const tscConfigPath =
|
|
19117
|
-
return
|
|
19663
|
+
const tscConfigPath = join53(cacheDir, "tsconfig.typecheck.json");
|
|
19664
|
+
return writeFile17(tscConfigPath, JSON.stringify({
|
|
19118
19665
|
compilerOptions: {
|
|
19119
19666
|
rootDir: ".."
|
|
19120
19667
|
},
|
|
@@ -19128,7 +19675,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19128
19675
|
resolve42(tscConfigPath),
|
|
19129
19676
|
"--incremental",
|
|
19130
19677
|
"--tsBuildInfoFile",
|
|
19131
|
-
|
|
19678
|
+
join53(cacheDir, "tsc.tsbuildinfo"),
|
|
19132
19679
|
"--pretty"
|
|
19133
19680
|
]));
|
|
19134
19681
|
}, buildSvelteCheck = async (cacheDir, svelteDir) => {
|
|
@@ -19137,8 +19684,8 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19137
19684
|
console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
|
|
19138
19685
|
process.exit(1);
|
|
19139
19686
|
}
|
|
19140
|
-
const svelteTsconfigPath =
|
|
19141
|
-
await
|
|
19687
|
+
const svelteTsconfigPath = join53(cacheDir, "tsconfig.svelte-check.json");
|
|
19688
|
+
await writeFile17(svelteTsconfigPath, JSON.stringify({
|
|
19142
19689
|
extends: resolve42("tsconfig.json"),
|
|
19143
19690
|
files: ABSOLUTE_TYPECHECK_FILES,
|
|
19144
19691
|
include: [`../${svelteDir}/**/*`]
|
|
@@ -19167,7 +19714,7 @@ Found ${errorCount} error${suffix}.`;
|
|
|
19167
19714
|
...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
19168
19715
|
];
|
|
19169
19716
|
const cacheDir = ".absolutejs";
|
|
19170
|
-
await
|
|
19717
|
+
await mkdir15(cacheDir, { recursive: true });
|
|
19171
19718
|
const checks = [];
|
|
19172
19719
|
checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
|
|
19173
19720
|
for (const svelteDir of hasSvelte ? svelteDirs : []) {
|