@absolutejs/absolute 0.20.0-beta.37 → 0.20.0-beta.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -6411,12 +6411,25 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
6411
6411
  if (candidate)
6412
6412
  return candidate;
6413
6413
  throw new TypeError("AbsoluteJS mobile push shell module is missing.");
6414
- }, escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;"), indexHtml = (appName) => `<!doctype html>
6414
+ }, escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;"), contentSecurityPolicy = (productionOrigin) => {
6415
+ const backend = new URL(productionOrigin);
6416
+ const socketOrigin = `${backend.protocol === "https:" ? "wss:" : "ws:"}//${backend.host}`;
6417
+ return [
6418
+ "default-src 'self' data: blob: https:",
6419
+ "base-uri 'none'",
6420
+ "object-src 'none'",
6421
+ "script-src 'self'",
6422
+ "style-src 'self' 'unsafe-inline'",
6423
+ `connect-src 'self' ${backend.origin} ${socketOrigin}`,
6424
+ "form-action 'none'"
6425
+ ].join("; ");
6426
+ }, indexHtml = (appName, productionOrigin) => `<!doctype html>
6415
6427
  <html>
6416
6428
  <head>
6417
6429
  <meta charset="utf-8">
6418
6430
  <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
6419
6431
  <meta name="color-scheme" content="light dark">
6432
+ <meta http-equiv="Content-Security-Policy" content="${escapeHtml(contentSecurityPolicy(productionOrigin))}">
6420
6433
  <title>${escapeHtml(appName)}</title>
6421
6434
  </head>
6422
6435
  <body>
@@ -6635,7 +6648,7 @@ void startAbsoluteMobileShell(${push ? `{ createAuth: (config, options) => creat
6635
6648
  await Promise.all([
6636
6649
  writeFile7(join17(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
6637
6650
  `),
6638
- writeFile7(join17(staging, INDEX_FILE), indexHtml(options.config.appName)),
6651
+ writeFile7(join17(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
6639
6652
  buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.deviceCapabilities, options.projectRoot)
6640
6653
  ]);
6641
6654
  await installBundle(staging, destination);
@@ -18048,28 +18061,173 @@ var DEFAULT_ROUTE_TIMEOUT_MS = 30000, DEFAULT_HMR_TIMEOUT_MS = 30000, routeExpre
18048
18061
  return apply;
18049
18062
  };
18050
18063
 
18051
- // src/mobile/releaseDoctor.ts
18052
- import { access as access8, readFile as readFile16, readdir as readdir4 } from "fs/promises";
18053
- import { dirname as dirname29, extname as extname8, join as join49, relative as relative24 } from "path";
18054
- var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18064
+ // src/mobile/mobileBundleInspection.ts
18065
+ import { createHash as createHash12 } from "crypto";
18066
+ import { access as access8, readFile as readFile16, stat as stat2 } from "fs/promises";
18067
+ import { join as join49, relative as relative24, resolve as resolve39 } from "path";
18068
+ var MOBILE_FRAMEWORKS, SHA256_PATTERN, isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), portablePath = (projectRoot, path) => {
18069
+ const value = relative24(resolve39(projectRoot), resolve39(path)).replaceAll("\\", "/");
18070
+ return value || ".";
18071
+ }, pathExists5 = async (path) => {
18055
18072
  try {
18056
18073
  await access8(path);
18057
18074
  return true;
18058
18075
  } catch {
18059
18076
  return false;
18060
18077
  }
18078
+ }, readObject = async (path) => {
18079
+ const value = JSON.parse(await readFile16(path, "utf8"));
18080
+ if (!isObject2(value))
18081
+ throw new TypeError("JSON root must be an object.");
18082
+ return value;
18083
+ }, requireString = (value, field) => {
18084
+ if (typeof value !== "string" || value.length === 0)
18085
+ throw new TypeError(`${field} must be a non-empty string.`);
18086
+ return value;
18087
+ }, requireStringArray = (value, field) => {
18088
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
18089
+ throw new TypeError(`${field} must be a string array.`);
18090
+ return value;
18091
+ }, requireBundleFile = async (root, value, field, expectedHash) => {
18092
+ const portable = requireString(value, field);
18093
+ const path = resolve39(root, portable);
18094
+ const normalizedRoot = resolve39(root);
18095
+ if (path === normalizedRoot || !path.startsWith(`${normalizedRoot}/`))
18096
+ throw new TypeError(`${field} must remain inside the mobile bundle.`);
18097
+ if (!(await stat2(path).catch(() => {
18098
+ return;
18099
+ }))?.isFile())
18100
+ throw new TypeError(`${field} does not exist in the mobile bundle.`);
18101
+ if (expectedHash !== undefined) {
18102
+ if (!SHA256_PATTERN.test(expectedHash))
18103
+ throw new TypeError(`${field} has an invalid SHA-256 digest.`);
18104
+ const actual = createHash12("sha256").update(await readFile16(path)).digest("hex");
18105
+ if (actual !== expectedHash)
18106
+ throw new TypeError(`${field} failed its SHA-256 integrity check.`);
18107
+ }
18108
+ return portable;
18109
+ }, inspectAbsoluteMobileBundle = async (config, projectRoot) => {
18110
+ const manifestPath = join49(config.bundleDirectory, "absolute-mobile-manifest.json");
18111
+ const manifest = portablePath(projectRoot, manifestPath);
18112
+ if (!await pathExists5(manifestPath))
18113
+ return { manifest, status: "missing" };
18114
+ try {
18115
+ const value = await readObject(manifestPath);
18116
+ if (value.format !== ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT)
18117
+ throw new TypeError("format is not supported by this runtime.");
18118
+ if (requireString(value.appId, "appId") !== config.appId)
18119
+ throw new TypeError("appId does not match the effective mobile config.");
18120
+ if (requireString(value.productionOrigin, "productionOrigin") !== config.productionOrigin)
18121
+ throw new TypeError("productionOrigin does not match the effective mobile config.");
18122
+ const appBuild = requireString(value.appBuild, "appBuild");
18123
+ const runtime = requireString(value.runtime, "runtime");
18124
+ if (runtime !== String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION))
18125
+ throw new TypeError("runtime is not supported by this AbsoluteJS build.");
18126
+ const capabilities = requireStringArray(value.deviceCapabilities, "deviceCapabilities").sort();
18127
+ if (!Array.isArray(value.pages) || !Array.isArray(value.routes))
18128
+ throw new TypeError("pages and routes must be arrays.");
18129
+ const pageIds = new Set;
18130
+ const frameworks7 = new Set;
18131
+ await Promise.all(value.pages.map(async (candidate) => {
18132
+ if (!isObject2(candidate))
18133
+ throw new TypeError("pages contains an invalid entry.");
18134
+ const pageId = requireString(candidate.pageId, "page.pageId");
18135
+ if (pageIds.has(pageId))
18136
+ throw new TypeError("page.pageId values must be unique.");
18137
+ pageIds.add(pageId);
18138
+ const framework = requireString(candidate.framework, "page.framework");
18139
+ if (!MOBILE_FRAMEWORKS.has(framework))
18140
+ throw new TypeError("page.framework is unsupported.");
18141
+ frameworks7.add(framework);
18142
+ const bundleHash = requireString(candidate.bundleHash, "page.bundleHash");
18143
+ requireString(candidate.contract, "page.contract");
18144
+ requireString(candidate.propsSchemaHash, "page.propsSchemaHash");
18145
+ await requireBundleFile(config.bundleDirectory, candidate.localBundlePath, "page.localBundlePath", bundleHash);
18146
+ if (candidate.localStylePath !== undefined) {
18147
+ const styleHash = requireString(candidate.styleBundleHash, "page.styleBundleHash");
18148
+ await requireBundleFile(config.bundleDirectory, candidate.localStylePath, "page.localStylePath", styleHash);
18149
+ } else if (candidate.styleBundleHash !== undefined)
18150
+ throw new TypeError("page.styleBundleHash requires page.localStylePath.");
18151
+ }));
18152
+ const routes = value.routes.map((candidate) => {
18153
+ if (!isObject2(candidate))
18154
+ throw new TypeError("routes contains an invalid entry.");
18155
+ const { method } = candidate;
18156
+ if (method !== "GET" && method !== "HEAD")
18157
+ throw new TypeError("route.method must be GET or HEAD.");
18158
+ const pageId = requireString(candidate.pageId, "route.pageId");
18159
+ if (!pageIds.has(pageId))
18160
+ throw new TypeError("route.pageId references a missing page.");
18161
+ return {
18162
+ method,
18163
+ pageId,
18164
+ pattern: requireString(candidate.pattern, "route.pattern")
18165
+ };
18166
+ });
18167
+ await Promise.all(["index.html", "absolute-mobile-bootstrap.js"].map((file) => requireBundleFile(config.bundleDirectory, file, file)));
18168
+ const entryPath = new URL(config.entry, "https://absolute.invalid").pathname;
18169
+ const entryResolved = resolveAbsoluteMobileRoute(routes, entryPath) !== undefined;
18170
+ if (!entryResolved)
18171
+ throw new TypeError("entry is not owned by an embedded route.");
18172
+ return {
18173
+ appBuild,
18174
+ auth: isObject2(value.auth),
18175
+ capabilities,
18176
+ entryResolved,
18177
+ frameworks: [...frameworks7].sort(),
18178
+ manifest,
18179
+ pageCount: value.pages.length,
18180
+ routeCount: value.routes.length,
18181
+ runtime,
18182
+ status: "valid",
18183
+ sync: isObject2(value.sync)
18184
+ };
18185
+ } catch (error) {
18186
+ return {
18187
+ issue: error instanceof Error ? error.message : "The embedded mobile manifest is invalid.",
18188
+ manifest,
18189
+ status: "invalid"
18190
+ };
18191
+ }
18192
+ };
18193
+ var init_mobileBundleInspection = __esm(() => {
18194
+ init_pageProtocol();
18195
+ init_routeMatcher();
18196
+ init_transport();
18197
+ MOBILE_FRAMEWORKS = new Set([
18198
+ "angular",
18199
+ "ember",
18200
+ "html",
18201
+ "htmx",
18202
+ "react",
18203
+ "svelte",
18204
+ "vue"
18205
+ ]);
18206
+ SHA256_PATTERN = /^[a-f0-9]{64}$/u;
18207
+ });
18208
+
18209
+ // src/mobile/releaseDoctor.ts
18210
+ import { access as access9, readFile as readFile17, readdir as readdir4 } from "fs/promises";
18211
+ import { dirname as dirname29, extname as extname8, join as join50, relative as relative25 } from "path";
18212
+ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, EXACT_VERSION_PATTERN, NOT_FOUND3 = -1, LOCK_FILES, MANUAL_REVIEW, pathExists6 = async (path) => {
18213
+ try {
18214
+ await access9(path);
18215
+ return true;
18216
+ } catch {
18217
+ return false;
18218
+ }
18061
18219
  }, inspectReleaseAsset = async (path, isDirectory, isFile2) => {
18062
18220
  if (isDirectory)
18063
18221
  return findHmrAsset(path);
18064
18222
  if (!isFile2 || !RELEASE_ASSET_EXTENSIONS.has(extname8(path)))
18065
18223
  return;
18066
- const source = await readFile16(path, "utf8");
18224
+ const source = await readFile17(path, "utf8");
18067
18225
  return HMR_ASSET_PATTERN.test(source) ? path : undefined;
18068
18226
  }, findHmrAsset = async (root) => {
18069
- if (!await pathExists5(root))
18227
+ if (!await pathExists6(root))
18070
18228
  return;
18071
18229
  const entries = await readdir4(root, { withFileTypes: true });
18072
- const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join49(root, entry.name), entry.isDirectory(), entry.isFile())));
18230
+ const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join50(root, entry.name), entry.isDirectory(), entry.isFile())));
18073
18231
  return matches.find((match) => match !== undefined);
18074
18232
  }, pass = (id, detail, path) => ({ detail, id, path, status: "pass" }), fail5 = (id, detail, path, remediation) => ({
18075
18233
  detail,
@@ -18077,7 +18235,70 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18077
18235
  path,
18078
18236
  remediation,
18079
18237
  status: "fail"
18080
- }), journalReleaseCheck = async (journalPath, platform6) => await pathExists5(journalPath) ? fail5(`${platform6}.dev-journal`, "A live-reload recovery journal is still active.", journalPath, "Close the development session or run mobile doctor again after stale-session repair.") : pass(`${platform6}.dev-journal`, "No live-reload recovery journal is active.", journalPath), parsedJson = (source) => {
18238
+ }), warn = (id, detail, path, remediation) => ({
18239
+ detail,
18240
+ id,
18241
+ path,
18242
+ remediation,
18243
+ status: "warn"
18244
+ }), readJsonObject = async (path) => {
18245
+ const value = JSON.parse(await readFile17(path, "utf8"));
18246
+ if (typeof value !== "object" || value === null || Array.isArray(value))
18247
+ throw new TypeError("JSON root must be an object.");
18248
+ return Object.fromEntries(Object.entries(value));
18249
+ }, packageDeclarations = (manifest) => {
18250
+ const declarations = new Map;
18251
+ for (const field of ["dependencies", "devDependencies"]) {
18252
+ const value = manifest[field];
18253
+ if (typeof value !== "object" || value === null || Array.isArray(value))
18254
+ continue;
18255
+ for (const [name, version2] of Object.entries(value))
18256
+ if (typeof version2 === "string")
18257
+ declarations.set(name, version2);
18258
+ }
18259
+ return declarations;
18260
+ }, versionCore = (version2) => version2.split("-")[0]?.split(".").slice(0, 2).join("."), capacitorVersionCheck = async (config, projectRoot) => {
18261
+ const manifestPath = join50(projectRoot, "package.json");
18262
+ try {
18263
+ const manifest = await readJsonObject(manifestPath);
18264
+ const declarations = packageDeclarations(manifest);
18265
+ const names = [
18266
+ "@capacitor/core",
18267
+ "@capacitor/cli",
18268
+ ...config.platforms.map((platform6) => `@capacitor/${platform6}`)
18269
+ ];
18270
+ const versions = await Promise.all(names.map(async (name) => {
18271
+ const declared = declarations.get(name);
18272
+ if (!declared || !EXACT_VERSION_PATTERN.test(declared))
18273
+ throw new TypeError(`${name} must be a direct exact dependency.`);
18274
+ const installed = await readJsonObject(join50(projectRoot, "node_modules", name, "package.json"));
18275
+ if (installed.version !== declared)
18276
+ throw new TypeError(`${name} does not match its installed version.`);
18277
+ return declared;
18278
+ }));
18279
+ const lines = new Set(versions.map(versionCore));
18280
+ if (lines.size !== 1)
18281
+ throw new TypeError("Capacitor core, CLI, and platform packages must use one major/minor line.");
18282
+ return pass("mobile.capacitor-versions", `Capacitor core, CLI, and configured platforms are pinned and aligned on ${versions[0]}.`, manifestPath);
18283
+ } catch (error) {
18284
+ return fail5("mobile.capacitor-versions", error instanceof Error ? error.message : "Capacitor package versions could not be validated.", manifestPath, "Pin @capacitor/core, @capacitor/cli, and each configured platform to exact versions on the same major/minor line, then reinstall.");
18285
+ }
18286
+ }, dependencyLockCheck = async (projectRoot) => {
18287
+ const present = (await Promise.all(LOCK_FILES.map(async (name) => ({
18288
+ exists: await pathExists6(join50(projectRoot, name)),
18289
+ name
18290
+ })))).find(({ exists: exists3 }) => exists3);
18291
+ return present ? pass("mobile.dependency-lock", `Dependency graph is locked by ${present.name}.`, join50(projectRoot, present.name)) : fail5("mobile.dependency-lock", "No supported dependency lockfile is present.", projectRoot, "Install dependencies with the project package manager and commit its lockfile before release.");
18292
+ }, productionOriginCheck = (config, projectRoot) => {
18293
+ const origin = new URL(config.productionOrigin);
18294
+ return origin.protocol === "https:" ? pass("mobile.production-origin", "Production transport uses an HTTPS origin.") : fail5("mobile.production-origin", "A loopback development origin cannot be used for a signed release.", projectRoot, "Configure mobile.server.productionOrigin with the deployed HTTPS origin.");
18295
+ }, associationIdentityCheck = (config, projectRoot) => {
18296
+ const missing = [
18297
+ ...config.platforms.includes("ios") && !config.appleAppIdPrefix ? ["mobile.deepLinks.apple.appIdPrefix"] : [],
18298
+ ...config.platforms.includes("android") && config.androidCertificateFingerprints.length === 0 ? ["mobile.deepLinks.android.sha256CertificateFingerprints"] : []
18299
+ ];
18300
+ return missing.length === 0 ? pass("mobile.association-identities", "Deep-link association identities are configured for every release platform.") : fail5("mobile.association-identities", `Release association identity is missing: ${missing.join(", ")}.`, projectRoot, "Configure the Apple application prefix and every Android signing-certificate SHA-256 fingerprint used for release.");
18301
+ }, journalReleaseCheck = async (journalPath, platform6) => await pathExists6(journalPath) ? fail5(`${platform6}.dev-journal`, "A live-reload recovery journal is still active.", journalPath, "Close the development session or run mobile doctor again after stale-session repair.") : pass(`${platform6}.dev-journal`, "No live-reload recovery journal is active.", journalPath), parsedJson = (source) => {
18081
18302
  try {
18082
18303
  const parsed = JSON.parse(source);
18083
18304
  return parsed;
@@ -18099,30 +18320,138 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18099
18320
  const allowNavigation = Reflect.get(server, "allowNavigation");
18100
18321
  return typeof Reflect.get(server, "url") === "string" || Reflect.get(server, "cleartext") === true || Array.isArray(allowNavigation) && allowNavigation.length > 0;
18101
18322
  }, capacitorConfigReleaseCheck = async (nativeConfigPath) => {
18102
- if (!await pathExists5(nativeConfigPath)) {
18323
+ if (!await pathExists6(nativeConfigPath)) {
18103
18324
  return fail5("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
18104
18325
  }
18105
- const unsafe = isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"));
18326
+ const unsafe = isUnsafeCapacitorConfig(await readFile17(nativeConfigPath, "utf8"));
18106
18327
  return unsafe ? fail5("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);
18328
+ }, capacitorIdentityCheck = async (config, platform6, nativeConfigPath) => {
18329
+ try {
18330
+ const parsed = await readJsonObject(nativeConfigPath);
18331
+ if (parsed.appId !== config.appId || parsed.appName !== config.appName)
18332
+ throw new TypeError(`${platform6} packaged application identity does not match mobile config.`);
18333
+ return pass(`${platform6}.app-identity`, `Packaged ${platform6} application identity matches mobile config.`, nativeConfigPath);
18334
+ } catch (error) {
18335
+ return fail5(`${platform6}.app-identity`, error instanceof Error ? error.message : `${platform6} application identity could not be validated.`, nativeConfigPath, `Run \`absolute mobile sync ${platform6}\` and review the generated Capacitor config.`);
18336
+ }
18107
18337
  }, manifestReleaseCheck = async (manifestPath) => {
18108
- if (!await pathExists5(manifestPath)) {
18338
+ if (!await pathExists6(manifestPath)) {
18109
18339
  return fail5("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
18110
18340
  }
18111
- const source = await readFile16(manifestPath, "utf8");
18341
+ const source = await readFile17(manifestPath, "utf8");
18112
18342
  const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
18113
18343
  const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
18114
- const networkConfigPath = networkConfigName ? join49(dirname29(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
18344
+ const networkConfigPath = networkConfigName ? join50(dirname29(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
18115
18345
  const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
18116
- const developmentTrustContents = networkConfigPath ? await readFile16(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
18346
+ const developmentTrustContents = networkConfigPath ? await readFile17(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
18117
18347
  const developmentTrust = developmentTrustReference || developmentTrustContents;
18118
18348
  return cleartext || developmentTrust ? fail5("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);
18119
18349
  }, hmrAssetsReleaseCheck = async (publicRoot) => {
18120
18350
  const hmrAsset = await findHmrAsset(publicRoot);
18121
18351
  return hmrAsset ? fail5("android.hmr-assets", "A packaged Android asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("android.hmr-assets", "Packaged Android assets contain no development HMR markers.", publicRoot);
18352
+ }, embeddedBundleReleaseCheck = async (config, projectRoot, platform6, publicRoot) => {
18353
+ const inspection = await inspectAbsoluteMobileBundle({ ...config, bundleDirectory: publicRoot }, projectRoot);
18354
+ if (inspection.status === "valid")
18355
+ return pass(`${platform6}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join50(publicRoot, "absolute-mobile-manifest.json"));
18356
+ return fail5(`${platform6}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join50(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
18357
+ }, contentSecurityPolicyCheck = async (config, platform6, publicRoot) => {
18358
+ const path = join50(publicRoot, "index.html");
18359
+ try {
18360
+ const source = await readFile17(path, "utf8");
18361
+ const requirements = [
18362
+ "Content-Security-Policy",
18363
+ "default-src 'self'",
18364
+ "base-uri 'none'",
18365
+ "object-src 'none'",
18366
+ "script-src 'self'",
18367
+ "form-action 'none'",
18368
+ config.productionOrigin
18369
+ ];
18370
+ const missing = requirements.filter((value) => !source.includes(value));
18371
+ if (missing.length > 0)
18372
+ throw new TypeError("Packaged shell CSP is missing a required AbsoluteJS directive or backend origin.");
18373
+ return pass(`${platform6}.content-security-policy`, "Packaged shell CSP restricts scripts, objects, base URLs, forms, and backend connections.", path);
18374
+ } catch (error) {
18375
+ return fail5(`${platform6}.content-security-policy`, error instanceof Error ? error.message : "Packaged shell CSP could not be validated.", path, "Rebuild the production mobile bundle with the AbsoluteJS-generated shell.");
18376
+ }
18377
+ }, sourceFiles = async (root, extensions) => {
18378
+ if (!await pathExists6(root))
18379
+ return [];
18380
+ const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
18381
+ return files.filter((file) => extensions.has(extname8(file))).map((file) => join50(root, file));
18382
+ }, containsPattern = async (paths, pattern) => {
18383
+ const sources = await Promise.all(paths.map((path) => readFile17(path, "utf8")));
18384
+ const index = sources.findIndex((source) => pattern.test(source));
18385
+ return index === NOT_FOUND3 ? undefined : paths[index];
18386
+ }, androidNativeSecurityCheck = async (androidRoot) => {
18387
+ const manifestPath = join50(androidRoot, "app/src/main/AndroidManifest.xml");
18388
+ try {
18389
+ const manifest = await readFile17(manifestPath, "utf8");
18390
+ if (/android:debuggable=["']true["']/u.test(manifest))
18391
+ throw new TypeError("Android release manifest explicitly enables application debugging.");
18392
+ const sources = await sourceFiles(join50(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
18393
+ const debugSource = await containsPattern(sources, /setWebContentsDebuggingEnabled\s*\(\s*true\s*\)/u);
18394
+ if (debugSource)
18395
+ return fail5("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.");
18396
+ return pass("android.native-debugging", "Android does not explicitly enable app or WebView debugging in release source.", manifestPath);
18397
+ } catch (error) {
18398
+ return fail5("android.native-debugging", error instanceof Error ? error.message : "Android native debugging configuration could not be validated.", manifestPath, "Remove explicit release debugging settings and rerun mobile sync.");
18399
+ }
18400
+ }, androidExportedComponentsCheck = async (manifestPath) => {
18401
+ const source = await readFile17(manifestPath, "utf8").catch(() => "");
18402
+ const exported = [
18403
+ ...source.matchAll(/<(?:activity|activity-alias|provider|receiver|service)\b[^>]*>/giu)
18404
+ ].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");
18405
+ return exported.length === 0 ? pass("android.exported-components", "No non-launcher Android component is explicitly exported.", manifestPath) : warn("android.exported-components", `${exported.length} non-launcher Android component(s) are exported and require manual authorization review.`, manifestPath, "Confirm each exported component is intentional, permission-protected where appropriate, and documented in the mobile threat model review.");
18406
+ }, androidDeepLinkProjectionCheck = async (config, manifestPath) => {
18407
+ try {
18408
+ const source = await readFile17(manifestPath, "utf8");
18409
+ const required = [
18410
+ 'android:autoVerify="true"',
18411
+ "android.intent.category.BROWSABLE",
18412
+ ...config.deepLinkHosts.map((host2) => `android:scheme="https" android:host="${host2}"`),
18413
+ ...config.deepLinkScheme ? [`android:scheme="${config.deepLinkScheme}"`] : []
18414
+ ];
18415
+ if (required.some((value) => !source.includes(value)))
18416
+ throw new TypeError("Android App Link or custom-scheme projection does not match mobile config.");
18417
+ return pass("android.deep-links", "Android verified links and custom scheme match the effective mobile config.", manifestPath);
18418
+ } catch (error) {
18419
+ return fail5("android.deep-links", error instanceof Error ? error.message : "Android deep-link projection could not be validated.", manifestPath, "Run `absolute mobile sync android` and review the AbsoluteJS-owned deep-link region.");
18420
+ }
18421
+ }, iosNativeSecurityCheck = async (iosRoot) => {
18422
+ const entitlementsPath = join50(iosRoot, "App/AbsoluteJS.entitlements");
18423
+ const entitlements = await readFile17(entitlementsPath, "utf8").catch(() => "");
18424
+ if (/<key>get-task-allow<\/key>\s*<true\s*\/>/u.test(entitlements) || /<key>com\.apple\.security\.get-task-allow<\/key>\s*<true\s*\/>/u.test(entitlements))
18425
+ return fail5("ios.native-debugging", "iOS source entitlements explicitly permit debugger attachment.", entitlementsPath, "Remove get-task-allow from source entitlements; Xcode supplies development entitlements only to debug builds.");
18426
+ const sources = await sourceFiles(join50(iosRoot, "App"), new Set([".m", ".mm", ".swift"]));
18427
+ const debugSource = await containsPattern(sources, /\.isInspectable\s*=\s*true|setInspectable\s*\(\s*true\s*\)/u);
18428
+ if (debugSource)
18429
+ return fail5("ios.native-debugging", "iOS application source unconditionally enables WebView inspection.", debugSource, "Remove unconditional WebView inspection from release source.");
18430
+ return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementsPath);
18431
+ }, iosDeepLinkProjectionCheck = async (config, iosRoot) => {
18432
+ const infoPath = join50(iosRoot, "App/App/Info.plist");
18433
+ const entitlementsPath = join50(iosRoot, "App/AbsoluteJS.entitlements");
18434
+ const projectPath = join50(iosRoot, "App/App.xcodeproj/project.pbxproj");
18435
+ try {
18436
+ const [info2, entitlements, project] = await Promise.all([
18437
+ readFile17(infoPath, "utf8"),
18438
+ readFile17(entitlementsPath, "utf8"),
18439
+ readFile17(projectPath, "utf8")
18440
+ ]);
18441
+ if (config.deepLinkScheme && (!info2.includes("<key>CFBundleURLTypes</key>") || !info2.includes(`<string>${config.deepLinkScheme}</string>`)))
18442
+ throw new TypeError("iOS custom URL scheme does not match mobile config.");
18443
+ if (config.deepLinkHosts.some((host2) => !entitlements.includes(`<string>applinks:${host2}</string>`)))
18444
+ throw new TypeError("iOS associated domains do not match mobile config.");
18445
+ if (!project.includes("CODE_SIGN_ENTITLEMENTS = App/AbsoluteJS.entitlements;"))
18446
+ throw new TypeError("iOS target does not sign the AbsoluteJS entitlements file.");
18447
+ return pass("ios.deep-links", "iOS universal links, custom scheme, and signed entitlements match mobile config.", entitlementsPath);
18448
+ } catch (error) {
18449
+ return fail5("ios.deep-links", error instanceof Error ? error.message : "iOS deep-link projection could not be validated.", entitlementsPath, "Run `absolute mobile sync ios` and review the AbsoluteJS-owned deep-link and entitlement projection.");
18450
+ }
18122
18451
  }, syncSchemaReleaseCheck = (projectRoot) => {
18123
18452
  if (!projectUsesAbsoluteSync(projectRoot))
18124
18453
  return;
18125
- const manifestPath = join49(projectRoot, "package.json");
18454
+ const manifestPath = join50(projectRoot, "package.json");
18126
18455
  try {
18127
18456
  const schema = discoverAbsoluteSyncSchema(projectRoot);
18128
18457
  const versions = schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ");
@@ -18144,8 +18473,8 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18144
18473
  }, IOS_USAGE_KEYS, androidDevicePermissionCheck = async (config, permissions) => {
18145
18474
  if (!config.platforms.includes("android") || permissions.length === 0)
18146
18475
  return;
18147
- const path = join49(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
18148
- const source = await readFile16(path, "utf8");
18476
+ const path = join50(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
18477
+ const source = await readFile17(path, "utf8");
18149
18478
  const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
18150
18479
  if (missing.length === 0)
18151
18480
  return;
@@ -18153,14 +18482,44 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18153
18482
  }, iosDevicePermissionCheck = async (config, purposes) => {
18154
18483
  if (!config.platforms.includes("ios") || purposes.length === 0)
18155
18484
  return;
18156
- const path = join49(config.nativeProjectDirectory, "ios/App/App/Info.plist");
18157
- const source = await readFile16(path, "utf8");
18485
+ const path = join50(config.nativeProjectDirectory, "ios/App/App/Info.plist");
18486
+ const source = await readFile17(path, "utf8");
18158
18487
  const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
18159
18488
  if (missing.length === 0)
18160
18489
  return;
18161
18490
  return fail5("mobile.device-capabilities", `iOS is missing usage descriptions for: ${missing.join(", ")}.`, path, "Run `absolute mobile sync ios` to regenerate detected device usage descriptions.");
18491
+ }, iosCapabilityProjectionCheck = async (config, requirements) => {
18492
+ if (!config.platforms.includes("ios"))
18493
+ return;
18494
+ const appRoot = join50(config.nativeProjectDirectory, "ios/App/App");
18495
+ const infoPath = join50(appRoot, "Info.plist");
18496
+ const info2 = await readFile17(infoPath, "utf8").catch(() => "");
18497
+ if (requirements.iosSystemBars && !/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<true\s*\/>/u.test(info2))
18498
+ return fail5("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.");
18499
+ if (requirements.iosPrivacyAccessedApis.length > 0) {
18500
+ const privacyPath = join50(appRoot, "PrivacyInfo.xcprivacy");
18501
+ const projectPath = join50(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
18502
+ const [privacy, project] = await Promise.all([
18503
+ readFile17(privacyPath, "utf8").catch(() => ""),
18504
+ readFile17(projectPath, "utf8").catch(() => "")
18505
+ ]);
18506
+ const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
18507
+ if (missing || !project.includes("PrivacyInfo.xcprivacy in Resources"))
18508
+ return fail5("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.");
18509
+ }
18510
+ if (requirements.iosPushNotifications) {
18511
+ const entitlementsPath = join50(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
18512
+ const delegatePath = join50(appRoot, "AppDelegate.swift");
18513
+ const [entitlements, delegate] = await Promise.all([
18514
+ readFile17(entitlementsPath, "utf8").catch(() => ""),
18515
+ readFile17(delegatePath, "utf8").catch(() => "")
18516
+ ]);
18517
+ if (!entitlements.includes("<key>aps-environment</key>") || !delegate.includes("capacitorDidRegisterForRemoteNotifications") || !delegate.includes("capacitorDidFailToRegisterForRemoteNotifications"))
18518
+ return fail5("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.");
18519
+ }
18520
+ return;
18162
18521
  }, deviceCapabilityReleaseCheck = async (config, projectRoot) => {
18163
- const manifestPath = join49(projectRoot, "package.json");
18522
+ const manifestPath = join50(projectRoot, "package.json");
18164
18523
  try {
18165
18524
  const plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot);
18166
18525
  assertAbsoluteDeviceCapabilityPackages(projectRoot, plan);
@@ -18171,32 +18530,41 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18171
18530
  const iosCheck = await iosDevicePermissionCheck(config, requirements.iosUsageDescriptions);
18172
18531
  if (iosCheck)
18173
18532
  return iosCheck;
18533
+ const iosProjectionCheck = await iosCapabilityProjectionCheck(config, requirements);
18534
+ if (iosProjectionCheck)
18535
+ return iosProjectionCheck;
18174
18536
  return pass("mobile.device-capabilities", plan.capabilities.length > 0 ? `Native provider packages and permission declarations match detected capabilities: ${plan.capabilities.join(", ")}.` : "No optional native device capabilities are used.", manifestPath);
18175
18537
  } catch (error) {
18176
18538
  return fail5("mobile.device-capabilities", error instanceof Error ? error.message : "Native device capability provisioning is invalid.", manifestPath, "Run `absolute mobile sync` and approve the exact capability plugins before releasing.");
18177
18539
  }
18178
18540
  }, inspectAndroidRelease = async (config, projectRoot) => {
18179
- const androidRoot = join49(config.nativeProjectDirectory, "android");
18180
- const nativeConfigPath = join49(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
18181
- const manifestPath = join49(androidRoot, "app", "src", "main", "AndroidManifest.xml");
18182
- const publicRoot = join49(androidRoot, "app", "src", "main", "assets", "public");
18183
- const journalPath = join49(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
18541
+ const androidRoot = join50(config.nativeProjectDirectory, "android");
18542
+ const nativeConfigPath = join50(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
18543
+ const manifestPath = join50(androidRoot, "app", "src", "main", "AndroidManifest.xml");
18544
+ const publicRoot = join50(androidRoot, "app", "src", "main", "assets", "public");
18545
+ const journalPath = join50(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
18184
18546
  const checks = await Promise.all([
18185
18547
  journalReleaseCheck(journalPath, "android"),
18186
18548
  capacitorConfigReleaseCheck(nativeConfigPath),
18549
+ capacitorIdentityCheck(config, "android", nativeConfigPath),
18187
18550
  manifestReleaseCheck(manifestPath),
18188
- hmrAssetsReleaseCheck(publicRoot)
18551
+ hmrAssetsReleaseCheck(publicRoot),
18552
+ embeddedBundleReleaseCheck(config, projectRoot, "android", publicRoot),
18553
+ contentSecurityPolicyCheck(config, "android", publicRoot),
18554
+ androidNativeSecurityCheck(androidRoot),
18555
+ androidExportedComponentsCheck(manifestPath),
18556
+ androidDeepLinkProjectionCheck(config, manifestPath)
18189
18557
  ]);
18190
18558
  return checks.map((check2) => ({
18191
18559
  ...check2,
18192
- path: check2.path ? relative24(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
18560
+ path: check2.path ? relative25(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
18193
18561
  }));
18194
18562
  }, inspectIosRelease = async (config, projectRoot) => {
18195
- const iosAppRoot = join49(config.nativeProjectDirectory, "ios", "App", "App");
18196
- const nativeConfigPath = join49(iosAppRoot, "capacitor.config.json");
18197
- const infoPath = join49(iosAppRoot, "Info.plist");
18198
- const publicRoot = join49(iosAppRoot, "public");
18199
- const journalPath = join49(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
18563
+ const iosAppRoot = join50(config.nativeProjectDirectory, "ios", "App", "App");
18564
+ const nativeConfigPath = join50(iosAppRoot, "capacitor.config.json");
18565
+ const infoPath = join50(iosAppRoot, "Info.plist");
18566
+ const publicRoot = join50(iosAppRoot, "public");
18567
+ const journalPath = join50(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
18200
18568
  const checks = [
18201
18569
  await journalReleaseCheck(journalPath, "ios")
18202
18570
  ];
@@ -18205,27 +18573,59 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18205
18573
  } else {
18206
18574
  checks.push(pass("ios.marketing-version", `The iOS marketing version is ${config.iosVersion}.`));
18207
18575
  }
18208
- if (!await pathExists5(nativeConfigPath)) {
18576
+ if (!await pathExists6(nativeConfigPath)) {
18209
18577
  checks.push(fail5("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
18210
- } else if (isUnsafeCapacitorConfig(await readFile16(nativeConfigPath, "utf8"))) {
18578
+ } else if (isUnsafeCapacitorConfig(await readFile17(nativeConfigPath, "utf8"))) {
18211
18579
  checks.push(fail5("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."));
18212
18580
  } else {
18213
18581
  checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
18214
18582
  }
18215
- if (!await pathExists5(infoPath)) {
18583
+ checks.push(await capacitorIdentityCheck(config, "ios", nativeConfigPath));
18584
+ if (!await pathExists6(infoPath)) {
18216
18585
  checks.push(fail5("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
18217
18586
  } else {
18218
- const info2 = await readFile16(infoPath, "utf8");
18587
+ const info2 = await readFile17(infoPath, "utf8");
18219
18588
  checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2) ? fail5("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));
18220
18589
  }
18221
18590
  const hmrAsset = await findHmrAsset(publicRoot);
18222
18591
  checks.push(hmrAsset ? fail5("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));
18592
+ checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join50(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join50(config.nativeProjectDirectory, "ios")));
18223
18593
  return checks.map((check2) => ({
18224
18594
  ...check2,
18225
- path: check2.path ? relative24(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
18595
+ path: check2.path ? relative25(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
18226
18596
  }));
18597
+ }, createAbsoluteMobileComplianceReport = (config, result) => {
18598
+ const summary = {
18599
+ failed: result.checks.filter(({ status: status2 }) => status2 === "fail").length,
18600
+ passed: result.checks.filter(({ status: status2 }) => status2 === "pass").length,
18601
+ warnings: result.checks.filter(({ status: status2 }) => status2 === "warn").length
18602
+ };
18603
+ return {
18604
+ app: {
18605
+ appId: config.appId,
18606
+ engine: config.engine,
18607
+ platforms: [...config.platforms],
18608
+ productionOrigin: config.productionOrigin
18609
+ },
18610
+ checks: result.checks.map(({ id, status: status2 }) => ({ id, status: status2 })),
18611
+ format: ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT,
18612
+ manualReview: [...MANUAL_REVIEW],
18613
+ ready: result.ready,
18614
+ summary
18615
+ };
18227
18616
  }, inspectAbsoluteMobileRelease = async (config, projectRoot) => {
18228
- const checks = config.platforms.includes("android") ? await inspectAndroidRelease(config, projectRoot) : [];
18617
+ const globalChecks = await Promise.all([
18618
+ Promise.resolve(productionOriginCheck(config, projectRoot)),
18619
+ Promise.resolve(associationIdentityCheck(config, projectRoot)),
18620
+ dependencyLockCheck(projectRoot),
18621
+ capacitorVersionCheck(config, projectRoot)
18622
+ ]);
18623
+ const checks = globalChecks.map((check2) => ({
18624
+ ...check2,
18625
+ path: check2.path ? relative25(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
18626
+ }));
18627
+ if (config.platforms.includes("android"))
18628
+ checks.push(...await inspectAndroidRelease(config, projectRoot));
18229
18629
  if (config.platforms.includes("ios")) {
18230
18630
  checks.push(...await inspectIosRelease(config, projectRoot));
18231
18631
  }
@@ -18233,25 +18633,41 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
18233
18633
  if (syncSchema) {
18234
18634
  checks.push({
18235
18635
  ...syncSchema,
18236
- path: syncSchema.path ? relative24(projectRoot, syncSchema.path).replaceAll("\\", "/") || "." : undefined
18636
+ path: syncSchema.path ? relative25(projectRoot, syncSchema.path).replaceAll("\\", "/") || "." : undefined
18237
18637
  });
18238
18638
  }
18239
18639
  const deviceCapabilities = await deviceCapabilityReleaseCheck(config, projectRoot);
18240
18640
  checks.push({
18241
18641
  ...deviceCapabilities,
18242
- path: deviceCapabilities.path ? relative24(projectRoot, deviceCapabilities.path).replaceAll("\\", "/") || "." : undefined
18642
+ path: deviceCapabilities.path ? relative25(projectRoot, deviceCapabilities.path).replaceAll("\\", "/") || "." : undefined
18243
18643
  });
18244
18644
  return {
18245
18645
  checks,
18246
- ready: checks.length > 0 && checks.every((check2) => check2.status === "pass")
18646
+ ready: checks.length > 0 && checks.every((check2) => check2.status !== "fail")
18247
18647
  };
18248
18648
  };
18249
18649
  var init_releaseDoctor = __esm(() => {
18650
+ init_mobileBundleInspection();
18250
18651
  init_nativeAuth();
18251
18652
  init_syncSchema();
18252
18653
  init_deviceCapabilities();
18253
18654
  HMR_ASSET_PATTERN = /(?:__HMR_WS__|hmr-timing|__absolute_target|absolutejs-error-overlay)/u;
18254
18655
  RELEASE_ASSET_EXTENSIONS = new Set([".html", ".js", ".mjs"]);
18656
+ EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
18657
+ LOCK_FILES = [
18658
+ "bun.lock",
18659
+ "bun.lockb",
18660
+ "package-lock.json",
18661
+ "pnpm-lock.yaml",
18662
+ "yarn.lock"
18663
+ ];
18664
+ MANUAL_REVIEW = [
18665
+ "physical-device",
18666
+ "store-privacy-questionnaire",
18667
+ "privacy-policy",
18668
+ "signing-key-custody",
18669
+ "native-sdk-data-practices"
18670
+ ];
18255
18671
  IOS_USAGE_KEYS = {
18256
18672
  camera: "NSCameraUsageDescription",
18257
18673
  "location-always": "NSLocationAlwaysAndWhenInUseUsageDescription",
@@ -18262,19 +18678,19 @@ var init_releaseDoctor = __esm(() => {
18262
18678
  });
18263
18679
 
18264
18680
  // src/mobile/androidRelease.ts
18265
- import { createHash as createHash12 } from "crypto";
18681
+ import { createHash as createHash13 } from "crypto";
18266
18682
  import {
18267
- access as access9,
18683
+ access as access10,
18268
18684
  copyFile as copyFile5,
18269
18685
  mkdir as mkdir12,
18270
18686
  mkdtemp as mkdtemp5,
18271
- readFile as readFile17,
18687
+ readFile as readFile18,
18272
18688
  rename as rename12,
18273
18689
  rm as rm8,
18274
- stat as stat2,
18690
+ stat as stat3,
18275
18691
  writeFile as writeFile14
18276
18692
  } from "fs/promises";
18277
- import { dirname as dirname30, isAbsolute as isAbsolute7, join as join50, relative as relative25, resolve as resolve39, sep as sep6 } from "path";
18693
+ import { dirname as dirname30, isAbsolute as isAbsolute7, join as join51, relative as relative26, resolve as resolve40, sep as sep6 } from "path";
18278
18694
  var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
18279
18695
  if (!isRecord13(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
18280
18696
  throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
@@ -18284,9 +18700,9 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18284
18700
  appId: value.appId,
18285
18701
  runtime: value.runtime
18286
18702
  };
18287
- }, pathExists6 = async (path) => {
18703
+ }, pathExists7 = async (path) => {
18288
18704
  try {
18289
- await access9(path);
18705
+ await access10(path);
18290
18706
  return true;
18291
18707
  } catch {
18292
18708
  return false;
@@ -18324,22 +18740,22 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18324
18740
  artifactPath
18325
18741
  ]);
18326
18742
  return result.exitCode === 0 && /jar verified/iu.test(result.stdout);
18327
- }, sha256File2 = async (path) => createHash12("sha256").update(await readFile17(path)).digest("hex"), safeOutputDirectory2 = (projectRoot, requested) => {
18328
- const root = resolve39(projectRoot);
18329
- const output = resolve39(root, requested ?? ".absolutejs/mobile/releases/android");
18330
- const projectRelative = relative25(root, output);
18743
+ }, sha256File2 = async (path) => createHash13("sha256").update(await readFile18(path)).digest("hex"), safeOutputDirectory2 = (projectRoot, requested) => {
18744
+ const root = resolve40(projectRoot);
18745
+ const output = resolve40(root, requested ?? ".absolutejs/mobile/releases/android");
18746
+ const projectRelative = relative26(root, output);
18331
18747
  if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute7(projectRelative)) {
18332
18748
  throw new TypeError("mobile build --outdir must remain inside the project.");
18333
18749
  }
18334
18750
  return output;
18335
18751
  }, installRelease2 = async (artifactPath, metadata, outputRoot) => {
18336
- const releaseRoot = join50(outputRoot, metadata.releaseId);
18752
+ const releaseRoot = join51(outputRoot, metadata.releaseId);
18337
18753
  const artifactName = "app-release.aab";
18338
- const destination = join50(releaseRoot, artifactName);
18339
- if (await pathExists6(releaseRoot)) {
18340
- const existing = requireManifestIdentity(JSON.parse(await readFile17(join50(releaseRoot, "release.json"), "utf8")), metadata);
18754
+ const destination = join51(releaseRoot, artifactName);
18755
+ if (await pathExists7(releaseRoot)) {
18756
+ const existing = requireManifestIdentity(JSON.parse(await readFile18(join51(releaseRoot, "release.json"), "utf8")), metadata);
18341
18757
  const [installedBytes, installedSha256] = await Promise.all([
18342
- stat2(destination).then(({ size }) => size),
18758
+ stat3(destination).then(({ size }) => size),
18343
18759
  sha256File2(destination)
18344
18760
  ]);
18345
18761
  if (installedBytes !== metadata.bytes || installedSha256 !== metadata.sha256) {
@@ -18348,14 +18764,14 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18348
18764
  return { artifactPath: destination, metadata: existing, releaseRoot };
18349
18765
  }
18350
18766
  await mkdir12(dirname30(releaseRoot), { recursive: true });
18351
- const staging = await mkdtemp5(join50(dirname30(releaseRoot), ".android-stage-"));
18767
+ const staging = await mkdtemp5(join51(dirname30(releaseRoot), ".android-stage-"));
18352
18768
  try {
18353
- await copyFile5(artifactPath, join50(staging, artifactName));
18769
+ await copyFile5(artifactPath, join51(staging, artifactName));
18354
18770
  const complete = {
18355
18771
  ...metadata,
18356
18772
  artifact: artifactName
18357
18773
  };
18358
- await writeFile14(join50(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
18774
+ await writeFile14(join51(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
18359
18775
  `, { flag: "wx" });
18360
18776
  await rename12(staging, releaseRoot);
18361
18777
  return { artifactPath: destination, metadata: complete, releaseRoot };
@@ -18380,18 +18796,18 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18380
18796
  if (options.versionCode !== undefined && (!Number.isSafeInteger(options.versionCode) || options.versionCode < 1 || options.versionCode > 2100000000)) {
18381
18797
  throw new TypeError("Android versionCode must be an integer from 1 through 2100000000.");
18382
18798
  }
18383
- const projectRoot = resolve39(options.projectRoot);
18799
+ const projectRoot = resolve40(options.projectRoot);
18384
18800
  const host2 = options.host ?? detectAbsoluteMobileHost();
18385
18801
  const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
18386
- const nativeDirectory = join50(options.config.nativeProjectDirectory, "android");
18387
- const manifest = requireManifest2(JSON.parse(await readFile17(join50(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
18802
+ const nativeDirectory = join51(options.config.nativeProjectDirectory, "android");
18803
+ const manifest = requireManifest2(JSON.parse(await readFile18(join51(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
18388
18804
  if (manifest.appId !== options.config.appId) {
18389
18805
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
18390
18806
  }
18391
18807
  let { versionCode } = options;
18392
18808
  if (options.prepareVersionCode) {
18393
18809
  const nativeFingerprint = await fingerprintAbsoluteAndroidNativeProject({ nativeDirectory });
18394
- const buildIdentity = createHash12("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}`).digest("hex");
18810
+ const buildIdentity = createHash13("sha256").update(`${manifest.appBuild}\x00${nativeFingerprint}`).digest("hex");
18395
18811
  versionCode = await options.prepareVersionCode(buildIdentity);
18396
18812
  }
18397
18813
  if (versionCode !== undefined && (!Number.isSafeInteger(versionCode) || versionCode < 1 || versionCode > 2100000000)) {
@@ -18410,7 +18826,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18410
18826
  run: options.run,
18411
18827
  task: "bundleRelease"
18412
18828
  });
18413
- if (!await pathExists6(artifactPath)) {
18829
+ if (!await pathExists7(artifactPath)) {
18414
18830
  throw new TypeError(`Android Gradle did not produce the expected App Bundle: ${artifactPath}`);
18415
18831
  }
18416
18832
  const capture = options.capture ?? defaultCapture4;
@@ -18422,7 +18838,7 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord13 = (value) => typeof value ==
18422
18838
  throw new TypeError("Android Gradle produced an unsigned App Bundle. Configure the release signingConfig in the source-owned Android project (prefer external Gradle properties), or pass --unsigned only for a non-publishable build.");
18423
18839
  }
18424
18840
  const [bytes, sha2562] = await Promise.all([
18425
- stat2(artifactPath).then(({ size }) => size),
18841
+ stat3(artifactPath).then(({ size }) => size),
18426
18842
  sha256File2(artifactPath)
18427
18843
  ]);
18428
18844
  const releaseId = `amobile_android_${sha2562}`;
@@ -18486,7 +18902,7 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
18486
18902
  };
18487
18903
 
18488
18904
  // src/mobile/iosConformance.ts
18489
- import { readFile as readFile18, stat as stat3 } from "fs/promises";
18905
+ import { readFile as readFile19, stat as stat4 } from "fs/promises";
18490
18906
  var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
18491
18907
  const match = HMR_LINE.exec(line);
18492
18908
  if (!match)
@@ -18515,13 +18931,13 @@ var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
18515
18931
  const sleep2 = options.sleep ?? Bun.sleep;
18516
18932
  const timeoutMs = options.timeoutMs ?? 30000;
18517
18933
  const deadline = Date.now() + timeoutMs;
18518
- let offset = options.startOffset ?? await stat3(options.logPath).then(({ size }) => size).catch(() => 0);
18934
+ let offset = options.startOffset ?? await stat4(options.logPath).then(({ size }) => size).catch(() => 0);
18519
18935
  let buffered = "";
18520
18936
  const poll = async () => {
18521
18937
  if (Date.now() > deadline)
18522
18938
  throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
18523
18939
  options.signal?.throwIfAborted();
18524
- const contents = await readFile18(options.logPath).catch(() => Buffer.alloc(0));
18940
+ const contents = await readFile19(options.logPath).catch(() => Buffer.alloc(0));
18525
18941
  if (contents.byteLength < offset) {
18526
18942
  offset = 0;
18527
18943
  buffered = "";
@@ -18545,8 +18961,8 @@ var init_iosConformance = __esm(() => {
18545
18961
  });
18546
18962
 
18547
18963
  // src/mobile/nativeTestReport.ts
18548
- import { mkdir as mkdir13, readFile as readFile19, writeFile as writeFile15 } from "fs/promises";
18549
- import { join as join51 } from "path";
18964
+ import { mkdir as mkdir13, readFile as readFile20, writeFile as writeFile15 } from "fs/promises";
18965
+ import { join as join52 } from "path";
18550
18966
  var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sanitizeNativeReportText = (value) => value.replace(nativeCredentialPattern, "[REDACTED]").replace(bearerPattern, "Bearer [REDACTED]").replace(secretPattern, "$1$2[REDACTED]").replace(coordinatePattern, "$1$2[REDACTED]").replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/giu, "$1?[REDACTED]"), markdownCell = (value) => sanitizeNativeReportText(value).replaceAll("|", "\\|").replaceAll(`
18551
18967
  `, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
18552
18968
  const target = `${run.targetKind} ${run.targetId}`;
@@ -18648,7 +19064,7 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
18648
19064
  reportVersion: 1,
18649
19065
  run: options.run
18650
19066
  }), readPackageVersionForNativeReport = async (packageJsonPath) => {
18651
- const manifest = JSON.parse(await readFile19(packageJsonPath, "utf8"));
19067
+ const manifest = JSON.parse(await readFile20(packageJsonPath, "utf8"));
18652
19068
  if (typeof manifest !== "object" || manifest === null)
18653
19069
  return "unknown";
18654
19070
  const version2 = Reflect.get(manifest, "version");
@@ -18685,8 +19101,8 @@ ${table(report.manualChecks)}
18685
19101
  `;
18686
19102
  }, writeAbsoluteNativeTestReport = async (directory, report) => {
18687
19103
  await mkdir13(directory, { recursive: true });
18688
- const jsonPath = join51(directory, "report.json");
18689
- const markdownPath = join51(directory, "report.md");
19104
+ const jsonPath = join52(directory, "report.json");
19105
+ const markdownPath = join52(directory, "report.md");
18690
19106
  await Promise.all([
18691
19107
  writeFile15(jsonPath, `${JSON.stringify(report, null, 2)}
18692
19108
  `),
@@ -18904,8 +19320,8 @@ var init_androidTestReport = __esm(() => {
18904
19320
  });
18905
19321
 
18906
19322
  // src/mobile/releasePublisher.ts
18907
- import { access as access10 } from "fs/promises";
18908
- import { isAbsolute as isAbsolute8, relative as relative26, resolve as resolve40, sep as sep7 } from "path";
19323
+ import { access as access11 } from "fs/promises";
19324
+ import { isAbsolute as isAbsolute8, relative as relative27, resolve as resolve41, sep as sep7 } from "path";
18909
19325
  import { pathToFileURL as pathToFileURL2 } from "url";
18910
19326
  var prepareAbsoluteIosRelease = async (publisher, options) => {
18911
19327
  if (typeof publisher.prepareIosRelease !== "function") {
@@ -18927,16 +19343,16 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
18927
19343
  }
18928
19344
  return versionCode;
18929
19345
  }, isRecord14 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord14(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
18930
- const root = resolve40(projectRoot);
18931
- const path = resolve40(root, requested);
18932
- const projectRelative = relative26(root, path);
19346
+ const root = resolve41(projectRoot);
19347
+ const path = resolve41(root, requested);
19348
+ const projectRelative = relative27(root, path);
18933
19349
  if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute8(projectRelative)) {
18934
19350
  throw new TypeError("mobile publish --registry must remain inside the project.");
18935
19351
  }
18936
19352
  return path;
18937
19353
  }, loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
18938
19354
  const modulePath = publisherModulePath(projectRoot, requestedModulePath);
18939
- await access10(modulePath).catch(() => {
19355
+ await access11(modulePath).catch(() => {
18940
19356
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
18941
19357
  });
18942
19358
  const loaded = await import(pathToFileURL2(modulePath).href);
@@ -18997,103 +19413,103 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
18997
19413
  var init_releasePublisher = () => {};
18998
19414
 
18999
19415
  // src/mobile/mobileInspect.ts
19000
- import { access as access11, readFile as readFile20, stat as stat4 } from "fs/promises";
19001
- import { join as join52, relative as relative27, resolve as resolve41 } from "path";
19002
- var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, MOBILE_FRAMEWORKS, isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), portablePath = (projectRoot, path) => {
19003
- const value = relative27(resolve41(projectRoot), resolve41(path)).replaceAll("\\", "/");
19416
+ import { access as access12, readFile as readFile21, stat as stat5 } from "fs/promises";
19417
+ import { join as join53, relative as relative28, resolve as resolve42 } from "path";
19418
+ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, MOBILE_FRAMEWORKS2, isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), portablePath2 = (projectRoot, path) => {
19419
+ const value = relative28(resolve42(projectRoot), resolve42(path)).replaceAll("\\", "/");
19004
19420
  return value || ".";
19005
- }, pathExists7 = async (path) => {
19421
+ }, pathExists8 = async (path) => {
19006
19422
  try {
19007
- await access11(path);
19423
+ await access12(path);
19008
19424
  return true;
19009
19425
  } catch {
19010
19426
  return false;
19011
19427
  }
19012
- }, readObject = async (path) => {
19013
- const value = JSON.parse(await readFile20(path, "utf8"));
19014
- if (!isObject2(value))
19428
+ }, readObject2 = async (path) => {
19429
+ const value = JSON.parse(await readFile21(path, "utf8"));
19430
+ if (!isObject3(value))
19015
19431
  throw new TypeError("JSON root must be an object.");
19016
19432
  return value;
19017
- }, requireString = (value, field) => {
19433
+ }, requireString2 = (value, field) => {
19018
19434
  if (typeof value !== "string" || value.length === 0)
19019
19435
  throw new TypeError(`${field} must be a non-empty string.`);
19020
19436
  return value;
19021
- }, requireStringArray = (value, field) => {
19437
+ }, requireStringArray2 = (value, field) => {
19022
19438
  if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
19023
19439
  throw new TypeError(`${field} must be a string array.`);
19024
19440
  return value;
19025
- }, requireBundleFile = async (root, value, field) => {
19026
- const portable = requireString(value, field);
19027
- const path = resolve41(root, portable);
19028
- const normalizedRoot = resolve41(root);
19441
+ }, requireBundleFile2 = async (root, value, field) => {
19442
+ const portable = requireString2(value, field);
19443
+ const path = resolve42(root, portable);
19444
+ const normalizedRoot = resolve42(root);
19029
19445
  if (path === normalizedRoot || !path.startsWith(`${normalizedRoot}/`))
19030
19446
  throw new TypeError(`${field} must remain inside the mobile bundle.`);
19031
- if (!(await stat4(path).catch(() => {
19447
+ if (!(await stat5(path).catch(() => {
19032
19448
  return;
19033
19449
  }))?.isFile())
19034
19450
  throw new TypeError(`${field} does not exist in the mobile bundle.`);
19035
19451
  return portable;
19036
19452
  }, inspectBundle = async (config, projectRoot) => {
19037
- const manifestPath = join52(config.bundleDirectory, "absolute-mobile-manifest.json");
19038
- const manifest = portablePath(projectRoot, manifestPath);
19039
- if (!await pathExists7(manifestPath))
19453
+ const manifestPath = join53(config.bundleDirectory, "absolute-mobile-manifest.json");
19454
+ const manifest = portablePath2(projectRoot, manifestPath);
19455
+ if (!await pathExists8(manifestPath))
19040
19456
  return { manifest, status: "missing" };
19041
19457
  try {
19042
- const value = await readObject(manifestPath);
19458
+ const value = await readObject2(manifestPath);
19043
19459
  if (value.format !== 1)
19044
19460
  throw new TypeError("format is not supported by this runtime.");
19045
- if (requireString(value.appId, "appId") !== config.appId)
19461
+ if (requireString2(value.appId, "appId") !== config.appId)
19046
19462
  throw new TypeError("appId does not match the effective mobile config.");
19047
- if (requireString(value.productionOrigin, "productionOrigin") !== config.productionOrigin)
19463
+ if (requireString2(value.productionOrigin, "productionOrigin") !== config.productionOrigin)
19048
19464
  throw new TypeError("productionOrigin does not match the effective mobile config.");
19049
- const appBuild = requireString(value.appBuild, "appBuild");
19050
- const runtime = requireString(value.runtime, "runtime");
19051
- const capabilities = requireStringArray(value.deviceCapabilities, "deviceCapabilities").sort();
19465
+ const appBuild = requireString2(value.appBuild, "appBuild");
19466
+ const runtime = requireString2(value.runtime, "runtime");
19467
+ const capabilities = requireStringArray2(value.deviceCapabilities, "deviceCapabilities").sort();
19052
19468
  if (!Array.isArray(value.pages) || !Array.isArray(value.routes))
19053
19469
  throw new TypeError("pages and routes must be arrays.");
19054
19470
  const pageIds = new Set;
19055
19471
  const frameworks7 = new Set;
19056
19472
  await Promise.all(value.pages.map(async (candidate) => {
19057
- if (!isObject2(candidate))
19473
+ if (!isObject3(candidate))
19058
19474
  throw new TypeError("pages contains an invalid entry.");
19059
- const pageId = requireString(candidate.pageId, "page.pageId");
19475
+ const pageId = requireString2(candidate.pageId, "page.pageId");
19060
19476
  if (pageIds.has(pageId))
19061
19477
  throw new TypeError("page.pageId values must be unique.");
19062
19478
  pageIds.add(pageId);
19063
- const framework = requireString(candidate.framework, "page.framework");
19064
- if (!MOBILE_FRAMEWORKS.has(framework))
19479
+ const framework = requireString2(candidate.framework, "page.framework");
19480
+ if (!MOBILE_FRAMEWORKS2.has(framework))
19065
19481
  throw new TypeError("page.framework is unsupported.");
19066
19482
  frameworks7.add(framework);
19067
- requireString(candidate.bundleHash, "page.bundleHash");
19068
- requireString(candidate.contract, "page.contract");
19069
- requireString(candidate.propsSchemaHash, "page.propsSchemaHash");
19070
- await requireBundleFile(config.bundleDirectory, candidate.localBundlePath, "page.localBundlePath");
19483
+ requireString2(candidate.bundleHash, "page.bundleHash");
19484
+ requireString2(candidate.contract, "page.contract");
19485
+ requireString2(candidate.propsSchemaHash, "page.propsSchemaHash");
19486
+ await requireBundleFile2(config.bundleDirectory, candidate.localBundlePath, "page.localBundlePath");
19071
19487
  if (candidate.localStylePath !== undefined)
19072
- await requireBundleFile(config.bundleDirectory, candidate.localStylePath, "page.localStylePath");
19488
+ await requireBundleFile2(config.bundleDirectory, candidate.localStylePath, "page.localStylePath");
19073
19489
  }));
19074
19490
  const routes = value.routes.map((candidate) => {
19075
- if (!isObject2(candidate))
19491
+ if (!isObject3(candidate))
19076
19492
  throw new TypeError("routes contains an invalid entry.");
19077
19493
  const { method } = candidate;
19078
19494
  if (method !== "GET" && method !== "HEAD")
19079
19495
  throw new TypeError("route.method must be GET or HEAD.");
19080
- const pageId = requireString(candidate.pageId, "route.pageId");
19496
+ const pageId = requireString2(candidate.pageId, "route.pageId");
19081
19497
  if (!pageIds.has(pageId))
19082
19498
  throw new TypeError("route.pageId references a missing page.");
19083
19499
  return {
19084
19500
  method,
19085
19501
  pageId,
19086
- pattern: requireString(candidate.pattern, "route.pattern")
19502
+ pattern: requireString2(candidate.pattern, "route.pattern")
19087
19503
  };
19088
19504
  });
19089
- await Promise.all(["index.html", "absolute-mobile-bootstrap.js"].map((file) => requireBundleFile(config.bundleDirectory, file, file)));
19505
+ await Promise.all(["index.html", "absolute-mobile-bootstrap.js"].map((file) => requireBundleFile2(config.bundleDirectory, file, file)));
19090
19506
  const entryPath = new URL(config.entry, "https://absolute.invalid").pathname;
19091
19507
  const entryResolved = resolveAbsoluteMobileRoute(routes, entryPath) !== undefined;
19092
19508
  if (!entryResolved)
19093
19509
  throw new TypeError("entry is not owned by an embedded route.");
19094
19510
  return {
19095
19511
  appBuild,
19096
- auth: isObject2(value.auth),
19512
+ auth: isObject3(value.auth),
19097
19513
  capabilities,
19098
19514
  entryResolved,
19099
19515
  frameworks: [...frameworks7].sort(),
@@ -19102,7 +19518,7 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, MOBILE_FRAMEWOR
19102
19518
  routeCount: value.routes.length,
19103
19519
  runtime,
19104
19520
  status: "valid",
19105
- sync: isObject2(value.sync)
19521
+ sync: isObject3(value.sync)
19106
19522
  };
19107
19523
  } catch (error) {
19108
19524
  return {
@@ -19112,18 +19528,18 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, MOBILE_FRAMEWOR
19112
19528
  };
19113
19529
  }
19114
19530
  }, addPackageDeclarations = (declarations, value) => {
19115
- if (!isObject2(value))
19531
+ if (!isObject3(value))
19116
19532
  return;
19117
19533
  for (const [name, declared] of Object.entries(value).filter((entry) => typeof entry[1] === "string"))
19118
19534
  declarations.set(name, declared);
19119
19535
  }, packageInspections = async (projectRoot, additionalNames) => {
19120
- const project = await readObject(join52(projectRoot, "package.json"));
19536
+ const project = await readObject2(join53(projectRoot, "package.json"));
19121
19537
  const declarations = new Map;
19122
19538
  for (const field of ["dependencies", "devDependencies"])
19123
19539
  addPackageDeclarations(declarations, project[field]);
19124
19540
  const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
19125
19541
  return Promise.all(names.map(async (name) => {
19126
- const installedManifest = await readObject(join52(projectRoot, "node_modules", name, "package.json")).catch(() => {
19542
+ const installedManifest = await readObject2(join53(projectRoot, "node_modules", name, "package.json")).catch(() => {
19127
19543
  return;
19128
19544
  });
19129
19545
  const installed = installedManifest?.version;
@@ -19160,22 +19576,22 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, MOBILE_FRAMEWOR
19160
19576
  config: {
19161
19577
  appId: config.appId,
19162
19578
  appName: config.appName,
19163
- bundleDirectory: portablePath(projectRoot, config.bundleDirectory),
19579
+ bundleDirectory: portablePath2(projectRoot, config.bundleDirectory),
19164
19580
  deepLinkHosts: config.deepLinkHosts,
19165
19581
  deepLinkScheme: config.deepLinkScheme,
19166
19582
  engine: config.engine,
19167
19583
  entry: config.entry,
19168
19584
  iosVersion: config.iosVersion,
19169
- nativeProjectDirectory: portablePath(projectRoot, config.nativeProjectDirectory),
19585
+ nativeProjectDirectory: portablePath2(projectRoot, config.nativeProjectDirectory),
19170
19586
  platforms: config.platforms,
19171
19587
  productionOrigin: config.productionOrigin
19172
19588
  },
19173
19589
  format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
19174
19590
  nativeProjects: await Promise.all(config.platforms.map(async (platform6) => {
19175
- const path = join52(config.nativeProjectDirectory, platform6);
19591
+ const path = join53(config.nativeProjectDirectory, platform6);
19176
19592
  return {
19177
- initialized: await pathExists7(path),
19178
- path: portablePath(projectRoot, path),
19593
+ initialized: await pathExists8(path),
19594
+ path: portablePath2(projectRoot, path),
19179
19595
  platform: platform6
19180
19596
  };
19181
19597
  })),
@@ -19239,7 +19655,7 @@ var init_mobileInspect = __esm(() => {
19239
19655
  "@absolutejs/sync-capacitor",
19240
19656
  "@capacitor-community/sqlite"
19241
19657
  ]);
19242
- MOBILE_FRAMEWORKS = new Set([
19658
+ MOBILE_FRAMEWORKS2 = new Set([
19243
19659
  "angular",
19244
19660
  "ember",
19245
19661
  "html",
@@ -19255,11 +19671,11 @@ var exports_mobile = {};
19255
19671
  __export(exports_mobile, {
19256
19672
  runMobile: () => runMobile
19257
19673
  });
19258
- import { access as access12, mkdir as mkdir14, readFile as readFile21, writeFile as writeFile16 } from "fs/promises";
19259
- import { join as join53, resolve as resolve42 } from "path";
19674
+ import { access as access13, mkdir as mkdir14, readFile as readFile22, writeFile as writeFile16 } from "fs/promises";
19675
+ import { join as join54, resolve as resolve43 } from "path";
19260
19676
  import { createInterface } from "readline/promises";
19261
- var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
19262
- const manifest = JSON.parse(await readFile21(join53(projectRoot, "package.json"), "utf8"));
19677
+ var NOT_FOUND4 = -1, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
19678
+ const manifest = JSON.parse(await readFile22(join54(projectRoot, "package.json"), "utf8"));
19263
19679
  if (!isRecord15(manifest))
19264
19680
  throw new TypeError("Application package.json must contain an object.");
19265
19681
  const names = new Set;
@@ -19272,7 +19688,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19272
19688
  return names;
19273
19689
  }, resolvedPackageVersion = async (projectRoot, packageName) => {
19274
19690
  try {
19275
- const manifest = JSON.parse(await readFile21(join53(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
19691
+ const manifest = JSON.parse(await readFile22(join54(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
19276
19692
  return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
19277
19693
  } catch {
19278
19694
  return;
@@ -19303,7 +19719,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19303
19719
  await installApprovedPackages(projectRoot, args, `AbsoluteJS detected native device capabilities (${capabilityPlan.capabilities.join(", ")}). Install only their required Capacitor plugins now?`, capabilityPackages);
19304
19720
  }, valueAfter = (args, flag) => {
19305
19721
  const index = args.indexOf(flag);
19306
- return index === NOT_FOUND3 ? undefined : args[index + 1];
19722
+ return index === NOT_FOUND4 ? undefined : args[index + 1];
19307
19723
  }, valuesAfter = (args, flag) => args.flatMap((value, index) => {
19308
19724
  const next = args[index + 1];
19309
19725
  return value === flag && next !== undefined ? [next] : [];
@@ -19313,9 +19729,9 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19313
19729
  }
19314
19730
  return value;
19315
19731
  }, capacitorExecutable = async (projectRoot) => {
19316
- const executable = join53(projectRoot, "node_modules", ".bin", "cap");
19732
+ const executable = join54(projectRoot, "node_modules", ".bin", "cap");
19317
19733
  try {
19318
- await access12(executable);
19734
+ await access13(executable);
19319
19735
  return executable;
19320
19736
  } catch {
19321
19737
  throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
@@ -19412,7 +19828,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19412
19828
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
19413
19829
  }, associations = async (args) => {
19414
19830
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
19415
- const outputDirectory = resolve42(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19831
+ const outputDirectory = resolve43(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19416
19832
  if (args.includes("--verify")) {
19417
19833
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
19418
19834
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -19457,7 +19873,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19457
19873
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
19458
19874
  const result = await inspectAbsoluteMobileRelease(mobile, projectRoot);
19459
19875
  if (args.includes("--json")) {
19460
- console.log(JSON.stringify(result, null, 2));
19876
+ console.log(JSON.stringify(createAbsoluteMobileComplianceReport(mobile, result), null, 2));
19461
19877
  } else {
19462
19878
  result.checks.forEach((check2) => {
19463
19879
  console.log(`${doctorMark(check2.status)} ${check2.detail}${check2.path ? ` (${check2.path})` : ""}`);
@@ -19465,8 +19881,8 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19465
19881
  console.log(` ${check2.remediation}`);
19466
19882
  });
19467
19883
  console.log(result.ready ? `
19468
- Mobile release transport checks passed.` : `
19469
- Mobile release transport checks failed.`);
19884
+ Mobile release security and compliance checks passed.` : `
19885
+ Mobile release security and compliance checks failed.`);
19470
19886
  }
19471
19887
  if (!result.ready) {
19472
19888
  throw new TypeError("Mobile release validation failed. Resolve every failed check before signing or publishing the app.");
@@ -19645,7 +20061,7 @@ Mobile release transport checks failed.`);
19645
20061
  const durationMs = Math.round(performance.now() - startedAt);
19646
20062
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
19647
20063
  console.log(`Artifact: ${release.artifactPath}`);
19648
- console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
20064
+ console.log(`Metadata: ${join54(release.releaseRoot, "release.json")}`);
19649
20065
  return release;
19650
20066
  } finally {
19651
20067
  sendTelemetryEvent("mobile:android-release-build", {
@@ -19748,7 +20164,7 @@ Mobile release transport checks failed.`);
19748
20164
  const durationMs = Math.round(performance.now() - startedAt);
19749
20165
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
19750
20166
  console.log(`Artifact: ${release.artifactPath}`);
19751
- console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
20167
+ console.log(`Metadata: ${join54(release.releaseRoot, "release.json")}`);
19752
20168
  return release;
19753
20169
  } finally {
19754
20170
  sendTelemetryEvent("mobile:ios-release-build", {
@@ -19855,7 +20271,7 @@ Mobile release transport checks failed.`);
19855
20271
  checks.push({
19856
20272
  id: "sync.storage-schema",
19857
20273
  label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
19858
- path: join53(projectRoot, "package.json"),
20274
+ path: join54(projectRoot, "package.json"),
19859
20275
  platform: "host",
19860
20276
  status: "pass"
19861
20277
  });
@@ -19863,7 +20279,7 @@ Mobile release transport checks failed.`);
19863
20279
  checks.push({
19864
20280
  id: "sync.storage-schema",
19865
20281
  label: "Offline schema metadata is invalid",
19866
- path: join53(projectRoot, "package.json"),
20282
+ path: join54(projectRoot, "package.json"),
19867
20283
  platform: "host",
19868
20284
  remediation: error instanceof Error ? error.message : String(error),
19869
20285
  status: "fail"
@@ -19948,7 +20364,7 @@ Emulator setup verification:`);
19948
20364
  }
19949
20365
  return { https: args.includes("--https"), port };
19950
20366
  }
19951
- const instances = listLiveInstances().filter((instance2) => resolve42(instance2.cwd) === resolve42(projectRoot) && instance2.source === "dev" && instance2.port !== null);
20367
+ const instances = listLiveInstances().filter((instance2) => resolve43(instance2.cwd) === resolve43(projectRoot) && instance2.source === "dev" && instance2.port !== null);
19952
20368
  if (instances.length !== 1) {
19953
20369
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
19954
20370
  }
@@ -19991,8 +20407,8 @@ Emulator setup verification:`);
19991
20407
  }
19992
20408
  return selected;
19993
20409
  }, safeArtifactRoot = (projectRoot, value) => {
19994
- const root = resolve42(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
19995
- if (root !== projectRoot && !root.startsWith(`${resolve42(projectRoot)}/`)) {
20410
+ const root = resolve43(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
20411
+ if (root !== projectRoot && !root.startsWith(`${resolve43(projectRoot)}/`)) {
19996
20412
  throw new TypeError("mobile test --artifacts must remain inside the project.");
19997
20413
  }
19998
20414
  return root;
@@ -20017,10 +20433,10 @@ Emulator setup verification:`);
20017
20433
  });
20018
20434
  }, writeAndroidFailureArtifacts = async (options) => {
20019
20435
  await mkdir14(options.artifactRoot, { recursive: true });
20020
- const screenshot = options.session ? await options.session.screenshot(join53(options.artifactRoot, "android-failure.png")).catch(() => {
20436
+ const screenshot = options.session ? await options.session.screenshot(join54(options.artifactRoot, "android-failure.png")).catch(() => {
20021
20437
  return;
20022
20438
  }) : undefined;
20023
- const diagnosticPath = join53(options.artifactRoot, "android-failure.json");
20439
+ const diagnosticPath = join54(options.artifactRoot, "android-failure.json");
20024
20440
  await writeFile16(diagnosticPath, `${JSON.stringify({
20025
20441
  diagnostics: options.session?.diagnostics ?? [],
20026
20442
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -20084,7 +20500,7 @@ Emulator setup verification:`);
20084
20500
  console.log(JSON.stringify(report, null, 2));
20085
20501
  else
20086
20502
  printAndroidTestReport(report);
20087
- const screenshot = reportRoot ? await session.screenshot(join53(artifactRoot, "android-emulator.png")) : undefined;
20503
+ const screenshot = reportRoot ? await session.screenshot(join54(artifactRoot, "android-emulator.png")) : undefined;
20088
20504
  await writeRequestedAndroidReport({
20089
20505
  adb,
20090
20506
  args,
@@ -20152,14 +20568,14 @@ Emulator setup verification:`);
20152
20568
  const port = Number(explicit);
20153
20569
  if (!Number.isInteger(port) || port < 1 || port > 65535)
20154
20570
  throw new TypeError("mobile test --port must be a valid TCP port.");
20155
- const instance2 = listLiveInstances().find((candidate) => resolve42(candidate.cwd) === resolve42(projectRoot) && candidate.source === "dev" && candidate.port === port);
20571
+ const instance2 = listLiveInstances().find((candidate) => resolve43(candidate.cwd) === resolve43(projectRoot) && candidate.source === "dev" && candidate.port === port);
20156
20572
  return {
20157
20573
  https: instance2?.https ?? args.includes("--https"),
20158
20574
  instance: instance2,
20159
20575
  port
20160
20576
  };
20161
20577
  }
20162
- const instances = listLiveInstances().filter((instance2) => resolve42(instance2.cwd) === resolve42(projectRoot) && instance2.source === "dev" && instance2.port !== null);
20578
+ const instances = listLiveInstances().filter((instance2) => resolve43(instance2.cwd) === resolve43(projectRoot) && instance2.source === "dev" && instance2.port !== null);
20163
20579
  if (instances.length !== 1)
20164
20580
  throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
20165
20581
  const [instance] = instances;
@@ -20214,7 +20630,7 @@ Emulator setup verification:`);
20214
20630
  if (!instance)
20215
20631
  return;
20216
20632
  const index = instance.command.indexOf("--ios-device");
20217
- return index === NOT_FOUND3 ? undefined : instance.command[index + 1];
20633
+ return index === NOT_FOUND4 ? undefined : instance.command[index + 1];
20218
20634
  }, physicalIosCapture = async (options) => {
20219
20635
  const remoteName = valueAfter(options.args, "--remote");
20220
20636
  if (remoteName && options.instance.iosRemoteMac && remoteName !== options.instance.iosRemoteMac)
@@ -20274,7 +20690,7 @@ Emulator setup verification:`);
20274
20690
  return result;
20275
20691
  }, writeIosFailureArtifacts = async (options) => {
20276
20692
  await mkdir14(options.artifactRoot, { recursive: true });
20277
- const screenshot = join53(options.artifactRoot, "ios-failure.png");
20693
+ const screenshot = join54(options.artifactRoot, "ios-failure.png");
20278
20694
  const screenshotResult = captureCommand4([
20279
20695
  options.xcrun,
20280
20696
  "simctl",
@@ -20283,7 +20699,7 @@ Emulator setup verification:`);
20283
20699
  "screenshot",
20284
20700
  screenshot
20285
20701
  ]);
20286
- const diagnosticPath = join53(options.artifactRoot, "ios-failure.json");
20702
+ const diagnosticPath = join54(options.artifactRoot, "ios-failure.json");
20287
20703
  await writeFile16(diagnosticPath, `${JSON.stringify({
20288
20704
  appId: options.appId,
20289
20705
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -20300,7 +20716,7 @@ Emulator setup verification:`);
20300
20716
  };
20301
20717
  }, nativeReportRoot = (args, projectRoot, platform6) => {
20302
20718
  const index = args.indexOf("--report");
20303
- if (index === NOT_FOUND3)
20719
+ if (index === NOT_FOUND4)
20304
20720
  return;
20305
20721
  const candidate = args[index + 1];
20306
20722
  const explicit = candidate?.startsWith("--") ? undefined : candidate;
@@ -20309,8 +20725,8 @@ Emulator setup verification:`);
20309
20725
  }, absolutejsVersionForReport = async () => {
20310
20726
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
20311
20727
  const versions = await Promise.all([
20312
- resolve42(import.meta.dir, "..", "..", "package.json"),
20313
- resolve42(import.meta.dir, "..", "..", "..", "package.json")
20728
+ resolve43(import.meta.dir, "..", "..", "package.json"),
20729
+ resolve43(import.meta.dir, "..", "..", "..", "package.json")
20314
20730
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
20315
20731
  for (const version2 of versions) {
20316
20732
  if (version2 === "unknown")
@@ -20529,7 +20945,7 @@ Emulator setup verification:`);
20529
20945
  ], "iOS app launch");
20530
20946
  await waitForIosHmrClient({ https, port, timeoutMs });
20531
20947
  await mkdir14(artifactRoot, { recursive: true });
20532
- const screenshot = join53(artifactRoot, "ios-simulator.png");
20948
+ const screenshot = join54(artifactRoot, "ios-simulator.png");
20533
20949
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
20534
20950
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
20535
20951
  const report = {
@@ -20740,10 +21156,10 @@ var exports_typecheck = {};
20740
21156
  __export(exports_typecheck, {
20741
21157
  typecheck: () => typecheck
20742
21158
  });
20743
- import { resolve as resolve43, join as join54 } from "path";
21159
+ import { resolve as resolve44, join as join55 } from "path";
20744
21160
  import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
20745
21161
  import { mkdir as mkdir15, writeFile as writeFile17 } from "fs/promises";
20746
- var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve43(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
21162
+ var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve44(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
20747
21163
  if (!existsSync42(resolveConfigPath(configPath2))) {
20748
21164
  const defaultService = {};
20749
21165
  return [defaultService];
@@ -20765,7 +21181,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
20765
21181
  const exitCode = await proc.exited;
20766
21182
  return { exitCode, name, output: (stdout + stderr).trim() };
20767
21183
  }, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
20768
- const local = resolve43("node_modules", ".bin", name);
21184
+ const local = resolve44("node_modules", ".bin", name);
20769
21185
  return existsSync42(local) ? local : null;
20770
21186
  }, ANSI_COLOR_REGEX, ANSI_PURPLE_REGEX, ANSI_CYAN_REGEX, ANSI_TOKEN_END_REGEX, stripAnsi4 = (str) => str.replace(ANSI_COLOR_REGEX, ""), formatSvelteOutput = (output) => {
20771
21187
  const cwd = `${process.cwd()}/`;
@@ -20813,15 +21229,15 @@ Found ${errorCount} error${suffix}.`;
20813
21229
  return formatted;
20814
21230
  }, ABSOLUTE_INTERNAL_EXCLUDES, resolveAbsoluteTypeFile = (fileName) => {
20815
21231
  const candidates = [
20816
- resolve43("node_modules/@absolutejs/absolute/dist/types", fileName),
20817
- resolve43(import.meta.dir, "../types", fileName),
20818
- resolve43(import.meta.dir, "../../types", fileName),
20819
- resolve43(import.meta.dir, "../../../types", fileName)
21232
+ resolve44("node_modules/@absolutejs/absolute/dist/types", fileName),
21233
+ resolve44(import.meta.dir, "../types", fileName),
21234
+ resolve44(import.meta.dir, "../../types", fileName),
21235
+ resolve44(import.meta.dir, "../../../types", fileName)
20820
21236
  ];
20821
21237
  return candidates.find((candidate) => existsSync42(candidate)) ?? candidates[0];
20822
21238
  }, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
20823
21239
  try {
20824
- return JSON.parse(readFileSync40(resolve43("tsconfig.json"), "utf-8"));
21240
+ return JSON.parse(readFileSync40(resolve44("tsconfig.json"), "utf-8"));
20825
21241
  } catch {
20826
21242
  return {};
20827
21243
  }
@@ -20849,27 +21265,27 @@ Found ${errorCount} error${suffix}.`;
20849
21265
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
20850
21266
  process.exit(1);
20851
21267
  }
20852
- const vueTsconfigPath = join54(cacheDir, "tsconfig.vue-check.json");
21268
+ const vueTsconfigPath = join55(cacheDir, "tsconfig.vue-check.json");
20853
21269
  await writeFile17(vueTsconfigPath, JSON.stringify({
20854
21270
  compilerOptions: {
20855
21271
  rootDir: ".."
20856
21272
  },
20857
21273
  exclude: getProjectTypecheckExcludes(),
20858
- extends: resolve43("tsconfig.json"),
21274
+ extends: resolve44("tsconfig.json"),
20859
21275
  include: getProjectTypecheckIncludes()
20860
21276
  }, null, "\t"));
20861
21277
  const base = [
20862
21278
  vueTscBin,
20863
21279
  "--noEmit",
20864
21280
  "--project",
20865
- resolve43(vueTsconfigPath),
21281
+ resolve44(vueTsconfigPath),
20866
21282
  "--pretty"
20867
21283
  ];
20868
21284
  const cached = await run("vue-tsc", [
20869
21285
  ...base,
20870
21286
  "--incremental",
20871
21287
  "--tsBuildInfoFile",
20872
- join54(cacheDir, "vue-tsc.tsbuildinfo")
21288
+ join55(cacheDir, "vue-tsc.tsbuildinfo")
20873
21289
  ]);
20874
21290
  if (cached.exitCode === 0 || cached.output.length > 0)
20875
21291
  return cached;
@@ -20880,7 +21296,7 @@ Found ${errorCount} error${suffix}.`;
20880
21296
  console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
20881
21297
  process.exit(1);
20882
21298
  }
20883
- const angularTsconfigPath = join54(cacheDir, "tsconfig.angular-check.json");
21299
+ const angularTsconfigPath = join55(cacheDir, "tsconfig.angular-check.json");
20884
21300
  await writeFile17(angularTsconfigPath, JSON.stringify({
20885
21301
  angularCompilerOptions: {
20886
21302
  strictTemplates: true
@@ -20890,32 +21306,32 @@ Found ${errorCount} error${suffix}.`;
20890
21306
  rootDir: ".."
20891
21307
  },
20892
21308
  exclude: ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
20893
- extends: resolve43("tsconfig.json"),
21309
+ extends: resolve44("tsconfig.json"),
20894
21310
  include: [`../${angularDir}/**/*`]
20895
21311
  }, null, "\t"));
20896
- return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve43(angularTsconfigPath))}`);
21312
+ return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve44(angularTsconfigPath))}`);
20897
21313
  }, buildTscCheck = (cacheDir) => {
20898
21314
  const tscBin = findBin("tsc");
20899
21315
  if (!tscBin) {
20900
21316
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
20901
21317
  process.exit(1);
20902
21318
  }
20903
- const tscConfigPath = join54(cacheDir, "tsconfig.typecheck.json");
21319
+ const tscConfigPath = join55(cacheDir, "tsconfig.typecheck.json");
20904
21320
  return writeFile17(tscConfigPath, JSON.stringify({
20905
21321
  compilerOptions: {
20906
21322
  rootDir: ".."
20907
21323
  },
20908
21324
  exclude: getProjectTypecheckExcludes(),
20909
- extends: resolve43("tsconfig.json"),
21325
+ extends: resolve44("tsconfig.json"),
20910
21326
  include: getProjectTypecheckIncludes()
20911
21327
  }, null, "\t")).then(() => run("tsc", [
20912
21328
  tscBin,
20913
21329
  "--noEmit",
20914
21330
  "--project",
20915
- resolve43(tscConfigPath),
21331
+ resolve44(tscConfigPath),
20916
21332
  "--incremental",
20917
21333
  "--tsBuildInfoFile",
20918
- join54(cacheDir, "tsc.tsbuildinfo"),
21334
+ join55(cacheDir, "tsc.tsbuildinfo"),
20919
21335
  "--pretty"
20920
21336
  ]));
20921
21337
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -20924,16 +21340,16 @@ Found ${errorCount} error${suffix}.`;
20924
21340
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
20925
21341
  process.exit(1);
20926
21342
  }
20927
- const svelteTsconfigPath = join54(cacheDir, "tsconfig.svelte-check.json");
21343
+ const svelteTsconfigPath = join55(cacheDir, "tsconfig.svelte-check.json");
20928
21344
  await writeFile17(svelteTsconfigPath, JSON.stringify({
20929
- extends: resolve43("tsconfig.json"),
21345
+ extends: resolve44("tsconfig.json"),
20930
21346
  files: ABSOLUTE_TYPECHECK_FILES,
20931
21347
  include: [`../${svelteDir}/**/*`]
20932
21348
  }, null, "\t"));
20933
21349
  return run("svelte-check", [
20934
21350
  svelteBin,
20935
21351
  "--tsconfig",
20936
- resolve43(svelteTsconfigPath),
21352
+ resolve44(svelteTsconfigPath),
20937
21353
  "--threshold",
20938
21354
  "error",
20939
21355
  "--compiler-warnings",
@@ -21127,11 +21543,11 @@ var DEFAULT_RELAY_PORT = 8787, DEFAULT_REQUEST_TIMEOUT_MS = 30000, headersToObje
21127
21543
  url: url.pathname + url.search,
21128
21544
  ...bodyBytes && bodyBytes.length > 0 ? { bodyBase64: Buffer.from(bodyBytes).toString("base64") } : {}
21129
21545
  };
21130
- const responsePromise = new Promise((resolve44) => {
21131
- pending.set(id, resolve44);
21546
+ const responsePromise = new Promise((resolve45) => {
21547
+ pending.set(id, resolve45);
21132
21548
  });
21133
21549
  client.send(encodeTunnelMessage(message));
21134
- const timeout = new Promise((resolve44) => setTimeout(() => resolve44({ id, message: "timeout", type: "error" }), requestTimeoutMs));
21550
+ const timeout = new Promise((resolve45) => setTimeout(() => resolve45({ id, message: "timeout", type: "error" }), requestTimeoutMs));
21135
21551
  const result = await Promise.race([responsePromise, timeout]);
21136
21552
  pending.delete(id);
21137
21553
  if (result.type === "error") {