@absolutejs/absolute 0.20.0-beta.102 → 0.20.0-beta.104
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 +8 -1
- package/changelog.json +12 -1
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/{compile-4xbpm6hf.js → compile-g8vmvr0w.js} +1 -1
- package/dist/cli/{index-rvpd4w9t.js → index-27hhdgtg.js} +1 -1
- package/dist/cli/{index-6ff78meg.js → index-67kedtn7.js} +9 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/{mobile-6xe6zysv.js → mobile-njmnd7vc.js} +423 -277
- package/dist/cli/{start-nmyy64tr.js → start-39aqmr5n.js} +2 -2
- package/dist/mobile/index.js +188 -37
- package/dist/mobile/index.js.map +7 -6
- package/dist/mobile/remoteMacAgentEntry.js +148 -148
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/nativeReleaseReadiness.d.ts +5 -0
- package/dist/src/mobile/releaseDoctor.d.ts +2 -0
- package/dist/src/mobile/shellReleaseReadiness.d.ts +2 -0
- package/package.json +2 -1
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
} from "./index-zh6hhrwy.js";
|
|
8
8
|
import {
|
|
9
9
|
start
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-27hhdgtg.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-67kedtn7.js";
|
|
16
16
|
import"./index-bzs19w3r.js";
|
|
17
17
|
import"./index-s2tazax7.js";
|
|
18
18
|
import {
|
|
@@ -16777,14 +16777,14 @@ var require_main = __commonJS(function(exports) {
|
|
|
16777
16777
|
// src/cli/scripts/mobile.ts
|
|
16778
16778
|
import {
|
|
16779
16779
|
access as access13,
|
|
16780
|
-
mkdir as
|
|
16780
|
+
mkdir as mkdir14,
|
|
16781
16781
|
mkdtemp as mkdtemp4,
|
|
16782
|
-
readFile as
|
|
16782
|
+
readFile as readFile18,
|
|
16783
16783
|
rm as rm7,
|
|
16784
|
-
writeFile as
|
|
16784
|
+
writeFile as writeFile16
|
|
16785
16785
|
} from "fs/promises";
|
|
16786
16786
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16787
|
-
import { join as
|
|
16787
|
+
import { join as join15, relative as relative12, resolve as resolve13 } from "path";
|
|
16788
16788
|
import { createInterface } from "readline/promises";
|
|
16789
16789
|
|
|
16790
16790
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -18454,14 +18454,156 @@ var applyAbsoluteNativeObservability = async (config, platforms = config.platfor
|
|
|
18454
18454
|
return { changed: changed.some(Boolean) };
|
|
18455
18455
|
};
|
|
18456
18456
|
|
|
18457
|
+
// src/mobile/nativeReleaseReadiness.ts
|
|
18458
|
+
import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as rename6, writeFile as writeFile6 } from "fs/promises";
|
|
18459
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
18460
|
+
var START3 = "// absolutejs:release-readiness:start";
|
|
18461
|
+
var END3 = "// absolutejs:release-readiness:end";
|
|
18462
|
+
var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
|
|
18463
|
+
var optionalSource4 = async (path) => {
|
|
18464
|
+
try {
|
|
18465
|
+
return await readFile6(path, "utf8");
|
|
18466
|
+
} catch (error) {
|
|
18467
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
18468
|
+
return null;
|
|
18469
|
+
throw error;
|
|
18470
|
+
}
|
|
18471
|
+
};
|
|
18472
|
+
var writeChanged4 = async (path, source) => {
|
|
18473
|
+
const current = await optionalSource4(path);
|
|
18474
|
+
if (current === source)
|
|
18475
|
+
return false;
|
|
18476
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
18477
|
+
if (current === null) {
|
|
18478
|
+
await writeFile6(path, source, { flag: "wx" });
|
|
18479
|
+
return true;
|
|
18480
|
+
}
|
|
18481
|
+
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
18482
|
+
await writeFile6(temporary, source, { flag: "wx" });
|
|
18483
|
+
await rename6(temporary, path);
|
|
18484
|
+
return true;
|
|
18485
|
+
};
|
|
18486
|
+
var activityInRoot = async (root) => {
|
|
18487
|
+
let entries;
|
|
18488
|
+
try {
|
|
18489
|
+
entries = await readdir3(root, { recursive: true });
|
|
18490
|
+
} catch (error) {
|
|
18491
|
+
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
|
|
18492
|
+
return;
|
|
18493
|
+
throw error;
|
|
18494
|
+
}
|
|
18495
|
+
const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
|
|
18496
|
+
return candidate ? join6(root, candidate) : undefined;
|
|
18497
|
+
};
|
|
18498
|
+
var activityPath = async (config) => {
|
|
18499
|
+
const roots = [
|
|
18500
|
+
join6(config.nativeProjectDirectory, "android/app/src/main/java"),
|
|
18501
|
+
join6(config.nativeProjectDirectory, "android/app/src/main/kotlin")
|
|
18502
|
+
];
|
|
18503
|
+
const candidates = await Promise.all(roots.map(activityInRoot));
|
|
18504
|
+
const candidate = candidates.find((value) => value !== undefined);
|
|
18505
|
+
if (candidate)
|
|
18506
|
+
return candidate;
|
|
18507
|
+
throw new TypeError("Android MainActivity.java or MainActivity.kt was not found.");
|
|
18508
|
+
};
|
|
18509
|
+
var registrationRegion = (kotlin, generatedMethod) => {
|
|
18510
|
+
if (generatedMethod)
|
|
18511
|
+
return kotlin ? ` ${START3}
|
|
18512
|
+
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
|
18513
|
+
registerPlugin(AbsoluteReleaseReadinessPlugin::class.java)
|
|
18514
|
+
super.onCreate(savedInstanceState)
|
|
18515
|
+
}
|
|
18516
|
+
${END3}
|
|
18517
|
+
` : ` ${START3}
|
|
18518
|
+
@Override
|
|
18519
|
+
public void onCreate(android.os.Bundle savedInstanceState) {
|
|
18520
|
+
registerPlugin(AbsoluteReleaseReadinessPlugin.class);
|
|
18521
|
+
super.onCreate(savedInstanceState);
|
|
18522
|
+
}
|
|
18523
|
+
${END3}
|
|
18524
|
+
`;
|
|
18525
|
+
return kotlin ? ` ${START3}
|
|
18526
|
+
registerPlugin(AbsoluteReleaseReadinessPlugin::class.java)
|
|
18527
|
+
${END3}
|
|
18528
|
+
` : ` ${START3}
|
|
18529
|
+
registerPlugin(AbsoluteReleaseReadinessPlugin.class);
|
|
18530
|
+
${END3}
|
|
18531
|
+
`;
|
|
18532
|
+
};
|
|
18533
|
+
var injectRegistration = (source) => {
|
|
18534
|
+
const kotlin = /\bfun\s+onCreate\s*\(/u.test(source) || source.includes("BridgeActivity()");
|
|
18535
|
+
const existingStart = source.indexOf(START3);
|
|
18536
|
+
const existingEnd = source.indexOf(END3);
|
|
18537
|
+
if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
|
|
18538
|
+
throw new TypeError("AbsoluteJS release-readiness markers are malformed.");
|
|
18539
|
+
if (existingStart >= 0) {
|
|
18540
|
+
const from = source.lastIndexOf(`
|
|
18541
|
+
`, existingStart) + 1;
|
|
18542
|
+
const newline = source.indexOf(`
|
|
18543
|
+
`, existingEnd + END3.length);
|
|
18544
|
+
const through = newline < 0 ? source.length : newline + 1;
|
|
18545
|
+
const generatedMethod = source.slice(from, through).includes("onCreate(");
|
|
18546
|
+
return `${source.slice(0, from)}${registrationRegion(kotlin, generatedMethod)}${source.slice(through)}`;
|
|
18547
|
+
}
|
|
18548
|
+
const onCreate = kotlin ? source.search(/\boverride\s+fun\s+onCreate\s*\([^)]*\)\s*\{/u) : source.search(/\b(?:public|protected)\s+void\s+onCreate\s*\([^)]*\)\s*\{/u);
|
|
18549
|
+
if (onCreate >= 0) {
|
|
18550
|
+
const brace = source.indexOf("{", onCreate);
|
|
18551
|
+
const insert = source.indexOf(`
|
|
18552
|
+
`, brace) + 1;
|
|
18553
|
+
if (insert <= 0)
|
|
18554
|
+
throw new TypeError("Could not inject Android release readiness into MainActivity.");
|
|
18555
|
+
return `${source.slice(0, insert)}${registrationRegion(kotlin, false)}${source.slice(insert)}`;
|
|
18556
|
+
}
|
|
18557
|
+
const close = source.lastIndexOf("}");
|
|
18558
|
+
if (close < 0)
|
|
18559
|
+
throw new TypeError("Could not find the Android MainActivity class body.");
|
|
18560
|
+
const separator = source[close - 1] === `
|
|
18561
|
+
` ? "" : `
|
|
18562
|
+
`;
|
|
18563
|
+
return `${source.slice(0, close)}${separator}${registrationRegion(kotlin, true)}${source.slice(close)}`;
|
|
18564
|
+
};
|
|
18565
|
+
var pluginSource = (packageName) => `package ${packageName};
|
|
18566
|
+
|
|
18567
|
+
import android.util.Log;
|
|
18568
|
+
import com.getcapacitor.JSObject;
|
|
18569
|
+
import com.getcapacitor.Plugin;
|
|
18570
|
+
import com.getcapacitor.PluginCall;
|
|
18571
|
+
import com.getcapacitor.PluginMethod;
|
|
18572
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
18573
|
+
|
|
18574
|
+
@CapacitorPlugin(name = "AbsoluteReleaseReadiness")
|
|
18575
|
+
public final class AbsoluteReleaseReadinessPlugin extends Plugin {
|
|
18576
|
+
@PluginMethod
|
|
18577
|
+
public void ready(PluginCall call) {
|
|
18578
|
+
Log.i("AbsoluteJS", "Capacitor embedded web content ready");
|
|
18579
|
+
call.resolve(new JSObject().put("ready", true));
|
|
18580
|
+
}
|
|
18581
|
+
}
|
|
18582
|
+
`;
|
|
18583
|
+
var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
|
|
18584
|
+
if (config.engine !== "capacitor" || !platforms.includes("android"))
|
|
18585
|
+
return { changed: false };
|
|
18586
|
+
const mainActivityPath = await activityPath(config);
|
|
18587
|
+
const activity = await readFile6(mainActivityPath, "utf8");
|
|
18588
|
+
const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
|
|
18589
|
+
if (!packageName)
|
|
18590
|
+
throw new TypeError("Android MainActivity package declaration was not found.");
|
|
18591
|
+
const pluginPath = join6(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN3);
|
|
18592
|
+
const changed = await Promise.all([
|
|
18593
|
+
writeChanged4(mainActivityPath, injectRegistration(activity)),
|
|
18594
|
+
writeChanged4(pluginPath, pluginSource(packageName))
|
|
18595
|
+
]);
|
|
18596
|
+
return { changed: changed.some(Boolean) };
|
|
18597
|
+
};
|
|
18598
|
+
|
|
18457
18599
|
// src/mobile/associationFiles.ts
|
|
18458
18600
|
import {
|
|
18459
18601
|
access,
|
|
18460
|
-
mkdir as
|
|
18461
|
-
readFile as
|
|
18462
|
-
rename as
|
|
18602
|
+
mkdir as mkdir4,
|
|
18603
|
+
readFile as readFile7,
|
|
18604
|
+
rename as rename7,
|
|
18463
18605
|
rm as rm3,
|
|
18464
|
-
writeFile as
|
|
18606
|
+
writeFile as writeFile7
|
|
18465
18607
|
} from "fs/promises";
|
|
18466
18608
|
import { resolve } from "path";
|
|
18467
18609
|
import { Elysia } from "elysia";
|
|
@@ -18525,7 +18667,7 @@ var createAbsoluteMobileAssociationDocuments = (config, options = {}) => {
|
|
|
18525
18667
|
var writeAtomic = async (path, source) => {
|
|
18526
18668
|
let current;
|
|
18527
18669
|
try {
|
|
18528
|
-
current = await
|
|
18670
|
+
current = await readFile7(path, "utf8");
|
|
18529
18671
|
} catch (error) {
|
|
18530
18672
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
18531
18673
|
throw error;
|
|
@@ -18534,8 +18676,8 @@ var writeAtomic = async (path, source) => {
|
|
|
18534
18676
|
if (current === source)
|
|
18535
18677
|
return false;
|
|
18536
18678
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
18537
|
-
await
|
|
18538
|
-
await
|
|
18679
|
+
await writeFile7(temporary, source, { flag: "wx" });
|
|
18680
|
+
await rename7(temporary, path);
|
|
18539
18681
|
return true;
|
|
18540
18682
|
};
|
|
18541
18683
|
var exists = async (path) => {
|
|
@@ -18550,7 +18692,7 @@ var assertOwnedOutput = async (root) => {
|
|
|
18550
18692
|
const path = resolve(root, OWNERSHIP_FILE);
|
|
18551
18693
|
let ownership;
|
|
18552
18694
|
try {
|
|
18553
|
-
ownership = JSON.parse(await
|
|
18695
|
+
ownership = JSON.parse(await readFile7(path, "utf8"));
|
|
18554
18696
|
} catch {
|
|
18555
18697
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
18556
18698
|
}
|
|
@@ -18564,12 +18706,12 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
18564
18706
|
await assertOwnedOutput(root);
|
|
18565
18707
|
const backup = `${root}.${crypto.randomUUID()}.previous`;
|
|
18566
18708
|
if (hasCurrent)
|
|
18567
|
-
await
|
|
18709
|
+
await rename7(root, backup);
|
|
18568
18710
|
try {
|
|
18569
|
-
await
|
|
18711
|
+
await rename7(temporary, root);
|
|
18570
18712
|
} catch (error) {
|
|
18571
18713
|
if (hasCurrent)
|
|
18572
|
-
await
|
|
18714
|
+
await rename7(backup, root);
|
|
18573
18715
|
throw error;
|
|
18574
18716
|
}
|
|
18575
18717
|
if (hasCurrent)
|
|
@@ -18577,7 +18719,7 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
18577
18719
|
};
|
|
18578
18720
|
var materializeHost = async (root, host, files) => {
|
|
18579
18721
|
const directory = resolve(root, host, ".well-known");
|
|
18580
|
-
await
|
|
18722
|
+
await mkdir4(directory, { recursive: true });
|
|
18581
18723
|
return Promise.all(files.map(async ([name, document2]) => {
|
|
18582
18724
|
const path = resolve(directory, name);
|
|
18583
18725
|
await writeAtomic(path, `${JSON.stringify(document2, null, 2)}
|
|
@@ -18615,7 +18757,7 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
|
|
|
18615
18757
|
if (documents.apple) {
|
|
18616
18758
|
files.push(["apple-app-site-association", documents.apple]);
|
|
18617
18759
|
}
|
|
18618
|
-
await
|
|
18760
|
+
await mkdir4(temporary, { recursive: true });
|
|
18619
18761
|
try {
|
|
18620
18762
|
const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
|
|
18621
18763
|
await writeAtomic(resolve(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
|
|
@@ -18660,8 +18802,8 @@ var verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.f
|
|
|
18660
18802
|
};
|
|
18661
18803
|
|
|
18662
18804
|
// src/mobile/androidWebView.ts
|
|
18663
|
-
import { mkdir as
|
|
18664
|
-
import { dirname as
|
|
18805
|
+
import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
|
|
18806
|
+
import { dirname as dirname4, resolve as resolve2 } from "path";
|
|
18665
18807
|
var CDP_COMMAND_TIMEOUT_MS = 1e4;
|
|
18666
18808
|
var WEBVIEW_ATTACH_TIMEOUT_MS = 30000;
|
|
18667
18809
|
var WEBVIEW_POLL_MS = 200;
|
|
@@ -18955,8 +19097,8 @@ var createSession = (options, capture, socket, hostPort, target, connection) =>
|
|
|
18955
19097
|
throw new Error("Android WebView screenshot returned no image data.");
|
|
18956
19098
|
}
|
|
18957
19099
|
const absolutePath = resolve2(path);
|
|
18958
|
-
await
|
|
18959
|
-
await
|
|
19100
|
+
await mkdir5(dirname4(absolutePath), { recursive: true });
|
|
19101
|
+
await writeFile8(absolutePath, Buffer.from(data, "base64"));
|
|
18960
19102
|
return absolutePath;
|
|
18961
19103
|
},
|
|
18962
19104
|
tap: async (coordinateX, coordinateY) => {
|
|
@@ -19103,13 +19245,13 @@ var waitForAbsoluteAndroidHmrApply = async (session, options) => {
|
|
|
19103
19245
|
};
|
|
19104
19246
|
|
|
19105
19247
|
// src/mobile/releaseDoctor.ts
|
|
19106
|
-
import { access as access4, readFile as
|
|
19107
|
-
import { dirname as
|
|
19248
|
+
import { access as access4, readFile as readFile9, readdir as readdir4 } from "fs/promises";
|
|
19249
|
+
import { dirname as dirname6, extname, join as join8, relative as relative3 } from "path";
|
|
19108
19250
|
|
|
19109
19251
|
// src/mobile/mobileBundleInspection.ts
|
|
19110
19252
|
import { createHash } from "crypto";
|
|
19111
|
-
import { access as access2, readFile as
|
|
19112
|
-
import { join as
|
|
19253
|
+
import { access as access2, readFile as readFile8, stat } from "fs/promises";
|
|
19254
|
+
import { join as join7, relative, resolve as resolve3 } from "path";
|
|
19113
19255
|
var MOBILE_FRAMEWORKS = new Set([
|
|
19114
19256
|
"angular",
|
|
19115
19257
|
"ember",
|
|
@@ -19134,7 +19276,7 @@ var pathExists = async (path) => {
|
|
|
19134
19276
|
}
|
|
19135
19277
|
};
|
|
19136
19278
|
var readObject = async (path) => {
|
|
19137
|
-
const value = JSON.parse(await
|
|
19279
|
+
const value = JSON.parse(await readFile8(path, "utf8"));
|
|
19138
19280
|
if (!isObject(value))
|
|
19139
19281
|
throw new TypeError("JSON root must be an object.");
|
|
19140
19282
|
return value;
|
|
@@ -19162,14 +19304,14 @@ var requireBundleFile = async (root, value, field, expectedHash) => {
|
|
|
19162
19304
|
if (expectedHash !== undefined) {
|
|
19163
19305
|
if (!SHA256_PATTERN.test(expectedHash))
|
|
19164
19306
|
throw new TypeError(`${field} has an invalid SHA-256 digest.`);
|
|
19165
|
-
const actual = createHash("sha256").update(await
|
|
19307
|
+
const actual = createHash("sha256").update(await readFile8(path)).digest("hex");
|
|
19166
19308
|
if (actual !== expectedHash)
|
|
19167
19309
|
throw new TypeError(`${field} failed its SHA-256 integrity check.`);
|
|
19168
19310
|
}
|
|
19169
19311
|
return portable;
|
|
19170
19312
|
};
|
|
19171
19313
|
var inspectAbsoluteMobileBundle = async (config, projectRoot) => {
|
|
19172
|
-
const manifestPath =
|
|
19314
|
+
const manifestPath = join7(config.bundleDirectory, "absolute-mobile-manifest.json");
|
|
19173
19315
|
const manifest = portablePath(projectRoot, manifestPath);
|
|
19174
19316
|
if (!await pathExists(manifestPath))
|
|
19175
19317
|
return { manifest, status: "missing" };
|
|
@@ -19266,8 +19408,8 @@ import {
|
|
|
19266
19408
|
createPublicKey,
|
|
19267
19409
|
X509Certificate
|
|
19268
19410
|
} from "crypto";
|
|
19269
|
-
import { access as access3, mkdir as
|
|
19270
|
-
import { dirname as
|
|
19411
|
+
import { access as access3, mkdir as mkdir6 } from "fs/promises";
|
|
19412
|
+
import { dirname as dirname5, isAbsolute, relative as relative2, resolve as resolve4, sep } from "path";
|
|
19271
19413
|
import { pathToFileURL } from "url";
|
|
19272
19414
|
import { Elysia as Elysia2 } from "elysia";
|
|
19273
19415
|
var ABSOLUTE_MOBILE_UPDATE_SERVER_FORMAT = 1;
|
|
@@ -19484,7 +19626,7 @@ var writeAbsoluteMobileUpdateRegistry = async (options) => {
|
|
|
19484
19626
|
return;
|
|
19485
19627
|
});
|
|
19486
19628
|
}
|
|
19487
|
-
await
|
|
19629
|
+
await mkdir6(dirname5(path), { recursive: true });
|
|
19488
19630
|
await Bun.write(path, renderAbsoluteMobileUpdateRegistry({
|
|
19489
19631
|
...options.health ? { health: options.health } : {},
|
|
19490
19632
|
...options.rollout ? { rollout: options.rollout } : {},
|
|
@@ -19527,14 +19669,14 @@ var inspectReleaseAsset = async (path, isDirectory, isFile) => {
|
|
|
19527
19669
|
return findHmrAsset(path);
|
|
19528
19670
|
if (!isFile || !RELEASE_ASSET_EXTENSIONS.has(extname(path)))
|
|
19529
19671
|
return;
|
|
19530
|
-
const source = await
|
|
19672
|
+
const source = await readFile9(path, "utf8");
|
|
19531
19673
|
return HMR_ASSET_PATTERN.test(source) ? path : undefined;
|
|
19532
19674
|
};
|
|
19533
19675
|
var findHmrAsset = async (root) => {
|
|
19534
19676
|
if (!await pathExists2(root))
|
|
19535
19677
|
return;
|
|
19536
|
-
const entries = await
|
|
19537
|
-
const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(
|
|
19678
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
19679
|
+
const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join8(root, entry.name), entry.isDirectory(), entry.isFile())));
|
|
19538
19680
|
return matches.find((match) => match !== undefined);
|
|
19539
19681
|
};
|
|
19540
19682
|
var pass = (id, detail, path) => ({ detail, id, path, status: "pass" });
|
|
@@ -19564,7 +19706,7 @@ var mobileUpdateServerCheck = async (config, projectRoot) => {
|
|
|
19564
19706
|
};
|
|
19565
19707
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19566
19708
|
var readJsonObject = async (path) => {
|
|
19567
|
-
const value = JSON.parse(await
|
|
19709
|
+
const value = JSON.parse(await readFile9(path, "utf8"));
|
|
19568
19710
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
19569
19711
|
throw new TypeError("JSON root must be an object.");
|
|
19570
19712
|
return Object.fromEntries(Object.entries(value));
|
|
@@ -19583,7 +19725,7 @@ var packageDeclarations = (manifest) => {
|
|
|
19583
19725
|
};
|
|
19584
19726
|
var versionCore = (version) => version.split("-")[0]?.split(".").slice(0, 2).join(".");
|
|
19585
19727
|
var capacitorVersionCheck = async (config, projectRoot) => {
|
|
19586
|
-
const manifestPath =
|
|
19728
|
+
const manifestPath = join8(projectRoot, "package.json");
|
|
19587
19729
|
try {
|
|
19588
19730
|
const manifest = await readJsonObject(manifestPath);
|
|
19589
19731
|
const declarations = packageDeclarations(manifest);
|
|
@@ -19596,7 +19738,7 @@ var capacitorVersionCheck = async (config, projectRoot) => {
|
|
|
19596
19738
|
const declared = declarations.get(name);
|
|
19597
19739
|
if (!declared || !EXACT_VERSION_PATTERN.test(declared))
|
|
19598
19740
|
throw new TypeError(`${name} must be a direct exact dependency.`);
|
|
19599
|
-
const installed = await readJsonObject(
|
|
19741
|
+
const installed = await readJsonObject(await findAbsoluteInstalledPackageManifest(projectRoot, name));
|
|
19600
19742
|
if (installed.version !== declared)
|
|
19601
19743
|
throw new TypeError(`${name} does not match its installed version.`);
|
|
19602
19744
|
return declared;
|
|
@@ -19619,7 +19761,7 @@ var satisfiesGeneratedVersion = (declared, installed) => {
|
|
|
19619
19761
|
return actual[0] === expected[0] && actual[1] === expected[1] && (actual[2] ?? NOT_FOUND3) >= (expected[2] ?? 0);
|
|
19620
19762
|
};
|
|
19621
19763
|
var expoVersionCheck = async (config) => {
|
|
19622
|
-
const manifestPath =
|
|
19764
|
+
const manifestPath = join8(config.nativeProjectDirectory, "package.json");
|
|
19623
19765
|
try {
|
|
19624
19766
|
const manifest = await readJsonObject(manifestPath);
|
|
19625
19767
|
const declarations = packageDeclarations(manifest);
|
|
@@ -19629,13 +19771,13 @@ var expoVersionCheck = async (config) => {
|
|
|
19629
19771
|
throw new TypeError(`Generated Expo project is missing ${missingRequired}.`);
|
|
19630
19772
|
const installedVersions = await Promise.all([...declarations].map(async ([name, declared]) => ({
|
|
19631
19773
|
declared,
|
|
19632
|
-
installed: await readJsonObject(
|
|
19774
|
+
installed: await readJsonObject(join8(config.nativeProjectDirectory, "node_modules", name, "package.json")),
|
|
19633
19775
|
name
|
|
19634
19776
|
})));
|
|
19635
19777
|
const mismatch = installedVersions.find(({ declared, installed }) => typeof installed.version !== "string" || !satisfiesGeneratedVersion(declared, installed.version));
|
|
19636
19778
|
if (mismatch)
|
|
19637
19779
|
throw new TypeError(`Generated Expo dependency ${mismatch.name}@${mismatch.declared} does not match its installed version.`);
|
|
19638
|
-
if (!await pathExists2(
|
|
19780
|
+
if (!await pathExists2(join8(config.nativeProjectDirectory, "bun.lock")))
|
|
19639
19781
|
throw new TypeError("Generated Expo dependency lockfile is missing.");
|
|
19640
19782
|
return pass("mobile.expo-versions", `Generated Expo SDK dependencies are pinned, installed, and locked (${declarations.get("expo")}).`, manifestPath);
|
|
19641
19783
|
} catch (error) {
|
|
@@ -19643,9 +19785,9 @@ var expoVersionCheck = async (config) => {
|
|
|
19643
19785
|
}
|
|
19644
19786
|
};
|
|
19645
19787
|
var dependencyLockAt = async (directory) => (await Promise.all(LOCK_FILES.map(async (name) => ({
|
|
19646
|
-
exists: await pathExists2(
|
|
19788
|
+
exists: await pathExists2(join8(directory, name)),
|
|
19647
19789
|
name,
|
|
19648
|
-
path:
|
|
19790
|
+
path: join8(directory, name)
|
|
19649
19791
|
})))).find(({ exists: exists2 }) => exists2);
|
|
19650
19792
|
var declaredWorkspacePatterns = (declared) => {
|
|
19651
19793
|
if (Array.isArray(declared))
|
|
@@ -19655,7 +19797,7 @@ var declaredWorkspacePatterns = (declared) => {
|
|
|
19655
19797
|
return [];
|
|
19656
19798
|
};
|
|
19657
19799
|
var ownsWorkspace = async (directory, projectRoot) => {
|
|
19658
|
-
const manifest = await readJsonObject(
|
|
19800
|
+
const manifest = await readJsonObject(join8(directory, "package.json")).catch(() => {
|
|
19659
19801
|
return;
|
|
19660
19802
|
});
|
|
19661
19803
|
const workspacePath = relative3(directory, projectRoot).replace(/\\/gu, "/");
|
|
@@ -19666,7 +19808,7 @@ var ancestorDirectories = (projectRoot) => {
|
|
|
19666
19808
|
let directory = projectRoot;
|
|
19667
19809
|
while (true) {
|
|
19668
19810
|
directories.push(directory);
|
|
19669
|
-
const parent =
|
|
19811
|
+
const parent = dirname6(directory);
|
|
19670
19812
|
if (parent === directory)
|
|
19671
19813
|
return directories;
|
|
19672
19814
|
directory = parent;
|
|
@@ -19681,6 +19823,18 @@ var findAbsoluteDependencyLock = async (projectRoot) => {
|
|
|
19681
19823
|
}));
|
|
19682
19824
|
return candidates.find((candidate) => candidate !== undefined);
|
|
19683
19825
|
};
|
|
19826
|
+
var findAbsoluteInstalledPackageManifest = async (projectRoot, name) => {
|
|
19827
|
+
const candidates = await Promise.all(ancestorDirectories(projectRoot).map(async (directory, index) => {
|
|
19828
|
+
if (index > 0 && !await ownsWorkspace(directory, projectRoot))
|
|
19829
|
+
return;
|
|
19830
|
+
const candidate = join8(directory, "node_modules", name, "package.json");
|
|
19831
|
+
return await pathExists2(candidate) ? candidate : undefined;
|
|
19832
|
+
}));
|
|
19833
|
+
const installed = candidates.find((candidate) => candidate !== undefined);
|
|
19834
|
+
if (installed)
|
|
19835
|
+
return installed;
|
|
19836
|
+
throw new TypeError(`${name} is not installed for this application.`);
|
|
19837
|
+
};
|
|
19684
19838
|
var dependencyLockCheck = async (projectRoot) => {
|
|
19685
19839
|
const present = await findAbsoluteDependencyLock(projectRoot);
|
|
19686
19840
|
return present ? pass("mobile.dependency-lock", `Dependency graph is locked by ${present.name}.`, present.path) : fail("mobile.dependency-lock", "No supported dependency lockfile is present.", projectRoot, "Install dependencies with the project package manager and commit its lockfile before release.");
|
|
@@ -19725,7 +19879,7 @@ var capacitorConfigReleaseCheck = async (nativeConfigPath) => {
|
|
|
19725
19879
|
if (!await pathExists2(nativeConfigPath)) {
|
|
19726
19880
|
return fail("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
|
|
19727
19881
|
}
|
|
19728
|
-
const unsafe = isUnsafeCapacitorConfig(await
|
|
19882
|
+
const unsafe = isUnsafeCapacitorConfig(await readFile9(nativeConfigPath, "utf8"));
|
|
19729
19883
|
return unsafe ? fail("android.capacitor-config", "Android Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync android`; do not ship development transport overrides.") : pass("android.capacitor-config", "Android Capacitor config contains no development transport overrides.", nativeConfigPath);
|
|
19730
19884
|
};
|
|
19731
19885
|
var capacitorIdentityCheck = async (config, platform, nativeConfigPath) => {
|
|
@@ -19742,12 +19896,12 @@ var manifestReleaseCheck = async (manifestPath) => {
|
|
|
19742
19896
|
if (!await pathExists2(manifestPath)) {
|
|
19743
19897
|
return fail("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
|
|
19744
19898
|
}
|
|
19745
|
-
const source = await
|
|
19899
|
+
const source = await readFile9(manifestPath, "utf8");
|
|
19746
19900
|
const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
|
|
19747
19901
|
const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
|
|
19748
|
-
const networkConfigPath = networkConfigName ?
|
|
19902
|
+
const networkConfigPath = networkConfigName ? join8(dirname6(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
|
|
19749
19903
|
const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
|
|
19750
|
-
const developmentTrustContents = networkConfigPath ? await
|
|
19904
|
+
const developmentTrustContents = networkConfigPath ? await readFile9(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
|
|
19751
19905
|
const developmentTrust = developmentTrustReference || developmentTrustContents;
|
|
19752
19906
|
return cleartext || developmentTrust ? fail("android.cleartext", developmentTrust ? "Android still references the AbsoluteJS development certificate authority." : "Android explicitly permits cleartext traffic.", manifestPath, "Run `absolute mobile sync android`; do not ship development transport or trust overrides.") : pass("android.cleartext", "Android does not explicitly permit cleartext traffic.", manifestPath);
|
|
19753
19907
|
};
|
|
@@ -19758,13 +19912,13 @@ var hmrAssetsReleaseCheck = async (publicRoot) => {
|
|
|
19758
19912
|
var embeddedBundleReleaseCheck = async (config, projectRoot, platform, publicRoot) => {
|
|
19759
19913
|
const inspection = await inspectAbsoluteMobileBundle({ ...config, bundleDirectory: publicRoot }, projectRoot);
|
|
19760
19914
|
if (inspection.status === "valid")
|
|
19761
|
-
return pass(`${platform}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`,
|
|
19762
|
-
return fail(`${platform}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`,
|
|
19915
|
+
return pass(`${platform}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join8(publicRoot, "absolute-mobile-manifest.json"));
|
|
19916
|
+
return fail(`${platform}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join8(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
|
|
19763
19917
|
};
|
|
19764
19918
|
var contentSecurityPolicyCheck = async (config, platform, publicRoot) => {
|
|
19765
|
-
const path =
|
|
19919
|
+
const path = join8(publicRoot, "index.html");
|
|
19766
19920
|
try {
|
|
19767
|
-
const source = await
|
|
19921
|
+
const source = await readFile9(path, "utf8");
|
|
19768
19922
|
const requirements = [
|
|
19769
19923
|
"Content-Security-Policy",
|
|
19770
19924
|
"default-src 'self'",
|
|
@@ -19793,12 +19947,12 @@ var validateExpoCodeSigningProjection = async (config, expo) => {
|
|
|
19793
19947
|
return;
|
|
19794
19948
|
if (!generatedUpdates || generatedUpdates.codeSigningCertificate !== "./certs/absolute-mobile-update.pem" || !isRecord3(generatedUpdates.codeSigningMetadata) || generatedUpdates.codeSigningMetadata.keyid !== configuredSigning.keyId || generatedUpdates.codeSigningMetadata.alg !== configuredSigning.algorithm)
|
|
19795
19949
|
throw new TypeError("Generated Expo code-signing metadata does not match mobile config.");
|
|
19796
|
-
const generatedCertificate = await
|
|
19950
|
+
const generatedCertificate = await readFile9(join8(config.nativeProjectDirectory, "certs", "absolute-mobile-update.pem"), "utf8");
|
|
19797
19951
|
if (generatedCertificate !== configuredSigning.certificatePem)
|
|
19798
19952
|
throw new TypeError("Generated Expo code-signing certificate does not match mobile config.");
|
|
19799
19953
|
};
|
|
19800
19954
|
var expoApplicationConfigCheck = async (config, projectRoot) => {
|
|
19801
|
-
const path =
|
|
19955
|
+
const path = join8(config.nativeProjectDirectory, "app.json");
|
|
19802
19956
|
try {
|
|
19803
19957
|
const root = await readJsonObject(path);
|
|
19804
19958
|
if (!isRecord3(root.expo))
|
|
@@ -19821,12 +19975,12 @@ var expoApplicationConfigCheck = async (config, projectRoot) => {
|
|
|
19821
19975
|
}
|
|
19822
19976
|
};
|
|
19823
19977
|
var expoEmbeddedAssetsCheck = async (config) => {
|
|
19824
|
-
const generated =
|
|
19825
|
-
const assetsRoot =
|
|
19978
|
+
const generated = join8(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
|
|
19979
|
+
const assetsRoot = join8(config.nativeProjectDirectory, "assets", "absolute");
|
|
19826
19980
|
try {
|
|
19827
19981
|
const [source, manifest] = await Promise.all([
|
|
19828
|
-
|
|
19829
|
-
readJsonObject(
|
|
19982
|
+
readFile9(generated, "utf8"),
|
|
19983
|
+
readJsonObject(join8(config.bundleDirectory, "absolute-mobile-manifest.json"))
|
|
19830
19984
|
]);
|
|
19831
19985
|
if (source.includes("embedded AbsoluteJS bundle is unavailable") || typeof manifest.appBuild !== "string" || !source.includes(JSON.stringify(manifest.appBuild)) || !source.includes(JSON.stringify(config.productionOrigin))) {
|
|
19832
19986
|
throw new TypeError("Generated Expo assets do not contain the prepared production release identity.");
|
|
@@ -19841,7 +19995,7 @@ var expoEmbeddedAssetsCheck = async (config) => {
|
|
|
19841
19995
|
}))).sort();
|
|
19842
19996
|
if (sourceFiles.length === 0 || embeddedFiles.length !== 1)
|
|
19843
19997
|
throw new TypeError("Generated Expo asset count does not match the prepared mobile bundle.");
|
|
19844
|
-
const contents = await Promise.all(sourceFiles.map((path) =>
|
|
19998
|
+
const contents = await Promise.all(sourceFiles.map((path) => readFile9(join8(config.bundleDirectory, path))));
|
|
19845
19999
|
let offset = 0;
|
|
19846
20000
|
const entries = sourceFiles.map((path, index) => {
|
|
19847
20001
|
const entry = {
|
|
@@ -19852,7 +20006,7 @@ var expoEmbeddedAssetsCheck = async (config) => {
|
|
|
19852
20006
|
offset += entry.length;
|
|
19853
20007
|
return entry;
|
|
19854
20008
|
});
|
|
19855
|
-
const archive = await
|
|
20009
|
+
const archive = await readFile9(join8(assetsRoot, embeddedFiles[0] ?? ""));
|
|
19856
20010
|
if (!archive.equals(Buffer.concat(contents)) || entries.some((entry) => !source.includes(JSON.stringify(entry))))
|
|
19857
20011
|
throw new TypeError("Generated Expo asset bytes differ from the prepared mobile bundle.");
|
|
19858
20012
|
return pass("expo.bundle-projection", `Expo embeds the complete signed mobile bundle as one opaque archive containing ${sourceFiles.length} file(s).`, generated);
|
|
@@ -19864,20 +20018,20 @@ var sourceFiles = async (root, extensions) => {
|
|
|
19864
20018
|
if (!await pathExists2(root))
|
|
19865
20019
|
return [];
|
|
19866
20020
|
const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
|
|
19867
|
-
return files.filter((file) => extensions.has(extname(file))).map((file) =>
|
|
20021
|
+
return files.filter((file) => extensions.has(extname(file))).map((file) => join8(root, file));
|
|
19868
20022
|
};
|
|
19869
20023
|
var containsPattern = async (paths, pattern) => {
|
|
19870
|
-
const sources = await Promise.all(paths.map((path) =>
|
|
20024
|
+
const sources = await Promise.all(paths.map((path) => readFile9(path, "utf8")));
|
|
19871
20025
|
const index = sources.findIndex((source) => pattern.test(source));
|
|
19872
20026
|
return index === NOT_FOUND3 ? undefined : paths[index];
|
|
19873
20027
|
};
|
|
19874
20028
|
var androidNativeSecurityCheck = async (androidRoot) => {
|
|
19875
|
-
const manifestPath =
|
|
20029
|
+
const manifestPath = join8(androidRoot, "app/src/main/AndroidManifest.xml");
|
|
19876
20030
|
try {
|
|
19877
|
-
const manifest = await
|
|
20031
|
+
const manifest = await readFile9(manifestPath, "utf8");
|
|
19878
20032
|
if (/android:debuggable=["']true["']/u.test(manifest))
|
|
19879
20033
|
throw new TypeError("Android release manifest explicitly enables application debugging.");
|
|
19880
|
-
const sources = await sourceFiles(
|
|
20034
|
+
const sources = await sourceFiles(join8(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
|
|
19881
20035
|
const debugSource = await containsPattern(sources, /setWebContentsDebuggingEnabled\s*\(\s*true\s*\)/u);
|
|
19882
20036
|
if (debugSource)
|
|
19883
20037
|
return fail("android.native-debugging", "Android application source unconditionally enables WebView debugging.", debugSource, "Remove the unconditional WebView debugging call; use the platform debug-build behavior during development.");
|
|
@@ -19887,7 +20041,7 @@ var androidNativeSecurityCheck = async (androidRoot) => {
|
|
|
19887
20041
|
}
|
|
19888
20042
|
};
|
|
19889
20043
|
var androidExportedComponentsCheck = async (manifestPath) => {
|
|
19890
|
-
const source = await
|
|
20044
|
+
const source = await readFile9(manifestPath, "utf8").catch(() => "");
|
|
19891
20045
|
const exported = [
|
|
19892
20046
|
...source.matchAll(/<(?:activity|activity-alias|provider|receiver|service)\b[^>]*>/giu)
|
|
19893
20047
|
].map(([tag]) => tag).filter((tag) => /android:exported=["']true["']/iu.test(tag)).map((tag) => tag.match(/android:name=["']([^"']+)["']/iu)?.[1]).filter((name) => Boolean(name) && name !== ".MainActivity");
|
|
@@ -19895,7 +20049,7 @@ var androidExportedComponentsCheck = async (manifestPath) => {
|
|
|
19895
20049
|
};
|
|
19896
20050
|
var androidDeepLinkProjectionCheck = async (config, manifestPath) => {
|
|
19897
20051
|
try {
|
|
19898
|
-
const source = await
|
|
20052
|
+
const source = await readFile9(manifestPath, "utf8");
|
|
19899
20053
|
const hasWebHost = (host) => [...source.matchAll(/<data\b[^>]*>/giu)].some(([tag]) => tag.includes('android:scheme="https"') && tag.includes(`android:host="${host}"`));
|
|
19900
20054
|
if (!source.includes('android:autoVerify="true"') || !source.includes("android.intent.category.BROWSABLE") || config.deepLinkHosts.some((host) => !hasWebHost(host)) || config.deepLinkScheme && !source.includes(`android:scheme="${config.deepLinkScheme}"`))
|
|
19901
20055
|
throw new TypeError("Android App Link or custom-scheme projection does not match mobile config.");
|
|
@@ -19909,15 +20063,15 @@ var nativeUpdateWatchdogCheck = async (config, platform) => {
|
|
|
19909
20063
|
return;
|
|
19910
20064
|
const timeout = String(config.updates.bootTimeoutMs);
|
|
19911
20065
|
if (platform === "ios") {
|
|
19912
|
-
const path =
|
|
19913
|
-
const configPath =
|
|
20066
|
+
const path = join8(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
20067
|
+
const configPath = join8(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
19914
20068
|
const [source, nativeConfig] = await Promise.all([
|
|
19915
|
-
|
|
19916
|
-
|
|
20069
|
+
readFile9(path, "utf8").catch(() => ""),
|
|
20070
|
+
readFile9(configPath, "utf8").catch(() => "")
|
|
19917
20071
|
]);
|
|
19918
20072
|
return source.includes("AbsoluteMobileUpdateWatchdogPlugin") && source.includes("recoverInterruptedBoot()") && source.includes(`.milliseconds(${timeout})`) && source.includes("quarantinedReleases") && nativeConfig.includes("AbsoluteMobileUpdateWatchdogPlugin") ? pass("ios.update-watchdog", `The native update boot watchdog is projected with a ${timeout}ms deadline.`, path) : fail("ios.update-watchdog", "The iOS update boot watchdog does not match mobile config.", path, "Run `absolute mobile sync ios` before building the release.");
|
|
19919
20073
|
}
|
|
19920
|
-
const sourceRoot =
|
|
20074
|
+
const sourceRoot = join8(config.nativeProjectDirectory, "android/app/src/main");
|
|
19921
20075
|
const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
|
|
19922
20076
|
const [activity, plugin] = await Promise.all([
|
|
19923
20077
|
containsPattern(sources, /recoverInterruptedBoot\s*\(\s*this\s*\)/u),
|
|
@@ -19930,27 +20084,27 @@ var nativeObservabilityProjectionCheck = async (config, platform) => {
|
|
|
19930
20084
|
return;
|
|
19931
20085
|
const nativeRoot = config.nativeProjectDirectory;
|
|
19932
20086
|
if (config.engine === "expo") {
|
|
19933
|
-
const moduleRoot =
|
|
19934
|
-
const configPath =
|
|
19935
|
-
const sourcePath = platform === "android" ?
|
|
20087
|
+
const moduleRoot = join8(nativeRoot, "modules", "absolute-mobile-observability");
|
|
20088
|
+
const configPath = join8(moduleRoot, "expo-module.config.json");
|
|
20089
|
+
const sourcePath = platform === "android" ? join8(moduleRoot, "android/src/main/java/expo/modules/absolutemobileobservability/AbsoluteMobileObservabilityModule.kt") : join8(moduleRoot, "ios/AbsoluteMobileObservabilityModule.swift");
|
|
19936
20090
|
const [moduleConfig, source] = await Promise.all([
|
|
19937
|
-
|
|
19938
|
-
|
|
20091
|
+
readFile9(configPath, "utf8").catch(() => ""),
|
|
20092
|
+
readFile9(sourcePath, "utf8").catch(() => "")
|
|
19939
20093
|
]);
|
|
19940
20094
|
const valid = moduleConfig.includes("AbsoluteMobileObservabilityModule") && source.includes("AbsoluteMobileObservability") && (platform === "android" ? source.includes("getHistoricalProcessExitReasons") : source.includes("MXMetricManagerSubscriber"));
|
|
19941
20095
|
return valid ? pass(`${platform}.native-observability`, `The Expo ${platform} native process collector is projected and autolinkable.`, sourcePath) : fail(`${platform}.native-observability`, `The Expo ${platform} native process collector is missing or incomplete.`, sourcePath, `Run \`absolute mobile build ${platform}\` before building the release.`);
|
|
19942
20096
|
}
|
|
19943
20097
|
if (platform === "ios") {
|
|
19944
|
-
const delegatePath =
|
|
19945
|
-
const configPath =
|
|
20098
|
+
const delegatePath = join8(nativeRoot, "ios/App/App/AppDelegate.swift");
|
|
20099
|
+
const configPath = join8(nativeRoot, "ios/App/App/capacitor.config.json");
|
|
19946
20100
|
const [delegate, nativeConfig] = await Promise.all([
|
|
19947
|
-
|
|
19948
|
-
|
|
20101
|
+
readFile9(delegatePath, "utf8").catch(() => ""),
|
|
20102
|
+
readFile9(configPath, "utf8").catch(() => "")
|
|
19949
20103
|
]);
|
|
19950
20104
|
const valid = delegate.includes("AbsoluteMobileObservabilityPlugin") && delegate.includes("MXMetricManagerSubscriber") && nativeConfig.includes("AbsoluteMobileObservabilityPlugin");
|
|
19951
20105
|
return valid ? pass("ios.native-observability", "The iOS MetricKit native process collector is projected and registered.", delegatePath) : fail("ios.native-observability", "The iOS MetricKit native process collector is missing or unregistered.", delegatePath, "Run `absolute mobile sync ios` before building the release.");
|
|
19952
20106
|
}
|
|
19953
|
-
const sourceRoot =
|
|
20107
|
+
const sourceRoot = join8(nativeRoot, "android/app/src/main");
|
|
19954
20108
|
const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
|
|
19955
20109
|
const [registration, plugin] = await Promise.all([
|
|
19956
20110
|
containsPattern(sources, /registerPlugin\s*\(\s*AbsoluteMobileObservabilityPlugin(?:::class\.java|\.class)\s*\)/u),
|
|
@@ -19971,14 +20125,14 @@ var iosNativeSecurityCheck = async (iosRoot) => {
|
|
|
19971
20125
|
return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementPaths[0] ?? iosRoot);
|
|
19972
20126
|
};
|
|
19973
20127
|
var iosDeepLinkProjectionCheck = async (config, iosRoot) => {
|
|
19974
|
-
const infoPath =
|
|
19975
|
-
const entitlementsPath =
|
|
19976
|
-
const projectPath2 =
|
|
20128
|
+
const infoPath = join8(iosRoot, "App/App/Info.plist");
|
|
20129
|
+
const entitlementsPath = join8(iosRoot, "App/AbsoluteJS.entitlements");
|
|
20130
|
+
const projectPath2 = join8(iosRoot, "App/App.xcodeproj/project.pbxproj");
|
|
19977
20131
|
try {
|
|
19978
20132
|
const [info, entitlements, project] = await Promise.all([
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
20133
|
+
readFile9(infoPath, "utf8"),
|
|
20134
|
+
readFile9(entitlementsPath, "utf8"),
|
|
20135
|
+
readFile9(projectPath2, "utf8")
|
|
19982
20136
|
]);
|
|
19983
20137
|
if (config.deepLinkScheme && (!info.includes("<key>CFBundleURLTypes</key>") || !info.includes(`<string>${config.deepLinkScheme}</string>`)))
|
|
19984
20138
|
throw new TypeError("iOS custom URL scheme does not match mobile config.");
|
|
@@ -19994,7 +20148,7 @@ var iosDeepLinkProjectionCheck = async (config, iosRoot) => {
|
|
|
19994
20148
|
var syncSchemaReleaseCheck = (projectRoot) => {
|
|
19995
20149
|
if (!projectUsesAbsoluteSync(projectRoot))
|
|
19996
20150
|
return;
|
|
19997
|
-
const manifestPath =
|
|
20151
|
+
const manifestPath = join8(projectRoot, "package.json");
|
|
19998
20152
|
try {
|
|
19999
20153
|
const schema = discoverAbsoluteSyncSchema(projectRoot);
|
|
20000
20154
|
const versions = schema.components.map((component) => `${component.id}@${component.version}`).join(", ");
|
|
@@ -20024,8 +20178,8 @@ var IOS_USAGE_KEYS = {
|
|
|
20024
20178
|
var androidDevicePermissionCheck = async (config, permissions) => {
|
|
20025
20179
|
if (!config.platforms.includes("android") || permissions.length === 0)
|
|
20026
20180
|
return;
|
|
20027
|
-
const path =
|
|
20028
|
-
const source = await
|
|
20181
|
+
const path = join8(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
20182
|
+
const source = await readFile9(path, "utf8");
|
|
20029
20183
|
const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
|
|
20030
20184
|
if (missing.length === 0)
|
|
20031
20185
|
return;
|
|
@@ -20034,9 +20188,9 @@ var androidDevicePermissionCheck = async (config, permissions) => {
|
|
|
20034
20188
|
var iosDevicePermissionCheck = async (config, purposes) => {
|
|
20035
20189
|
if (!config.platforms.includes("ios") || purposes.length === 0)
|
|
20036
20190
|
return;
|
|
20037
|
-
const iosRoot =
|
|
20038
|
-
const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") :
|
|
20039
|
-
const source = await
|
|
20191
|
+
const iosRoot = join8(config.nativeProjectDirectory, "ios");
|
|
20192
|
+
const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join8(iosRoot, "App/App/Info.plist");
|
|
20193
|
+
const source = await readFile9(path, "utf8");
|
|
20040
20194
|
const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
|
|
20041
20195
|
if (missing.length === 0)
|
|
20042
20196
|
return;
|
|
@@ -20046,7 +20200,7 @@ var expoIosPrivacyCapabilityCheck = async (iosRoot, project, requirements) => {
|
|
|
20046
20200
|
if (requirements.iosPrivacyAccessedApis.length === 0)
|
|
20047
20201
|
return;
|
|
20048
20202
|
const privacyPath = await uniqueExpoIosFile(iosRoot, "**/PrivacyInfo.xcprivacy", "privacy manifest");
|
|
20049
|
-
const privacy = await
|
|
20203
|
+
const privacy = await readFile9(privacyPath, "utf8");
|
|
20050
20204
|
const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
|
|
20051
20205
|
if (!missing && project.includes(privacyPath.split(/[\\/]/u).at(-1) ?? ""))
|
|
20052
20206
|
return;
|
|
@@ -20056,15 +20210,15 @@ var expoIosPushCapabilityCheck = async (iosRoot, requirements) => {
|
|
|
20056
20210
|
if (!requirements.iosPushNotifications)
|
|
20057
20211
|
return;
|
|
20058
20212
|
const entitlementsPath = await uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements");
|
|
20059
|
-
const entitlements = await
|
|
20213
|
+
const entitlements = await readFile9(entitlementsPath, "utf8");
|
|
20060
20214
|
if (entitlements.includes("<key>aps-environment</key>"))
|
|
20061
20215
|
return;
|
|
20062
20216
|
return fail("mobile.device-capabilities", "Expo iOS push entitlement does not match detected capabilities.", entitlementsPath, "Run `absolute mobile build ios` to regenerate native push integration.");
|
|
20063
20217
|
};
|
|
20064
20218
|
var expoIosCapabilityProjectionCheck = async (config, requirements) => {
|
|
20065
|
-
const iosRoot =
|
|
20219
|
+
const iosRoot = join8(config.nativeProjectDirectory, "ios");
|
|
20066
20220
|
const projectPath2 = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
|
|
20067
|
-
const project = await
|
|
20221
|
+
const project = await readFile9(projectPath2, "utf8");
|
|
20068
20222
|
const privacy = await expoIosPrivacyCapabilityCheck(iosRoot, project, requirements);
|
|
20069
20223
|
if (privacy)
|
|
20070
20224
|
return privacy;
|
|
@@ -20075,28 +20229,28 @@ var iosCapabilityProjectionCheck = async (config, requirements) => {
|
|
|
20075
20229
|
return;
|
|
20076
20230
|
if (config.engine === "expo")
|
|
20077
20231
|
return expoIosCapabilityProjectionCheck(config, requirements);
|
|
20078
|
-
const appRoot =
|
|
20079
|
-
const infoPath =
|
|
20080
|
-
const info = await
|
|
20232
|
+
const appRoot = join8(config.nativeProjectDirectory, "ios/App/App");
|
|
20233
|
+
const infoPath = join8(appRoot, "Info.plist");
|
|
20234
|
+
const info = await readFile9(infoPath, "utf8").catch(() => "");
|
|
20081
20235
|
if (requirements.iosSystemBars && !/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<true\s*\/>/u.test(info))
|
|
20082
20236
|
return fail("mobile.device-capabilities", "iOS system-bar capability is missing its required view-controller setting.", infoPath, "Run `absolute mobile sync ios` to regenerate native capability settings.");
|
|
20083
20237
|
if (requirements.iosPrivacyAccessedApis.length > 0) {
|
|
20084
|
-
const privacyPath =
|
|
20085
|
-
const projectPath2 =
|
|
20238
|
+
const privacyPath = join8(appRoot, "PrivacyInfo.xcprivacy");
|
|
20239
|
+
const projectPath2 = join8(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
20086
20240
|
const [privacy, project] = await Promise.all([
|
|
20087
|
-
|
|
20088
|
-
|
|
20241
|
+
readFile9(privacyPath, "utf8").catch(() => ""),
|
|
20242
|
+
readFile9(projectPath2, "utf8").catch(() => "")
|
|
20089
20243
|
]);
|
|
20090
20244
|
const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
|
|
20091
20245
|
if (missing || !project.includes("PrivacyInfo.xcprivacy in Resources"))
|
|
20092
20246
|
return fail("mobile.device-capabilities", "iOS privacy manifest or target membership does not match detected native capabilities.", privacyPath, "Run `absolute mobile sync ios` to regenerate and target PrivacyInfo.xcprivacy.");
|
|
20093
20247
|
}
|
|
20094
20248
|
if (requirements.iosPushNotifications) {
|
|
20095
|
-
const entitlementsPath =
|
|
20096
|
-
const delegatePath =
|
|
20249
|
+
const entitlementsPath = join8(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
20250
|
+
const delegatePath = join8(appRoot, "AppDelegate.swift");
|
|
20097
20251
|
const [entitlements, delegate] = await Promise.all([
|
|
20098
|
-
|
|
20099
|
-
|
|
20252
|
+
readFile9(entitlementsPath, "utf8").catch(() => ""),
|
|
20253
|
+
readFile9(delegatePath, "utf8").catch(() => "")
|
|
20100
20254
|
]);
|
|
20101
20255
|
if (!entitlements.includes("<key>aps-environment</key>") || !delegate.includes("capacitorDidRegisterForRemoteNotifications") || !delegate.includes("capacitorDidFailToRegisterForRemoteNotifications"))
|
|
20102
20256
|
return fail("mobile.device-capabilities", "iOS push entitlement or AppDelegate forwarding does not match detected capabilities.", entitlementsPath, "Run `absolute mobile sync ios` to regenerate native push integration.");
|
|
@@ -20104,13 +20258,13 @@ var iosCapabilityProjectionCheck = async (config, requirements) => {
|
|
|
20104
20258
|
return;
|
|
20105
20259
|
};
|
|
20106
20260
|
var deviceCapabilityReleaseCheck = async (config, projectRoot) => {
|
|
20107
|
-
const manifestPath =
|
|
20261
|
+
const manifestPath = join8(projectRoot, "package.json");
|
|
20108
20262
|
try {
|
|
20109
20263
|
const plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot, config.engine);
|
|
20110
20264
|
const assertPackages = async () => {
|
|
20111
20265
|
if (config.engine === "capacitor")
|
|
20112
20266
|
return assertAbsoluteDeviceCapabilityPackages(projectRoot, plan);
|
|
20113
|
-
const generated = await readJsonObject(
|
|
20267
|
+
const generated = await readJsonObject(join8(config.nativeProjectDirectory, "package.json"));
|
|
20114
20268
|
const declarations = packageDeclarations(generated);
|
|
20115
20269
|
const missing = plan.requiredPackages.filter((spec) => {
|
|
20116
20270
|
const separator = spec.lastIndexOf("@");
|
|
@@ -20137,11 +20291,11 @@ var deviceCapabilityReleaseCheck = async (config, projectRoot) => {
|
|
|
20137
20291
|
}
|
|
20138
20292
|
};
|
|
20139
20293
|
var inspectAndroidRelease = async (config, projectRoot) => {
|
|
20140
|
-
const androidRoot =
|
|
20141
|
-
const nativeConfigPath =
|
|
20142
|
-
const manifestPath =
|
|
20143
|
-
const publicRoot =
|
|
20144
|
-
const journalPath =
|
|
20294
|
+
const androidRoot = join8(config.nativeProjectDirectory, "android");
|
|
20295
|
+
const nativeConfigPath = join8(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
|
|
20296
|
+
const manifestPath = join8(androidRoot, "app", "src", "main", "AndroidManifest.xml");
|
|
20297
|
+
const publicRoot = join8(androidRoot, "app", "src", "main", "assets", "public");
|
|
20298
|
+
const journalPath = join8(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
|
|
20145
20299
|
const checks = await Promise.all([
|
|
20146
20300
|
journalReleaseCheck(journalPath, "android"),
|
|
20147
20301
|
capacitorConfigReleaseCheck(nativeConfigPath),
|
|
@@ -20162,9 +20316,9 @@ var inspectAndroidRelease = async (config, projectRoot) => {
|
|
|
20162
20316
|
}));
|
|
20163
20317
|
};
|
|
20164
20318
|
var inspectExpoAndroidRelease = async (config, projectRoot) => {
|
|
20165
|
-
const androidRoot =
|
|
20166
|
-
const manifestPath =
|
|
20167
|
-
const journalPath =
|
|
20319
|
+
const androidRoot = join8(config.nativeProjectDirectory, "android");
|
|
20320
|
+
const manifestPath = join8(androidRoot, "app", "src", "main", "AndroidManifest.xml");
|
|
20321
|
+
const journalPath = join8(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
|
|
20168
20322
|
const checks = await Promise.all([
|
|
20169
20323
|
journalReleaseCheck(journalPath, "android"),
|
|
20170
20324
|
expoApplicationConfigCheck(config, projectRoot),
|
|
@@ -20190,7 +20344,7 @@ var uniqueExpoIosFile = async (iosRoot, pattern, label) => {
|
|
|
20190
20344
|
const [path] = paths;
|
|
20191
20345
|
if (!path)
|
|
20192
20346
|
throw new TypeError(`Generated Expo iOS project is missing its ${label}.`);
|
|
20193
|
-
return
|
|
20347
|
+
return join8(iosRoot, path);
|
|
20194
20348
|
};
|
|
20195
20349
|
var expoIosNativeProjectionCheck = async (config, iosRoot) => {
|
|
20196
20350
|
try {
|
|
@@ -20200,9 +20354,9 @@ var expoIosNativeProjectionCheck = async (config, iosRoot) => {
|
|
|
20200
20354
|
uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project")
|
|
20201
20355
|
]);
|
|
20202
20356
|
const [info, entitlements, project] = await Promise.all([
|
|
20203
|
-
|
|
20204
|
-
|
|
20205
|
-
|
|
20357
|
+
readFile9(infoPath, "utf8"),
|
|
20358
|
+
readFile9(entitlementsPath, "utf8"),
|
|
20359
|
+
readFile9(projectPath2, "utf8")
|
|
20206
20360
|
]);
|
|
20207
20361
|
if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info))
|
|
20208
20362
|
throw new TypeError("Expo iOS App Transport Security permits arbitrary network loads.");
|
|
@@ -20218,8 +20372,8 @@ var expoIosNativeProjectionCheck = async (config, iosRoot) => {
|
|
|
20218
20372
|
}
|
|
20219
20373
|
};
|
|
20220
20374
|
var inspectExpoIosRelease = async (config, projectRoot) => {
|
|
20221
|
-
const iosRoot =
|
|
20222
|
-
const journalPath =
|
|
20375
|
+
const iosRoot = join8(config.nativeProjectDirectory, "ios");
|
|
20376
|
+
const journalPath = join8(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
|
|
20223
20377
|
const checks = [
|
|
20224
20378
|
await journalReleaseCheck(journalPath, "ios")
|
|
20225
20379
|
];
|
|
@@ -20242,11 +20396,11 @@ var inspectExpoIosRelease = async (config, projectRoot) => {
|
|
|
20242
20396
|
}));
|
|
20243
20397
|
};
|
|
20244
20398
|
var inspectIosRelease = async (config, projectRoot) => {
|
|
20245
|
-
const iosAppRoot =
|
|
20246
|
-
const nativeConfigPath =
|
|
20247
|
-
const infoPath =
|
|
20248
|
-
const publicRoot =
|
|
20249
|
-
const journalPath =
|
|
20399
|
+
const iosAppRoot = join8(config.nativeProjectDirectory, "ios", "App", "App");
|
|
20400
|
+
const nativeConfigPath = join8(iosAppRoot, "capacitor.config.json");
|
|
20401
|
+
const infoPath = join8(iosAppRoot, "Info.plist");
|
|
20402
|
+
const publicRoot = join8(iosAppRoot, "public");
|
|
20403
|
+
const journalPath = join8(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
|
|
20250
20404
|
const checks = [
|
|
20251
20405
|
await journalReleaseCheck(journalPath, "ios")
|
|
20252
20406
|
];
|
|
@@ -20258,7 +20412,7 @@ var inspectIosRelease = async (config, projectRoot) => {
|
|
|
20258
20412
|
}
|
|
20259
20413
|
if (!await pathExists2(nativeConfigPath)) {
|
|
20260
20414
|
checks.push(fail("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
|
|
20261
|
-
} else if (isUnsafeCapacitorConfig(await
|
|
20415
|
+
} else if (isUnsafeCapacitorConfig(await readFile9(nativeConfigPath, "utf8"))) {
|
|
20262
20416
|
checks.push(fail("ios.capacitor-config", "iOS Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync ios`; do not ship development transport overrides."));
|
|
20263
20417
|
} else {
|
|
20264
20418
|
checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
|
|
@@ -20267,12 +20421,12 @@ var inspectIosRelease = async (config, projectRoot) => {
|
|
|
20267
20421
|
if (!await pathExists2(infoPath)) {
|
|
20268
20422
|
checks.push(fail("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
|
|
20269
20423
|
} else {
|
|
20270
|
-
const info = await
|
|
20424
|
+
const info = await readFile9(infoPath, "utf8");
|
|
20271
20425
|
checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info) ? fail("ios.transport-security", "iOS App Transport Security permits arbitrary network loads.", infoPath, "Remove NSAllowsArbitraryLoads from the release Info.plist.") : pass("ios.transport-security", "iOS App Transport Security does not permit arbitrary loads.", infoPath));
|
|
20272
20426
|
}
|
|
20273
20427
|
const hmrAsset = await findHmrAsset(publicRoot);
|
|
20274
20428
|
checks.push(hmrAsset ? fail("ios.hmr-assets", "A packaged iOS asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("ios.hmr-assets", "Packaged iOS assets contain no development HMR markers.", publicRoot));
|
|
20275
|
-
checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(
|
|
20429
|
+
checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join8(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join8(config.nativeProjectDirectory, "ios")));
|
|
20276
20430
|
const nativeObservability = await nativeObservabilityProjectionCheck(config, "ios");
|
|
20277
20431
|
if (nativeObservability)
|
|
20278
20432
|
checks.push(nativeObservability);
|
|
@@ -20342,16 +20496,16 @@ import { createHash as createHash2 } from "crypto";
|
|
|
20342
20496
|
import {
|
|
20343
20497
|
access as access5,
|
|
20344
20498
|
copyFile,
|
|
20345
|
-
mkdir as
|
|
20499
|
+
mkdir as mkdir7,
|
|
20346
20500
|
mkdtemp,
|
|
20347
|
-
readFile as
|
|
20501
|
+
readFile as readFile10,
|
|
20348
20502
|
realpath,
|
|
20349
|
-
rename as
|
|
20503
|
+
rename as rename8,
|
|
20350
20504
|
rm as rm4,
|
|
20351
20505
|
stat as stat2,
|
|
20352
|
-
writeFile as
|
|
20506
|
+
writeFile as writeFile9
|
|
20353
20507
|
} from "fs/promises";
|
|
20354
|
-
import { dirname as
|
|
20508
|
+
import { dirname as dirname7, isAbsolute as isAbsolute2, join as join9, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
|
|
20355
20509
|
var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1;
|
|
20356
20510
|
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20357
20511
|
var requireManifest = (value) => {
|
|
@@ -20430,7 +20584,7 @@ var windowsJarsignerPath = (path, capture) => {
|
|
|
20430
20584
|
}
|
|
20431
20585
|
return translated;
|
|
20432
20586
|
};
|
|
20433
|
-
var sha256File = async (path) => createHash2("sha256").update(await
|
|
20587
|
+
var sha256File = async (path) => createHash2("sha256").update(await readFile10(path)).digest("hex");
|
|
20434
20588
|
var fingerprintExpoAndroidProject = async (nativeDirectory) => {
|
|
20435
20589
|
const root = await realpath(nativeDirectory);
|
|
20436
20590
|
const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
|
|
@@ -20438,7 +20592,7 @@ var fingerprintExpoAndroidProject = async (nativeDirectory) => {
|
|
|
20438
20592
|
const parts = path.replaceAll("\\", "/").split("/");
|
|
20439
20593
|
return !parts.includes(".gradle") && !parts.includes("build");
|
|
20440
20594
|
}).sort().map(async (path) => {
|
|
20441
|
-
const contents = await
|
|
20595
|
+
const contents = await readFile10(join9(root, path));
|
|
20442
20596
|
return `${path.replaceAll("\\", "/")}\x00${createHash2("sha256").update(contents).digest("hex")}\x00`;
|
|
20443
20597
|
}));
|
|
20444
20598
|
return createHash2("sha256").update(records.join("")).digest("hex");
|
|
@@ -20453,11 +20607,11 @@ var safeOutputDirectory = (projectRoot, requested) => {
|
|
|
20453
20607
|
return output;
|
|
20454
20608
|
};
|
|
20455
20609
|
var installRelease = async (artifactPath, metadata, outputRoot) => {
|
|
20456
|
-
const releaseRoot =
|
|
20610
|
+
const releaseRoot = join9(outputRoot, metadata.releaseId);
|
|
20457
20611
|
const artifactName = "app-release.aab";
|
|
20458
|
-
const destination =
|
|
20612
|
+
const destination = join9(releaseRoot, artifactName);
|
|
20459
20613
|
if (await pathExists3(releaseRoot)) {
|
|
20460
|
-
const existing = requireManifestIdentity(JSON.parse(await
|
|
20614
|
+
const existing = requireManifestIdentity(JSON.parse(await readFile10(join9(releaseRoot, "release.json"), "utf8")), metadata);
|
|
20461
20615
|
const [installedBytes, installedSha256] = await Promise.all([
|
|
20462
20616
|
stat2(destination).then(({ size }) => size),
|
|
20463
20617
|
sha256File(destination)
|
|
@@ -20467,17 +20621,17 @@ var installRelease = async (artifactPath, metadata, outputRoot) => {
|
|
|
20467
20621
|
}
|
|
20468
20622
|
return { artifactPath: destination, metadata: existing, releaseRoot };
|
|
20469
20623
|
}
|
|
20470
|
-
await
|
|
20471
|
-
const staging = await mkdtemp(
|
|
20624
|
+
await mkdir7(dirname7(releaseRoot), { recursive: true });
|
|
20625
|
+
const staging = await mkdtemp(join9(dirname7(releaseRoot), ".android-stage-"));
|
|
20472
20626
|
try {
|
|
20473
|
-
await copyFile(artifactPath,
|
|
20627
|
+
await copyFile(artifactPath, join9(staging, artifactName));
|
|
20474
20628
|
const complete = {
|
|
20475
20629
|
...metadata,
|
|
20476
20630
|
artifact: artifactName
|
|
20477
20631
|
};
|
|
20478
|
-
await
|
|
20632
|
+
await writeFile9(join9(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
|
|
20479
20633
|
`, { flag: "wx" });
|
|
20480
|
-
await
|
|
20634
|
+
await rename8(staging, releaseRoot);
|
|
20481
20635
|
return { artifactPath: destination, metadata: complete, releaseRoot };
|
|
20482
20636
|
} finally {
|
|
20483
20637
|
await rm4(staging, { force: true, recursive: true }).catch(() => {
|
|
@@ -20505,8 +20659,8 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
20505
20659
|
const projectRoot = resolve5(options.projectRoot);
|
|
20506
20660
|
const host = options.host ?? detectAbsoluteMobileHost();
|
|
20507
20661
|
const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
|
|
20508
|
-
const nativeDirectory =
|
|
20509
|
-
const manifest = requireManifest(JSON.parse(await
|
|
20662
|
+
const nativeDirectory = join9(options.config.nativeProjectDirectory, "android");
|
|
20663
|
+
const manifest = requireManifest(JSON.parse(await readFile10(join9(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
|
|
20510
20664
|
if (manifest.appId !== options.config.appId) {
|
|
20511
20665
|
throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
|
|
20512
20666
|
}
|
|
@@ -20595,20 +20749,20 @@ var buildAbsoluteAndroidRelease = async (options) => {
|
|
|
20595
20749
|
import { createHash as createHash3 } from "crypto";
|
|
20596
20750
|
import {
|
|
20597
20751
|
access as access6,
|
|
20598
|
-
mkdir as
|
|
20752
|
+
mkdir as mkdir8,
|
|
20599
20753
|
mkdtemp as mkdtemp2,
|
|
20600
|
-
readFile as
|
|
20601
|
-
rename as
|
|
20754
|
+
readFile as readFile11,
|
|
20755
|
+
rename as rename9,
|
|
20602
20756
|
rm as rm5,
|
|
20603
20757
|
stat as stat3,
|
|
20604
|
-
writeFile as
|
|
20758
|
+
writeFile as writeFile10
|
|
20605
20759
|
} from "fs/promises";
|
|
20606
20760
|
import { homedir } from "os";
|
|
20607
20761
|
import {
|
|
20608
20762
|
basename,
|
|
20609
|
-
dirname as
|
|
20763
|
+
dirname as dirname8,
|
|
20610
20764
|
isAbsolute as isAbsolute3,
|
|
20611
|
-
join as
|
|
20765
|
+
join as join10,
|
|
20612
20766
|
relative as relative5,
|
|
20613
20767
|
resolve as resolve6,
|
|
20614
20768
|
sep as sep3
|
|
@@ -20662,7 +20816,10 @@ var parseAbsoluteAndroidInstalledApp = (appId, output) => {
|
|
|
20662
20816
|
var ABSOLUTE_BUNDLETOOL_VERSION = "1.18.3";
|
|
20663
20817
|
var ABSOLUTE_BUNDLETOOL_SHA256 = "a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29";
|
|
20664
20818
|
var ABSOLUTE_BUNDLETOOL_URL = `https://github.com/google/bundletool/releases/download/${ABSOLUTE_BUNDLETOOL_VERSION}/bundletool-all-${ABSOLUTE_BUNDLETOOL_VERSION}.jar`;
|
|
20665
|
-
var
|
|
20819
|
+
var EMBEDDED_READY_MARKERS = {
|
|
20820
|
+
capacitor: "Capacitor embedded web content ready",
|
|
20821
|
+
expo: "Expo embedded web content ready"
|
|
20822
|
+
};
|
|
20666
20823
|
var RELEASE_READY_POLL_MS = 250;
|
|
20667
20824
|
var RELEASE_READY_TIMEOUT_MS = 30000;
|
|
20668
20825
|
var RELEASE_STABILITY_MS = 2000;
|
|
@@ -20675,7 +20832,7 @@ var exists2 = async (path) => {
|
|
|
20675
20832
|
return false;
|
|
20676
20833
|
}
|
|
20677
20834
|
};
|
|
20678
|
-
var sha256 = async (path) => createHash3("sha256").update(await
|
|
20835
|
+
var sha256 = async (path) => createHash3("sha256").update(await readFile11(path)).digest("hex");
|
|
20679
20836
|
var inside = (root, candidate) => {
|
|
20680
20837
|
const path = relative5(resolve6(root), resolve6(candidate));
|
|
20681
20838
|
return path === "" || !isAbsolute3(path) && path !== ".." && !path.startsWith(`..${sep3}`);
|
|
@@ -20717,7 +20874,7 @@ var parseMetadata = (value) => {
|
|
|
20717
20874
|
throw new TypeError("Android release ID does not match its artifact digest.");
|
|
20718
20875
|
return metadata;
|
|
20719
20876
|
};
|
|
20720
|
-
var absoluteBundletoolPath = (root = process.env.ABSOLUTE_BUNDLETOOL_ROOT ??
|
|
20877
|
+
var absoluteBundletoolPath = (root = process.env.ABSOLUTE_BUNDLETOOL_ROOT ?? join10(homedir(), ".absolutejs", "tools", "bundletool")) => join10(root, ABSOLUTE_BUNDLETOOL_VERSION, "bundletool.jar");
|
|
20721
20878
|
var inspectAbsoluteBundletool = async (root) => {
|
|
20722
20879
|
const path = absoluteBundletoolPath(root);
|
|
20723
20880
|
if (!await exists2(path))
|
|
@@ -20731,9 +20888,9 @@ var readAbsoluteAndroidRelease = async (projectRoot, requested) => {
|
|
|
20731
20888
|
const selected = resolve6(root, requested);
|
|
20732
20889
|
if (!inside(root, selected))
|
|
20733
20890
|
throw new TypeError("mobile test android --release must remain inside the project.");
|
|
20734
|
-
const metadataPath = basename(selected) === "release.json" ? selected :
|
|
20735
|
-
const releaseRoot =
|
|
20736
|
-
const metadata = parseMetadata(JSON.parse(await
|
|
20891
|
+
const metadataPath = basename(selected) === "release.json" ? selected : join10(selected, "release.json");
|
|
20892
|
+
const releaseRoot = dirname8(metadataPath);
|
|
20893
|
+
const metadata = parseMetadata(JSON.parse(await readFile11(metadataPath, "utf8")));
|
|
20737
20894
|
const artifactPath = resolve6(releaseRoot, metadata.artifact);
|
|
20738
20895
|
if (!inside(releaseRoot, artifactPath) || basename(artifactPath) !== metadata.artifact)
|
|
20739
20896
|
throw new TypeError("Android release artifact path is invalid.");
|
|
@@ -20761,12 +20918,12 @@ var ensureAbsoluteBundletool = async (options) => {
|
|
|
20761
20918
|
const digest = createHash3("sha256").update(bytes).digest("hex");
|
|
20762
20919
|
if (digest !== ABSOLUTE_BUNDLETOOL_SHA256)
|
|
20763
20920
|
throw new TypeError("Downloaded Bundletool failed checksum verification.");
|
|
20764
|
-
await
|
|
20765
|
-
const staging = await mkdtemp2(
|
|
20766
|
-
const candidate =
|
|
20921
|
+
await mkdir8(dirname8(inspected.path), { recursive: true });
|
|
20922
|
+
const staging = await mkdtemp2(join10(dirname8(inspected.path), ".install-"));
|
|
20923
|
+
const candidate = join10(staging, "bundletool.jar");
|
|
20767
20924
|
try {
|
|
20768
|
-
await
|
|
20769
|
-
await
|
|
20925
|
+
await writeFile10(candidate, bytes, { flag: "wx" });
|
|
20926
|
+
await rename9(candidate, inspected.path).catch(async (error) => {
|
|
20770
20927
|
if (await exists2(inspected.path))
|
|
20771
20928
|
return;
|
|
20772
20929
|
throw error;
|
|
@@ -20803,7 +20960,7 @@ var adb = (path, serial, ...args) => [
|
|
|
20803
20960
|
serial,
|
|
20804
20961
|
...args
|
|
20805
20962
|
];
|
|
20806
|
-
var waitForMarker = async (run, adbPath, serial, appId, timeoutMs) => {
|
|
20963
|
+
var waitForMarker = async (run, adbPath, serial, appId, engine, timeoutMs) => {
|
|
20807
20964
|
const startedAt = performance.now();
|
|
20808
20965
|
const inspect = async () => {
|
|
20809
20966
|
if (performance.now() - startedAt >= timeoutMs)
|
|
@@ -20814,9 +20971,9 @@ var waitForMarker = async (run, adbPath, serial, appId, timeoutMs) => {
|
|
|
20814
20971
|
]);
|
|
20815
20972
|
if (process2.exitCode !== 0 || !process2.stdout.trim())
|
|
20816
20973
|
throw new Error("The installed Android release exited before becoming ready.");
|
|
20817
|
-
if (logs.stdout.includes(
|
|
20974
|
+
if (logs.stdout.includes(EMBEDDED_READY_MARKERS[engine]))
|
|
20818
20975
|
return;
|
|
20819
|
-
const failedPhase = /Expo embedded web phase: (assets-(?:root|directory|destination|module|source|download|copy|read|write|finalize|unexpected)|devices)-failed/u.exec(logs.stdout)?.[1];
|
|
20976
|
+
const failedPhase = engine === "expo" ? /Expo embedded web phase: (assets-(?:root|directory|destination|module|source|download|copy|read|write|finalize|unexpected)|devices)-failed/u.exec(logs.stdout)?.[1] : undefined;
|
|
20820
20977
|
if (failedPhase)
|
|
20821
20978
|
throw new Error(`The installed release could not initialize its embedded ${failedPhase} runtime offline.`);
|
|
20822
20979
|
await Bun.sleep(RELEASE_READY_POLL_MS);
|
|
@@ -20834,14 +20991,12 @@ var launch = async (run, adbPath, serial, appId, launchUrl) => {
|
|
|
20834
20991
|
return Math.round(performance.now() - startedAt);
|
|
20835
20992
|
};
|
|
20836
20993
|
var runAbsoluteAndroidReleaseAcceptance = async (options) => {
|
|
20837
|
-
if (options.release.metadata.engine !== "expo")
|
|
20838
|
-
throw new TypeError("Installed AAB release acceptance currently requires the Expo engine.");
|
|
20839
20994
|
if (!options.serial.startsWith("emulator-"))
|
|
20840
20995
|
throw new TypeError("Installed release acceptance changes network state and therefore requires an Android emulator.");
|
|
20841
20996
|
const run = options.run ?? defaultRun;
|
|
20842
20997
|
const startedAt = performance.now();
|
|
20843
|
-
await
|
|
20844
|
-
const apksPath =
|
|
20998
|
+
await mkdir8(options.artifactDirectory, { recursive: true });
|
|
20999
|
+
const apksPath = join10(options.artifactDirectory, "release.apks");
|
|
20845
21000
|
const windowsJava = (options.host ?? detectAbsoluteMobileHost()) === "wsl" && options.java.toLowerCase().endsWith(".exe");
|
|
20846
21001
|
const toolPath = async (path) => {
|
|
20847
21002
|
if (!windowsJava)
|
|
@@ -20896,12 +21051,12 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
|
|
|
20896
21051
|
]);
|
|
20897
21052
|
await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release stop");
|
|
20898
21053
|
launchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
|
|
20899
|
-
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
|
|
21054
|
+
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, options.release.metadata.engine, RELEASE_READY_TIMEOUT_MS);
|
|
20900
21055
|
await Bun.sleep(options.stabilityMs ?? RELEASE_STABILITY_MS);
|
|
20901
21056
|
await requireSuccess(run, adb(options.adb, options.serial, "shell", "am", "force-stop", options.release.metadata.appId), "Android release relaunch stop");
|
|
20902
21057
|
await requireSuccess(run, adb(options.adb, options.serial, "logcat", "-c"), "Android release relaunch log isolation");
|
|
20903
21058
|
relaunchMs = await launch(run, options.adb, options.serial, options.release.metadata.appId, options.launchUrl);
|
|
20904
|
-
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, RELEASE_READY_TIMEOUT_MS);
|
|
21059
|
+
await waitForMarker(run, options.adb, options.serial, options.release.metadata.appId, options.release.metadata.engine, RELEASE_READY_TIMEOUT_MS);
|
|
20905
21060
|
} finally {
|
|
20906
21061
|
await Promise.all([
|
|
20907
21062
|
requireSuccess(run, adb(options.adb, options.serial, "shell", "svc", "wifi", wifi.stdout.trim() === "1" ? "enable" : "disable"), "Android Wi-Fi restore"),
|
|
@@ -20969,7 +21124,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
|
20969
21124
|
};
|
|
20970
21125
|
|
|
20971
21126
|
// src/mobile/iosConformance.ts
|
|
20972
|
-
import { readFile as
|
|
21127
|
+
import { readFile as readFile12, stat as stat4 } from "fs/promises";
|
|
20973
21128
|
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");
|
|
20974
21129
|
var parseAbsoluteIosHmrLog = (line) => {
|
|
20975
21130
|
const match = HMR_LINE.exec(line);
|
|
@@ -21007,7 +21162,7 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21007
21162
|
if (Date.now() > deadline)
|
|
21008
21163
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
21009
21164
|
options.signal?.throwIfAborted();
|
|
21010
|
-
const contents = await
|
|
21165
|
+
const contents = await readFile12(options.logPath).catch(() => Buffer.alloc(0));
|
|
21011
21166
|
if (contents.byteLength < offset) {
|
|
21012
21167
|
offset = 0;
|
|
21013
21168
|
buffered = "";
|
|
@@ -21028,8 +21183,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21028
21183
|
};
|
|
21029
21184
|
|
|
21030
21185
|
// src/mobile/nativeTestReport.ts
|
|
21031
|
-
import { mkdir as
|
|
21032
|
-
import { join as
|
|
21186
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
21187
|
+
import { join as join11 } from "path";
|
|
21033
21188
|
var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
|
|
21034
21189
|
var bearerPattern = /bearer\s+[^\s,;]+/giu;
|
|
21035
21190
|
var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
|
|
@@ -21158,7 +21313,7 @@ var createAbsoluteNativeTestReport = (options) => ({
|
|
|
21158
21313
|
run: options.run
|
|
21159
21314
|
});
|
|
21160
21315
|
var readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
21161
|
-
const manifest = JSON.parse(await
|
|
21316
|
+
const manifest = JSON.parse(await readFile13(packageJsonPath, "utf8"));
|
|
21162
21317
|
if (typeof manifest !== "object" || manifest === null)
|
|
21163
21318
|
return "unknown";
|
|
21164
21319
|
const version = Reflect.get(manifest, "version");
|
|
@@ -21196,13 +21351,13 @@ ${table(report.manualChecks)}
|
|
|
21196
21351
|
`;
|
|
21197
21352
|
};
|
|
21198
21353
|
var writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
21199
|
-
await
|
|
21200
|
-
const jsonPath =
|
|
21201
|
-
const markdownPath =
|
|
21354
|
+
await mkdir9(directory, { recursive: true });
|
|
21355
|
+
const jsonPath = join11(directory, "report.json");
|
|
21356
|
+
const markdownPath = join11(directory, "report.md");
|
|
21202
21357
|
await Promise.all([
|
|
21203
|
-
|
|
21358
|
+
writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
|
|
21204
21359
|
`),
|
|
21205
|
-
|
|
21360
|
+
writeFile11(markdownPath, renderAbsoluteNativeTestReport(report))
|
|
21206
21361
|
]);
|
|
21207
21362
|
return { directory, jsonPath, markdownPath };
|
|
21208
21363
|
};
|
|
@@ -21553,8 +21708,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
21553
21708
|
};
|
|
21554
21709
|
|
|
21555
21710
|
// src/mobile/mobileInspect.ts
|
|
21556
|
-
import { access as access8, readFile as
|
|
21557
|
-
import { join as
|
|
21711
|
+
import { access as access8, readFile as readFile14 } from "fs/promises";
|
|
21712
|
+
import { join as join12, relative as relative7, resolve as resolve8 } from "path";
|
|
21558
21713
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
21559
21714
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
21560
21715
|
"@absolutejs/absolute",
|
|
@@ -21581,7 +21736,7 @@ var pathExists4 = async (path) => {
|
|
|
21581
21736
|
}
|
|
21582
21737
|
};
|
|
21583
21738
|
var readObject2 = async (path) => {
|
|
21584
|
-
const value = JSON.parse(await
|
|
21739
|
+
const value = JSON.parse(await readFile14(path, "utf8"));
|
|
21585
21740
|
if (!isObject2(value))
|
|
21586
21741
|
throw new TypeError("JSON root must be an object.");
|
|
21587
21742
|
return value;
|
|
@@ -21593,13 +21748,13 @@ var addPackageDeclarations = (declarations, value) => {
|
|
|
21593
21748
|
declarations.set(name, declared);
|
|
21594
21749
|
};
|
|
21595
21750
|
var packageInspections = async (projectRoot, additionalNames) => {
|
|
21596
|
-
const project = await readObject2(
|
|
21751
|
+
const project = await readObject2(join12(projectRoot, "package.json"));
|
|
21597
21752
|
const declarations = new Map;
|
|
21598
21753
|
for (const field2 of ["dependencies", "devDependencies"])
|
|
21599
21754
|
addPackageDeclarations(declarations, project[field2]);
|
|
21600
21755
|
const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
|
|
21601
21756
|
return Promise.all(names.map(async (name) => {
|
|
21602
|
-
const installedManifest = await readObject2(
|
|
21757
|
+
const installedManifest = await readObject2(join12(projectRoot, "node_modules", name, "package.json")).catch(() => {
|
|
21603
21758
|
return;
|
|
21604
21759
|
});
|
|
21605
21760
|
const installed = installedManifest?.version;
|
|
@@ -21658,7 +21813,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
|
|
|
21658
21813
|
},
|
|
21659
21814
|
format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
|
|
21660
21815
|
nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
|
|
21661
|
-
const path =
|
|
21816
|
+
const path = join12(config.nativeProjectDirectory, platform);
|
|
21662
21817
|
return {
|
|
21663
21818
|
initialized: await pathExists4(path),
|
|
21664
21819
|
path: portablePath2(projectRoot, path),
|
|
@@ -21715,8 +21870,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
21715
21870
|
|
|
21716
21871
|
// src/mobile/ciWorkflow.ts
|
|
21717
21872
|
import { existsSync } from "fs";
|
|
21718
|
-
import { access as access9, mkdir as
|
|
21719
|
-
import { dirname as
|
|
21873
|
+
import { access as access9, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile12 } from "fs/promises";
|
|
21874
|
+
import { dirname as dirname9, extname as extname2, relative as relative8, resolve as resolve9, sep as sep5 } from "path";
|
|
21720
21875
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
21721
21876
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
21722
21877
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -22191,12 +22346,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
22191
22346
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
22192
22347
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
22193
22348
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
22194
|
-
const previous = await exists3(path) ? await
|
|
22349
|
+
const previous = await exists3(path) ? await readFile15(path, "utf8") : undefined;
|
|
22195
22350
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
22196
22351
|
throw new TypeError(`${relative8(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
22197
22352
|
if (previous !== generated.workflow) {
|
|
22198
|
-
await
|
|
22199
|
-
await
|
|
22353
|
+
await mkdir10(dirname9(path), { recursive: true });
|
|
22354
|
+
await writeFile12(path, generated.workflow);
|
|
22200
22355
|
}
|
|
22201
22356
|
return {
|
|
22202
22357
|
changed: previous !== generated.workflow,
|
|
@@ -22212,23 +22367,23 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
22212
22367
|
import { createHash as createHash4, sign, verify } from "crypto";
|
|
22213
22368
|
import {
|
|
22214
22369
|
cp,
|
|
22215
|
-
mkdir as
|
|
22370
|
+
mkdir as mkdir11,
|
|
22216
22371
|
mkdtemp as mkdtemp3,
|
|
22217
|
-
readdir as
|
|
22218
|
-
readFile as
|
|
22219
|
-
rename as
|
|
22372
|
+
readdir as readdir5,
|
|
22373
|
+
readFile as readFile16,
|
|
22374
|
+
rename as rename10,
|
|
22220
22375
|
rm as rm6,
|
|
22221
22376
|
stat as stat5,
|
|
22222
|
-
writeFile as
|
|
22377
|
+
writeFile as writeFile13
|
|
22223
22378
|
} from "fs/promises";
|
|
22224
|
-
import { dirname as
|
|
22379
|
+
import { dirname as dirname10, join as join13, relative as relative9, resolve as resolve10 } from "path";
|
|
22225
22380
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
22226
22381
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
22227
22382
|
var sha2562 = (value) => createHash4("sha256").update(value).digest("hex");
|
|
22228
22383
|
var listFiles = async (root, directory = root) => {
|
|
22229
|
-
const entries = await
|
|
22384
|
+
const entries = await readdir5(directory, { withFileTypes: true });
|
|
22230
22385
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
22231
|
-
const path =
|
|
22386
|
+
const path = join13(directory, entry.name);
|
|
22232
22387
|
if (entry.isDirectory())
|
|
22233
22388
|
return listFiles(root, path);
|
|
22234
22389
|
if (!entry.isFile())
|
|
@@ -22238,10 +22393,10 @@ var listFiles = async (root, directory = root) => {
|
|
|
22238
22393
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
22239
22394
|
};
|
|
22240
22395
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
22241
|
-
const source =
|
|
22396
|
+
const source = join13(root, path);
|
|
22242
22397
|
const [metadata, contents] = await Promise.all([
|
|
22243
22398
|
stat5(source),
|
|
22244
|
-
|
|
22399
|
+
readFile16(source)
|
|
22245
22400
|
]);
|
|
22246
22401
|
return { bytes: metadata.size, path, sha256: sha2562(contents) };
|
|
22247
22402
|
}));
|
|
@@ -22276,17 +22431,17 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
22276
22431
|
value: signature.toString("base64")
|
|
22277
22432
|
}
|
|
22278
22433
|
});
|
|
22279
|
-
await
|
|
22280
|
-
const outputDirectory =
|
|
22281
|
-
const staging = await mkdtemp3(
|
|
22434
|
+
await mkdir11(outputRoot, { recursive: true });
|
|
22435
|
+
const outputDirectory = join13(outputRoot, manifest.releaseId);
|
|
22436
|
+
const staging = await mkdtemp3(join13(outputRoot, ".stage-"));
|
|
22282
22437
|
try {
|
|
22283
|
-
await cp(bundleDirectory,
|
|
22438
|
+
await cp(bundleDirectory, join13(staging, UPDATE_FILES_DIRECTORY), {
|
|
22284
22439
|
force: true,
|
|
22285
22440
|
recursive: true
|
|
22286
22441
|
});
|
|
22287
|
-
await
|
|
22442
|
+
await writeFile13(join13(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
22288
22443
|
`);
|
|
22289
|
-
await
|
|
22444
|
+
await rename10(staging, outputDirectory);
|
|
22290
22445
|
} catch (error) {
|
|
22291
22446
|
await rm6(staging, { force: true, recursive: true });
|
|
22292
22447
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
@@ -22295,11 +22450,11 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
22295
22450
|
}
|
|
22296
22451
|
return {
|
|
22297
22452
|
manifest,
|
|
22298
|
-
manifestPath:
|
|
22453
|
+
manifestPath: join13(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
22299
22454
|
outputDirectory
|
|
22300
22455
|
};
|
|
22301
22456
|
};
|
|
22302
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
22457
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile16(join13(resolve10(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
22303
22458
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
22304
22459
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
22305
22460
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -22309,8 +22464,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
22309
22464
|
};
|
|
22310
22465
|
|
|
22311
22466
|
// src/mobile/expoUpdate.ts
|
|
22312
|
-
import { access as access10, mkdir as
|
|
22313
|
-
import { dirname as
|
|
22467
|
+
import { access as access10, mkdir as mkdir12, readFile as readFile17, writeFile as writeFile14 } from "fs/promises";
|
|
22468
|
+
import { dirname as dirname11, extname as extname3, join as join14 } from "path";
|
|
22314
22469
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
22315
22470
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
22316
22471
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -22376,7 +22531,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
22376
22531
|
};
|
|
22377
22532
|
};
|
|
22378
22533
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
22379
|
-
const metadata = JSON.parse(await
|
|
22534
|
+
const metadata = JSON.parse(await readFile17(join14(options.exportDirectory, "metadata.json"), "utf8"));
|
|
22380
22535
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
22381
22536
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
22382
22537
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -22387,20 +22542,20 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22387
22542
|
runtimeVersion: options.runtimeVersion
|
|
22388
22543
|
});
|
|
22389
22544
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
22390
|
-
await Promise.all(referenced.map(({ path }) => access10(
|
|
22545
|
+
await Promise.all(referenced.map(({ path }) => access10(join14(options.exportDirectory, path)).catch(() => {
|
|
22391
22546
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
22392
22547
|
})));
|
|
22393
|
-
const destination =
|
|
22394
|
-
await
|
|
22395
|
-
await
|
|
22548
|
+
const destination = join14(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
22549
|
+
await mkdir12(dirname11(destination), { recursive: true });
|
|
22550
|
+
await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
22396
22551
|
`);
|
|
22397
22552
|
return { descriptor, path: destination };
|
|
22398
22553
|
};
|
|
22399
22554
|
|
|
22400
22555
|
// src/mobile/expoCodeSigning.ts
|
|
22401
22556
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
22402
|
-
import { access as access11, mkdir as
|
|
22403
|
-
import { dirname as
|
|
22557
|
+
import { access as access11, mkdir as mkdir13, writeFile as writeFile15 } from "fs/promises";
|
|
22558
|
+
import { dirname as dirname12, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve11, sep as sep6 } from "path";
|
|
22404
22559
|
var exists4 = async (path) => access11(path).then(() => true).catch(() => false);
|
|
22405
22560
|
var inside2 = (root, path) => {
|
|
22406
22561
|
const location = relative10(root, path);
|
|
@@ -22410,7 +22565,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22410
22565
|
const root = resolve11(options.projectRoot);
|
|
22411
22566
|
const certificatePath = resolve11(root, options.certificatePath);
|
|
22412
22567
|
const privateKeyPath = resolve11(root, options.privateKeyPath);
|
|
22413
|
-
const publicKeyPath = resolve11(root, options.publicKeyPath ?? resolve11(
|
|
22568
|
+
const publicKeyPath = resolve11(root, options.publicKeyPath ?? resolve11(dirname12(privateKeyPath), "public-key.pem"));
|
|
22414
22569
|
if (!inside2(root, certificatePath))
|
|
22415
22570
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
22416
22571
|
if (inside2(root, privateKeyPath) || inside2(root, publicKeyPath))
|
|
@@ -22440,11 +22595,11 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22440
22595
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
22441
22596
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
22442
22597
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
22443
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
22598
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir13(dirname12(path), { recursive: true })));
|
|
22444
22599
|
await Promise.all([
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22600
|
+
writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
22601
|
+
writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
22602
|
+
writeFile15(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
|
|
22448
22603
|
]);
|
|
22449
22604
|
return {
|
|
22450
22605
|
certificatePath,
|
|
@@ -22672,7 +22827,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
22672
22827
|
];
|
|
22673
22828
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
22674
22829
|
var directProjectPackages = async (projectRoot) => {
|
|
22675
|
-
const manifest = JSON.parse(await
|
|
22830
|
+
const manifest = JSON.parse(await readFile18(join15(projectRoot, "package.json"), "utf8"));
|
|
22676
22831
|
if (!isRecord7(manifest))
|
|
22677
22832
|
throw new TypeError("Application package.json must contain an object.");
|
|
22678
22833
|
const names = new Set;
|
|
@@ -22686,7 +22841,7 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
22686
22841
|
};
|
|
22687
22842
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
22688
22843
|
try {
|
|
22689
|
-
const manifest = JSON.parse(await
|
|
22844
|
+
const manifest = JSON.parse(await readFile18(join15(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
22690
22845
|
return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
22691
22846
|
} catch {
|
|
22692
22847
|
return;
|
|
@@ -22736,18 +22891,8 @@ var requireMobileConfig = (value) => {
|
|
|
22736
22891
|
}
|
|
22737
22892
|
return value;
|
|
22738
22893
|
};
|
|
22739
|
-
var capacitorExecutable = async (projectRoot) => {
|
|
22740
|
-
const executable = join14(projectRoot, "node_modules", ".bin", "cap");
|
|
22741
|
-
try {
|
|
22742
|
-
await access13(executable);
|
|
22743
|
-
return executable;
|
|
22744
|
-
} catch {
|
|
22745
|
-
throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
|
|
22746
|
-
}
|
|
22747
|
-
};
|
|
22748
22894
|
var runCapacitor = async (projectRoot, args) => {
|
|
22749
|
-
const
|
|
22750
|
-
const process2 = Bun.spawn([executable, ...args], {
|
|
22895
|
+
const process2 = Bun.spawn(["bun", "x", "--no-install", "cap", ...args], {
|
|
22751
22896
|
cwd: projectRoot,
|
|
22752
22897
|
stderr: "inherit",
|
|
22753
22898
|
stdin: "inherit",
|
|
@@ -22755,12 +22900,12 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
22755
22900
|
});
|
|
22756
22901
|
const exitCode = await process2.exited;
|
|
22757
22902
|
if (exitCode !== 0) {
|
|
22758
|
-
throw new TypeError(`Capacitor exited with status ${exitCode}
|
|
22903
|
+
throw new TypeError(`Capacitor exited with status ${exitCode}. Ensure this app directly depends on: ${CAPACITOR_PACKAGES.join(" ")}`);
|
|
22759
22904
|
}
|
|
22760
22905
|
};
|
|
22761
22906
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
22762
22907
|
var expoExecutable = async (project) => {
|
|
22763
|
-
const executable =
|
|
22908
|
+
const executable = join15(project, "node_modules", ".bin", "expo");
|
|
22764
22909
|
try {
|
|
22765
22910
|
await access13(executable);
|
|
22766
22911
|
return executable;
|
|
@@ -22794,14 +22939,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
22794
22939
|
};
|
|
22795
22940
|
var ensureExpoPackages = async (project, args) => {
|
|
22796
22941
|
try {
|
|
22797
|
-
const manifest = JSON.parse(await
|
|
22942
|
+
const manifest = JSON.parse(await readFile18(join15(project, "package.json"), "utf8"));
|
|
22798
22943
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
22799
22944
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
22800
22945
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
22801
22946
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
22802
22947
|
if (typeof expected !== "string")
|
|
22803
22948
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
22804
|
-
const installed = JSON.parse(await
|
|
22949
|
+
const installed = JSON.parse(await readFile18(join15(project, "node_modules", name, "package.json"), "utf8"));
|
|
22805
22950
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
22806
22951
|
if (typeof actual !== "string")
|
|
22807
22952
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -22959,6 +23104,7 @@ var initialize = async (args) => {
|
|
|
22959
23104
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile);
|
|
22960
23105
|
await applyAbsoluteNativeUpdates(mobile);
|
|
22961
23106
|
await applyAbsoluteNativeObservability(mobile);
|
|
23107
|
+
await applyAbsoluteNativeReleaseReadiness(mobile);
|
|
22962
23108
|
};
|
|
22963
23109
|
var sync = async (args) => {
|
|
22964
23110
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
@@ -22994,6 +23140,7 @@ var sync = async (args) => {
|
|
|
22994
23140
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
|
|
22995
23141
|
await applyAbsoluteNativeUpdates(mobile, platforms);
|
|
22996
23142
|
await applyAbsoluteNativeObservability(mobile, platforms);
|
|
23143
|
+
await applyAbsoluteNativeReleaseReadiness(mobile, platforms);
|
|
22997
23144
|
};
|
|
22998
23145
|
var associations = async (args) => {
|
|
22999
23146
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
@@ -23160,9 +23307,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23160
23307
|
temporaryDirectory: undefined
|
|
23161
23308
|
};
|
|
23162
23309
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
23163
|
-
const temporaryParent =
|
|
23164
|
-
await
|
|
23165
|
-
const temporaryDirectory = await mkdtemp4(
|
|
23310
|
+
const temporaryParent = join15(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
23311
|
+
await mkdir14(temporaryParent, { recursive: true });
|
|
23312
|
+
const temporaryDirectory = await mkdtemp4(join15(temporaryParent, ".stage-"));
|
|
23166
23313
|
try {
|
|
23167
23314
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
23168
23315
|
"export",
|
|
@@ -23174,7 +23321,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23174
23321
|
temporaryDirectory,
|
|
23175
23322
|
"--clear"
|
|
23176
23323
|
], { production: true });
|
|
23177
|
-
const generatedApp = JSON.parse(await
|
|
23324
|
+
const generatedApp = JSON.parse(await readFile18(join15(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
23178
23325
|
const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
|
|
23179
23326
|
if (!isRecord7(expoConfig))
|
|
23180
23327
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
@@ -23217,11 +23364,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
23217
23364
|
let expoExportDirectory;
|
|
23218
23365
|
try {
|
|
23219
23366
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
23220
|
-
const embedded = JSON.parse(await
|
|
23367
|
+
const embedded = JSON.parse(await readFile18(join15(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23221
23368
|
const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
|
|
23222
23369
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23223
23370
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23224
|
-
const privateKey = await
|
|
23371
|
+
const privateKey = await readFile18(resolve13(projectRoot, signingKeyPath));
|
|
23225
23372
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
23226
23373
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
23227
23374
|
format: "der",
|
|
@@ -23242,7 +23389,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23242
23389
|
channel: mobile.updates.channel,
|
|
23243
23390
|
classification,
|
|
23244
23391
|
keyId,
|
|
23245
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
23392
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join15(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
23246
23393
|
privateKey,
|
|
23247
23394
|
runtimeFingerprint
|
|
23248
23395
|
});
|
|
@@ -23770,6 +23917,7 @@ var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
|
|
|
23770
23917
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["android"]);
|
|
23771
23918
|
await applyAbsoluteNativeUpdates(mobile, ["android"]);
|
|
23772
23919
|
await applyAbsoluteNativeObservability(mobile, ["android"]);
|
|
23920
|
+
await applyAbsoluteNativeReleaseReadiness(mobile, ["android"]);
|
|
23773
23921
|
};
|
|
23774
23922
|
var prepareAndroidReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoAndroidReleaseProject(mobile, projectRoot, args) : prepareCapacitorAndroidReleaseProject(mobile, projectRoot);
|
|
23775
23923
|
var buildAndroid = async (args, prepareVersionCode) => {
|
|
@@ -23821,7 +23969,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
23821
23969
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23822
23970
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
23823
23971
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23824
|
-
console.log(`Metadata: ${
|
|
23972
|
+
console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
|
|
23825
23973
|
return release;
|
|
23826
23974
|
} finally {
|
|
23827
23975
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -23977,7 +24125,7 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
23977
24125
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23978
24126
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
23979
24127
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23980
|
-
console.log(`Metadata: ${
|
|
24128
|
+
console.log(`Metadata: ${join15(release.releaseRoot, "release.json")}`);
|
|
23981
24129
|
return release;
|
|
23982
24130
|
} finally {
|
|
23983
24131
|
stopListeningForCancellation();
|
|
@@ -24089,7 +24237,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24089
24237
|
checks.push({
|
|
24090
24238
|
id: "sync.storage-schema",
|
|
24091
24239
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
24092
|
-
path:
|
|
24240
|
+
path: join15(projectRoot, "package.json"),
|
|
24093
24241
|
platform: "host",
|
|
24094
24242
|
status: "pass"
|
|
24095
24243
|
});
|
|
@@ -24097,7 +24245,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24097
24245
|
checks.push({
|
|
24098
24246
|
id: "sync.storage-schema",
|
|
24099
24247
|
label: "Offline schema metadata is invalid",
|
|
24100
|
-
path:
|
|
24248
|
+
path: join15(projectRoot, "package.json"),
|
|
24101
24249
|
platform: "host",
|
|
24102
24250
|
remediation: error instanceof Error ? error.message : String(error),
|
|
24103
24251
|
status: "fail"
|
|
@@ -24308,12 +24456,12 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
24308
24456
|
});
|
|
24309
24457
|
};
|
|
24310
24458
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
24311
|
-
await
|
|
24312
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
24459
|
+
await mkdir14(options.artifactRoot, { recursive: true });
|
|
24460
|
+
const screenshot = options.session ? await options.session.screenshot(join15(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
24313
24461
|
return;
|
|
24314
24462
|
}) : undefined;
|
|
24315
|
-
const diagnosticPath =
|
|
24316
|
-
await
|
|
24463
|
+
const diagnosticPath = join15(options.artifactRoot, "android-failure.json");
|
|
24464
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24317
24465
|
diagnostics: options.session?.diagnostics ?? [],
|
|
24318
24466
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
24319
24467
|
platform: "android",
|
|
@@ -24334,7 +24482,7 @@ var captureAndroidReleaseScreenshot = async (adb2, serial, destination) => {
|
|
|
24334
24482
|
]);
|
|
24335
24483
|
if (exitCode !== 0)
|
|
24336
24484
|
throw new Error(`Android release screenshot failed: ${stderr.trim() || `status ${exitCode}`}`);
|
|
24337
|
-
await
|
|
24485
|
+
await writeFile16(destination, bytes);
|
|
24338
24486
|
return destination;
|
|
24339
24487
|
};
|
|
24340
24488
|
var printAndroidReleaseAcceptance = (result, json) => {
|
|
@@ -24351,8 +24499,6 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24351
24499
|
throw new TypeError("mobile test android --release requires a release directory or release.json path.");
|
|
24352
24500
|
if (!mobile.platforms.includes("android"))
|
|
24353
24501
|
throw new TypeError("mobile test android requires android in mobile.platforms.");
|
|
24354
|
-
if (mobile.engine !== "expo")
|
|
24355
|
-
throw new TypeError("mobile test android --release currently requires mobile.engine: expo; use the ordinary test command for Capacitor.");
|
|
24356
24502
|
const release = await readAbsoluteAndroidRelease(projectRoot, requested);
|
|
24357
24503
|
if (release.metadata.appId !== mobile.appId)
|
|
24358
24504
|
throw new TypeError(`Android release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
|
|
@@ -24390,7 +24536,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24390
24536
|
release,
|
|
24391
24537
|
serial
|
|
24392
24538
|
});
|
|
24393
|
-
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial,
|
|
24539
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join15(reportRoot, "android-release.png")) : undefined;
|
|
24394
24540
|
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
24395
24541
|
durationMs: result.durationMs,
|
|
24396
24542
|
engine: result.engine,
|
|
@@ -24437,9 +24583,9 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24437
24583
|
platform: "android",
|
|
24438
24584
|
success: false
|
|
24439
24585
|
});
|
|
24440
|
-
await
|
|
24441
|
-
const diagnosticPath =
|
|
24442
|
-
await
|
|
24586
|
+
await mkdir14(artifactRoot, { recursive: true });
|
|
24587
|
+
const diagnosticPath = join15(artifactRoot, "android-release-failure.json");
|
|
24588
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24443
24589
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
24444
24590
|
platform: "android",
|
|
24445
24591
|
provider: mobile.engine,
|
|
@@ -24533,7 +24679,7 @@ var testAndroid = async (args) => {
|
|
|
24533
24679
|
console.log(JSON.stringify(report, null, 2));
|
|
24534
24680
|
else
|
|
24535
24681
|
printAndroidTestReport(report);
|
|
24536
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
24682
|
+
const screenshot = reportRoot ? await session.screenshot(join15(artifactRoot, "android-emulator.png")) : undefined;
|
|
24537
24683
|
await writeRequestedAndroidReport({
|
|
24538
24684
|
adb: adb2,
|
|
24539
24685
|
args,
|
|
@@ -24732,8 +24878,8 @@ var requireCapturedCommand = (command, label) => {
|
|
|
24732
24878
|
return result;
|
|
24733
24879
|
};
|
|
24734
24880
|
var writeIosFailureArtifacts = async (options) => {
|
|
24735
|
-
await
|
|
24736
|
-
const screenshot =
|
|
24881
|
+
await mkdir14(options.artifactRoot, { recursive: true });
|
|
24882
|
+
const screenshot = join15(options.artifactRoot, "ios-failure.png");
|
|
24737
24883
|
const screenshotResult = captureCommand3([
|
|
24738
24884
|
options.xcrun,
|
|
24739
24885
|
"simctl",
|
|
@@ -24742,8 +24888,8 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
24742
24888
|
"screenshot",
|
|
24743
24889
|
screenshot
|
|
24744
24890
|
]);
|
|
24745
|
-
const diagnosticPath =
|
|
24746
|
-
await
|
|
24891
|
+
const diagnosticPath = join15(options.artifactRoot, "ios-failure.json");
|
|
24892
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24747
24893
|
appId: options.appId,
|
|
24748
24894
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
24749
24895
|
platform: "ios",
|
|
@@ -24996,8 +25142,8 @@ var testIos = async (args) => {
|
|
|
24996
25142
|
mobile.appId
|
|
24997
25143
|
], "iOS app launch");
|
|
24998
25144
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
24999
|
-
await
|
|
25000
|
-
const screenshot =
|
|
25145
|
+
await mkdir14(artifactRoot, { recursive: true });
|
|
25146
|
+
const screenshot = join15(artifactRoot, "ios-simulator.png");
|
|
25001
25147
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
25002
25148
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
25003
25149
|
const report = {
|