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

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/README.md CHANGED
@@ -254,6 +254,13 @@ edit Swift/Kotlin, or maintain native bootstrap code. Type-only imports and test
254
254
  sources do not provision plugins. `absolute mobile doctor release` rejects a
255
255
  missing or mismatched plugin before release.
256
256
 
257
+ Run `absolute mobile inspect` from an application root for a read-only summary
258
+ of the effective mobile config, runtime package versions, discovered
259
+ capabilities/plugins, native-project state, embedded routes/frameworks, bundle
260
+ validation, and release projection. Add `--json` for a redacted CI or support
261
+ artifact; it omits credentials, device/account identifiers, absolute paths, and
262
+ detailed doctor messages. See [mobile project inspection](docs/MOBILE_INSPECT.md).
263
+
257
264
  `keyboard` provides portable visibility, CSS-pixel height, dismissal, and
258
265
  cleanup-safe change events. `systemBars` controls modern edge-to-edge status and
259
266
  navigation bar foreground appearance/visibility through Capacitor 8 core. Its
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-ISzk2S/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-6Q6jJy/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  var __require = import.meta.require;
3
3
 
4
- // .angular-partial-tmp-ISzk2S/src/core/streamingSlotRegistrar.ts
4
+ // .angular-partial-tmp-6Q6jJy/src/core/streamingSlotRegistrar.ts
5
5
  var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
6
6
  var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
7
7
  var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
48
48
  getWarningController()?.maybeWarn(primitiveName);
49
49
  };
50
50
 
51
- // .angular-partial-tmp-ISzk2S/src/core/streamingSlotRegistry.ts
51
+ // .angular-partial-tmp-6Q6jJy/src/core/streamingSlotRegistry.ts
52
52
  var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
53
53
  var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
54
54
  var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
package/dist/cli/index.js CHANGED
@@ -18996,16 +18996,270 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
18996
18996
  };
18997
18997
  var init_releasePublisher = () => {};
18998
18998
 
18999
+ // 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("\\", "/");
19004
+ return value || ".";
19005
+ }, pathExists7 = async (path) => {
19006
+ try {
19007
+ await access11(path);
19008
+ return true;
19009
+ } catch {
19010
+ return false;
19011
+ }
19012
+ }, readObject = async (path) => {
19013
+ const value = JSON.parse(await readFile20(path, "utf8"));
19014
+ if (!isObject2(value))
19015
+ throw new TypeError("JSON root must be an object.");
19016
+ return value;
19017
+ }, requireString = (value, field) => {
19018
+ if (typeof value !== "string" || value.length === 0)
19019
+ throw new TypeError(`${field} must be a non-empty string.`);
19020
+ return value;
19021
+ }, requireStringArray = (value, field) => {
19022
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
19023
+ throw new TypeError(`${field} must be a string array.`);
19024
+ 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);
19029
+ if (path === normalizedRoot || !path.startsWith(`${normalizedRoot}/`))
19030
+ throw new TypeError(`${field} must remain inside the mobile bundle.`);
19031
+ if (!(await stat4(path).catch(() => {
19032
+ return;
19033
+ }))?.isFile())
19034
+ throw new TypeError(`${field} does not exist in the mobile bundle.`);
19035
+ return portable;
19036
+ }, 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))
19040
+ return { manifest, status: "missing" };
19041
+ try {
19042
+ const value = await readObject(manifestPath);
19043
+ if (value.format !== 1)
19044
+ throw new TypeError("format is not supported by this runtime.");
19045
+ if (requireString(value.appId, "appId") !== config.appId)
19046
+ throw new TypeError("appId does not match the effective mobile config.");
19047
+ if (requireString(value.productionOrigin, "productionOrigin") !== config.productionOrigin)
19048
+ 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();
19052
+ if (!Array.isArray(value.pages) || !Array.isArray(value.routes))
19053
+ throw new TypeError("pages and routes must be arrays.");
19054
+ const pageIds = new Set;
19055
+ const frameworks7 = new Set;
19056
+ await Promise.all(value.pages.map(async (candidate) => {
19057
+ if (!isObject2(candidate))
19058
+ throw new TypeError("pages contains an invalid entry.");
19059
+ const pageId = requireString(candidate.pageId, "page.pageId");
19060
+ if (pageIds.has(pageId))
19061
+ throw new TypeError("page.pageId values must be unique.");
19062
+ pageIds.add(pageId);
19063
+ const framework = requireString(candidate.framework, "page.framework");
19064
+ if (!MOBILE_FRAMEWORKS.has(framework))
19065
+ throw new TypeError("page.framework is unsupported.");
19066
+ 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");
19071
+ if (candidate.localStylePath !== undefined)
19072
+ await requireBundleFile(config.bundleDirectory, candidate.localStylePath, "page.localStylePath");
19073
+ }));
19074
+ const routes = value.routes.map((candidate) => {
19075
+ if (!isObject2(candidate))
19076
+ throw new TypeError("routes contains an invalid entry.");
19077
+ const { method } = candidate;
19078
+ if (method !== "GET" && method !== "HEAD")
19079
+ throw new TypeError("route.method must be GET or HEAD.");
19080
+ const pageId = requireString(candidate.pageId, "route.pageId");
19081
+ if (!pageIds.has(pageId))
19082
+ throw new TypeError("route.pageId references a missing page.");
19083
+ return {
19084
+ method,
19085
+ pageId,
19086
+ pattern: requireString(candidate.pattern, "route.pattern")
19087
+ };
19088
+ });
19089
+ await Promise.all(["index.html", "absolute-mobile-bootstrap.js"].map((file) => requireBundleFile(config.bundleDirectory, file, file)));
19090
+ const entryPath = new URL(config.entry, "https://absolute.invalid").pathname;
19091
+ const entryResolved = resolveAbsoluteMobileRoute(routes, entryPath) !== undefined;
19092
+ if (!entryResolved)
19093
+ throw new TypeError("entry is not owned by an embedded route.");
19094
+ return {
19095
+ appBuild,
19096
+ auth: isObject2(value.auth),
19097
+ capabilities,
19098
+ entryResolved,
19099
+ frameworks: [...frameworks7].sort(),
19100
+ manifest,
19101
+ pageCount: value.pages.length,
19102
+ routeCount: value.routes.length,
19103
+ runtime,
19104
+ status: "valid",
19105
+ sync: isObject2(value.sync)
19106
+ };
19107
+ } catch (error) {
19108
+ return {
19109
+ issue: error instanceof Error ? error.message : "The embedded mobile manifest is invalid.",
19110
+ manifest,
19111
+ status: "invalid"
19112
+ };
19113
+ }
19114
+ }, addPackageDeclarations = (declarations, value) => {
19115
+ if (!isObject2(value))
19116
+ return;
19117
+ for (const [name, declared] of Object.entries(value).filter((entry) => typeof entry[1] === "string"))
19118
+ declarations.set(name, declared);
19119
+ }, packageInspections = async (projectRoot, additionalNames) => {
19120
+ const project = await readObject(join52(projectRoot, "package.json"));
19121
+ const declarations = new Map;
19122
+ for (const field of ["dependencies", "devDependencies"])
19123
+ addPackageDeclarations(declarations, project[field]);
19124
+ const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
19125
+ return Promise.all(names.map(async (name) => {
19126
+ const installedManifest = await readObject(join52(projectRoot, "node_modules", name, "package.json")).catch(() => {
19127
+ return;
19128
+ });
19129
+ const installed = installedManifest?.version;
19130
+ return {
19131
+ declared: declarations.get(name) ?? "transitive",
19132
+ ...typeof installed === "string" ? { installed } : {},
19133
+ name
19134
+ };
19135
+ }));
19136
+ }, inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) => {
19137
+ const bundle = await inspectBundle(config, projectRoot);
19138
+ let currentCapabilities = [];
19139
+ let capabilityIssue;
19140
+ let plugins = [];
19141
+ try {
19142
+ const plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot);
19143
+ currentCapabilities = plan.capabilities;
19144
+ plugins = plan.requiredPackages;
19145
+ } catch {
19146
+ capabilityIssue = "Native capability metadata could not be resolved.";
19147
+ }
19148
+ const pluginNames = plugins.map((spec) => spec.slice(0, spec.lastIndexOf("@")));
19149
+ const releaseInspection = await (options.inspectRelease ?? inspectAbsoluteMobileRelease)(config, projectRoot).catch(() => ({ checks: [], ready: false }));
19150
+ return {
19151
+ bundle,
19152
+ capabilities: {
19153
+ current: currentCapabilities,
19154
+ ...bundle.capabilities ? {
19155
+ embeddedMatchesCurrent: JSON.stringify(bundle.capabilities) === JSON.stringify(currentCapabilities)
19156
+ } : {},
19157
+ ...capabilityIssue ? { issue: capabilityIssue } : {},
19158
+ plugins
19159
+ },
19160
+ config: {
19161
+ appId: config.appId,
19162
+ appName: config.appName,
19163
+ bundleDirectory: portablePath(projectRoot, config.bundleDirectory),
19164
+ deepLinkHosts: config.deepLinkHosts,
19165
+ deepLinkScheme: config.deepLinkScheme,
19166
+ engine: config.engine,
19167
+ entry: config.entry,
19168
+ iosVersion: config.iosVersion,
19169
+ nativeProjectDirectory: portablePath(projectRoot, config.nativeProjectDirectory),
19170
+ platforms: config.platforms,
19171
+ productionOrigin: config.productionOrigin
19172
+ },
19173
+ format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
19174
+ nativeProjects: await Promise.all(config.platforms.map(async (platform6) => {
19175
+ const path = join52(config.nativeProjectDirectory, platform6);
19176
+ return {
19177
+ initialized: await pathExists7(path),
19178
+ path: portablePath(projectRoot, path),
19179
+ platform: platform6
19180
+ };
19181
+ })),
19182
+ packages: await packageInspections(projectRoot, pluginNames),
19183
+ release: {
19184
+ checks: releaseInspection.checks.map(({ id, status: status2 }) => ({
19185
+ id,
19186
+ status: status2
19187
+ })),
19188
+ ready: releaseInspection.ready
19189
+ },
19190
+ runtime: { absolutejs: options.absolutejsVersion ?? "unknown" }
19191
+ };
19192
+ }, yesNo = (value) => value ? "yes" : "no", renderAbsoluteMobileProjectInspection = (report) => {
19193
+ const lines = [
19194
+ `AbsoluteJS mobile inspection (format ${report.format})`,
19195
+ "",
19196
+ `App: ${report.config.appName} (${report.config.appId})`,
19197
+ `Engine: ${report.config.engine}`,
19198
+ `Platforms: ${report.config.platforms.join(", ")}`,
19199
+ `Entry: ${report.config.entry}`,
19200
+ `Production origin: ${report.config.productionOrigin}`,
19201
+ `AbsoluteJS: ${report.runtime.absolutejs}`,
19202
+ "",
19203
+ `Bundle: ${report.bundle.status} (${report.bundle.manifest})`
19204
+ ];
19205
+ if (report.bundle.status === "valid") {
19206
+ lines.push(` Build/runtime: ${report.bundle.appBuild} / ${report.bundle.runtime}`, ` Pages/routes: ${report.bundle.pageCount} / ${report.bundle.routeCount}`, ` Frameworks: ${report.bundle.frameworks?.join(", ") || "none"}`, ` Auth/Sync: ${yesNo(report.bundle.auth === true)} / ${yesNo(report.bundle.sync === true)}`);
19207
+ } else if (report.bundle.issue)
19208
+ lines.push(` Issue: ${report.bundle.issue}`);
19209
+ lines.push("", `Capabilities: ${report.capabilities.current.join(", ") || "none"}`, `Native plugins: ${report.capabilities.plugins.join(", ") || "none"}`);
19210
+ if (report.capabilities.embeddedMatchesCurrent !== undefined)
19211
+ lines.push(`Embedded capabilities current: ${yesNo(report.capabilities.embeddedMatchesCurrent)}`);
19212
+ if (report.capabilities.issue)
19213
+ lines.push(`Capability issue: ${report.capabilities.issue}`);
19214
+ lines.push("", "Native projects:");
19215
+ for (const project of report.nativeProjects)
19216
+ lines.push(` ${project.platform}: ${project.initialized ? "initialized" : "missing"} (${project.path})`);
19217
+ lines.push("", "Runtime packages:");
19218
+ for (const runtimePackage of report.packages)
19219
+ lines.push(` ${runtimePackage.name}: ${runtimePackage.installed ?? "not installed"} (declared ${runtimePackage.declared})`);
19220
+ const failed = report.release.checks.filter((check2) => check2.status === "fail").length;
19221
+ const warned = report.release.checks.filter((check2) => check2.status === "warn").length;
19222
+ lines.push("", `Release projection: ${report.release.ready ? "ready" : "not ready"} (${failed} failed, ${warned} warnings)`);
19223
+ return `${lines.join(`
19224
+ `)}
19225
+ `;
19226
+ };
19227
+ var init_mobileInspect = __esm(() => {
19228
+ init_deviceCapabilities();
19229
+ init_releaseDoctor();
19230
+ init_routeMatcher();
19231
+ MOBILE_PACKAGE_NAMES = new Set([
19232
+ "@absolutejs/absolute",
19233
+ "@absolutejs/auth",
19234
+ "@absolutejs/devices",
19235
+ "@absolutejs/devices-capacitor",
19236
+ "@absolutejs/http",
19237
+ "@absolutejs/pwa",
19238
+ "@absolutejs/sync",
19239
+ "@absolutejs/sync-capacitor",
19240
+ "@capacitor-community/sqlite"
19241
+ ]);
19242
+ MOBILE_FRAMEWORKS = new Set([
19243
+ "angular",
19244
+ "ember",
19245
+ "html",
19246
+ "htmx",
19247
+ "react",
19248
+ "svelte",
19249
+ "vue"
19250
+ ]);
19251
+ });
19252
+
18999
19253
  // src/cli/scripts/mobile.ts
19000
19254
  var exports_mobile = {};
19001
19255
  __export(exports_mobile, {
19002
19256
  runMobile: () => runMobile
19003
19257
  });
19004
- import { access as access11, mkdir as mkdir14, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
19005
- import { join as join52, resolve as resolve41 } from "path";
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";
19006
19260
  import { createInterface } from "readline/promises";
19007
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) => {
19008
- const manifest = JSON.parse(await readFile20(join52(projectRoot, "package.json"), "utf8"));
19262
+ const manifest = JSON.parse(await readFile21(join53(projectRoot, "package.json"), "utf8"));
19009
19263
  if (!isRecord15(manifest))
19010
19264
  throw new TypeError("Application package.json must contain an object.");
19011
19265
  const names = new Set;
@@ -19018,7 +19272,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19018
19272
  return names;
19019
19273
  }, resolvedPackageVersion = async (projectRoot, packageName) => {
19020
19274
  try {
19021
- const manifest = JSON.parse(await readFile20(join52(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
19275
+ const manifest = JSON.parse(await readFile21(join53(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
19022
19276
  return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
19023
19277
  } catch {
19024
19278
  return;
@@ -19059,9 +19313,9 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19059
19313
  }
19060
19314
  return value;
19061
19315
  }, capacitorExecutable = async (projectRoot) => {
19062
- const executable = join52(projectRoot, "node_modules", ".bin", "cap");
19316
+ const executable = join53(projectRoot, "node_modules", ".bin", "cap");
19063
19317
  try {
19064
- await access11(executable);
19318
+ await access12(executable);
19065
19319
  return executable;
19066
19320
  } catch {
19067
19321
  throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
@@ -19083,6 +19337,17 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19083
19337
  const config = await loadConfig(configPath2);
19084
19338
  const mobile = normalizeAbsoluteMobileConfig(requireMobileConfig(config.mobile), projectRoot);
19085
19339
  return { mobile, projectRoot };
19340
+ }, inspectMobile = async (args) => {
19341
+ const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
19342
+ const report = await inspectAbsoluteMobileProject(mobile, projectRoot, {
19343
+ absolutejsVersion: await absolutejsVersionForReport()
19344
+ });
19345
+ if (args.includes("--json")) {
19346
+ console.log(JSON.stringify(report, null, 2));
19347
+ return report;
19348
+ }
19349
+ console.log(renderAbsoluteMobileProjectInspection(report).trimEnd());
19350
+ return report;
19086
19351
  }, remoteProfilePath = () => process.env.ABSOLUTE_REMOTE_MAC_PROFILE_PATH || undefined, pairRemoteMac = async (args) => {
19087
19352
  if (args[0] !== "mac" || !args[1] || !args[2])
19088
19353
  throw new TypeError("Usage: absolute mobile pair mac <name> <user@host> [--port n] [--workspace path]");
@@ -19147,7 +19412,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19147
19412
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
19148
19413
  }, associations = async (args) => {
19149
19414
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
19150
- const outputDirectory = resolve41(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19415
+ const outputDirectory = resolve42(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19151
19416
  if (args.includes("--verify")) {
19152
19417
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
19153
19418
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -19380,7 +19645,7 @@ Mobile release transport checks failed.`);
19380
19645
  const durationMs = Math.round(performance.now() - startedAt);
19381
19646
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
19382
19647
  console.log(`Artifact: ${release.artifactPath}`);
19383
- console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
19648
+ console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
19384
19649
  return release;
19385
19650
  } finally {
19386
19651
  sendTelemetryEvent("mobile:android-release-build", {
@@ -19483,7 +19748,7 @@ Mobile release transport checks failed.`);
19483
19748
  const durationMs = Math.round(performance.now() - startedAt);
19484
19749
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
19485
19750
  console.log(`Artifact: ${release.artifactPath}`);
19486
- console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
19751
+ console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
19487
19752
  return release;
19488
19753
  } finally {
19489
19754
  sendTelemetryEvent("mobile:ios-release-build", {
@@ -19590,7 +19855,7 @@ Mobile release transport checks failed.`);
19590
19855
  checks.push({
19591
19856
  id: "sync.storage-schema",
19592
19857
  label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
19593
- path: join52(projectRoot, "package.json"),
19858
+ path: join53(projectRoot, "package.json"),
19594
19859
  platform: "host",
19595
19860
  status: "pass"
19596
19861
  });
@@ -19598,7 +19863,7 @@ Mobile release transport checks failed.`);
19598
19863
  checks.push({
19599
19864
  id: "sync.storage-schema",
19600
19865
  label: "Offline schema metadata is invalid",
19601
- path: join52(projectRoot, "package.json"),
19866
+ path: join53(projectRoot, "package.json"),
19602
19867
  platform: "host",
19603
19868
  remediation: error instanceof Error ? error.message : String(error),
19604
19869
  status: "fail"
@@ -19683,7 +19948,7 @@ Emulator setup verification:`);
19683
19948
  }
19684
19949
  return { https: args.includes("--https"), port };
19685
19950
  }
19686
- const instances = listLiveInstances().filter((instance2) => resolve41(instance2.cwd) === resolve41(projectRoot) && instance2.source === "dev" && instance2.port !== null);
19951
+ const instances = listLiveInstances().filter((instance2) => resolve42(instance2.cwd) === resolve42(projectRoot) && instance2.source === "dev" && instance2.port !== null);
19687
19952
  if (instances.length !== 1) {
19688
19953
  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>.");
19689
19954
  }
@@ -19726,8 +19991,8 @@ Emulator setup verification:`);
19726
19991
  }
19727
19992
  return selected;
19728
19993
  }, safeArtifactRoot = (projectRoot, value) => {
19729
- const root = resolve41(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
19730
- if (root !== projectRoot && !root.startsWith(`${resolve41(projectRoot)}/`)) {
19994
+ const root = resolve42(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
19995
+ if (root !== projectRoot && !root.startsWith(`${resolve42(projectRoot)}/`)) {
19731
19996
  throw new TypeError("mobile test --artifacts must remain inside the project.");
19732
19997
  }
19733
19998
  return root;
@@ -19752,10 +20017,10 @@ Emulator setup verification:`);
19752
20017
  });
19753
20018
  }, writeAndroidFailureArtifacts = async (options) => {
19754
20019
  await mkdir14(options.artifactRoot, { recursive: true });
19755
- const screenshot = options.session ? await options.session.screenshot(join52(options.artifactRoot, "android-failure.png")).catch(() => {
20020
+ const screenshot = options.session ? await options.session.screenshot(join53(options.artifactRoot, "android-failure.png")).catch(() => {
19756
20021
  return;
19757
20022
  }) : undefined;
19758
- const diagnosticPath = join52(options.artifactRoot, "android-failure.json");
20023
+ const diagnosticPath = join53(options.artifactRoot, "android-failure.json");
19759
20024
  await writeFile16(diagnosticPath, `${JSON.stringify({
19760
20025
  diagnostics: options.session?.diagnostics ?? [],
19761
20026
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -19819,7 +20084,7 @@ Emulator setup verification:`);
19819
20084
  console.log(JSON.stringify(report, null, 2));
19820
20085
  else
19821
20086
  printAndroidTestReport(report);
19822
- const screenshot = reportRoot ? await session.screenshot(join52(artifactRoot, "android-emulator.png")) : undefined;
20087
+ const screenshot = reportRoot ? await session.screenshot(join53(artifactRoot, "android-emulator.png")) : undefined;
19823
20088
  await writeRequestedAndroidReport({
19824
20089
  adb,
19825
20090
  args,
@@ -19887,14 +20152,14 @@ Emulator setup verification:`);
19887
20152
  const port = Number(explicit);
19888
20153
  if (!Number.isInteger(port) || port < 1 || port > 65535)
19889
20154
  throw new TypeError("mobile test --port must be a valid TCP port.");
19890
- const instance2 = listLiveInstances().find((candidate) => resolve41(candidate.cwd) === resolve41(projectRoot) && candidate.source === "dev" && candidate.port === port);
20155
+ const instance2 = listLiveInstances().find((candidate) => resolve42(candidate.cwd) === resolve42(projectRoot) && candidate.source === "dev" && candidate.port === port);
19891
20156
  return {
19892
20157
  https: instance2?.https ?? args.includes("--https"),
19893
20158
  instance: instance2,
19894
20159
  port
19895
20160
  };
19896
20161
  }
19897
- const instances = listLiveInstances().filter((instance2) => resolve41(instance2.cwd) === resolve41(projectRoot) && instance2.source === "dev" && instance2.port !== null);
20162
+ const instances = listLiveInstances().filter((instance2) => resolve42(instance2.cwd) === resolve42(projectRoot) && instance2.source === "dev" && instance2.port !== null);
19898
20163
  if (instances.length !== 1)
19899
20164
  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>.");
19900
20165
  const [instance] = instances;
@@ -20009,7 +20274,7 @@ Emulator setup verification:`);
20009
20274
  return result;
20010
20275
  }, writeIosFailureArtifacts = async (options) => {
20011
20276
  await mkdir14(options.artifactRoot, { recursive: true });
20012
- const screenshot = join52(options.artifactRoot, "ios-failure.png");
20277
+ const screenshot = join53(options.artifactRoot, "ios-failure.png");
20013
20278
  const screenshotResult = captureCommand4([
20014
20279
  options.xcrun,
20015
20280
  "simctl",
@@ -20018,7 +20283,7 @@ Emulator setup verification:`);
20018
20283
  "screenshot",
20019
20284
  screenshot
20020
20285
  ]);
20021
- const diagnosticPath = join52(options.artifactRoot, "ios-failure.json");
20286
+ const diagnosticPath = join53(options.artifactRoot, "ios-failure.json");
20022
20287
  await writeFile16(diagnosticPath, `${JSON.stringify({
20023
20288
  appId: options.appId,
20024
20289
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -20044,8 +20309,8 @@ Emulator setup verification:`);
20044
20309
  }, absolutejsVersionForReport = async () => {
20045
20310
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
20046
20311
  const versions = await Promise.all([
20047
- resolve41(import.meta.dir, "..", "..", "package.json"),
20048
- resolve41(import.meta.dir, "..", "..", "..", "package.json")
20312
+ resolve42(import.meta.dir, "..", "..", "package.json"),
20313
+ resolve42(import.meta.dir, "..", "..", "..", "package.json")
20049
20314
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
20050
20315
  for (const version2 of versions) {
20051
20316
  if (version2 === "unknown")
@@ -20264,7 +20529,7 @@ Emulator setup verification:`);
20264
20529
  ], "iOS app launch");
20265
20530
  await waitForIosHmrClient({ https, port, timeoutMs });
20266
20531
  await mkdir14(artifactRoot, { recursive: true });
20267
- const screenshot = join52(artifactRoot, "ios-simulator.png");
20532
+ const screenshot = join53(artifactRoot, "ios-simulator.png");
20268
20533
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
20269
20534
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
20270
20535
  const report = {
@@ -20377,6 +20642,10 @@ Emulator setup verification:`);
20377
20642
  await doctor(args.slice(1));
20378
20643
  return;
20379
20644
  }
20645
+ if (command === "inspect") {
20646
+ await inspectMobile(args.slice(1));
20647
+ return;
20648
+ }
20380
20649
  if (command === "test" && args[1] === "android") {
20381
20650
  await testAndroid(args.slice(2));
20382
20651
  return;
@@ -20401,7 +20670,7 @@ Emulator setup verification:`);
20401
20670
  await publishIos(args.slice(2));
20402
20671
  return;
20403
20672
  }
20404
- throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
20673
+ throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [--json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] | associations [--outdir dir] [--verify] | doctor [ios|android|release] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--outdir dir] [--web-outdir dir] [--unsigned] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--route path] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--serial id] [--artifacts dir] [--json] | test ios [--device identifier [--remote name] | --udid id] [--wait-for-hmr] [--report [dir]] [--timeout ms] [--port n] [--artifacts dir] [--json]> [--config path]");
20405
20674
  };
20406
20675
  var init_mobile = __esm(() => {
20407
20676
  init_dependencies();
@@ -20435,6 +20704,7 @@ var init_mobile = __esm(() => {
20435
20704
  init_nativeAuth();
20436
20705
  init_syncSchema();
20437
20706
  init_deviceCapabilities();
20707
+ init_mobileInspect();
20438
20708
  CAPACITOR_PACKAGES = [
20439
20709
  "@capacitor/core",
20440
20710
  "@capacitor/app",
@@ -20470,10 +20740,10 @@ var exports_typecheck = {};
20470
20740
  __export(exports_typecheck, {
20471
20741
  typecheck: () => typecheck
20472
20742
  });
20473
- import { resolve as resolve42, join as join53 } from "path";
20743
+ import { resolve as resolve43, join as join54 } from "path";
20474
20744
  import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
20475
20745
  import { mkdir as mkdir15, writeFile as writeFile17 } from "fs/promises";
20476
- var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve42(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
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) => {
20477
20747
  if (!existsSync42(resolveConfigPath(configPath2))) {
20478
20748
  const defaultService = {};
20479
20749
  return [defaultService];
@@ -20495,7 +20765,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
20495
20765
  const exitCode = await proc.exited;
20496
20766
  return { exitCode, name, output: (stdout + stderr).trim() };
20497
20767
  }, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
20498
- const local = resolve42("node_modules", ".bin", name);
20768
+ const local = resolve43("node_modules", ".bin", name);
20499
20769
  return existsSync42(local) ? local : null;
20500
20770
  }, ANSI_COLOR_REGEX, ANSI_PURPLE_REGEX, ANSI_CYAN_REGEX, ANSI_TOKEN_END_REGEX, stripAnsi4 = (str) => str.replace(ANSI_COLOR_REGEX, ""), formatSvelteOutput = (output) => {
20501
20771
  const cwd = `${process.cwd()}/`;
@@ -20543,15 +20813,15 @@ Found ${errorCount} error${suffix}.`;
20543
20813
  return formatted;
20544
20814
  }, ABSOLUTE_INTERNAL_EXCLUDES, resolveAbsoluteTypeFile = (fileName) => {
20545
20815
  const candidates = [
20546
- resolve42("node_modules/@absolutejs/absolute/dist/types", fileName),
20547
- resolve42(import.meta.dir, "../types", fileName),
20548
- resolve42(import.meta.dir, "../../types", fileName),
20549
- resolve42(import.meta.dir, "../../../types", fileName)
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)
20550
20820
  ];
20551
20821
  return candidates.find((candidate) => existsSync42(candidate)) ?? candidates[0];
20552
20822
  }, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
20553
20823
  try {
20554
- return JSON.parse(readFileSync40(resolve42("tsconfig.json"), "utf-8"));
20824
+ return JSON.parse(readFileSync40(resolve43("tsconfig.json"), "utf-8"));
20555
20825
  } catch {
20556
20826
  return {};
20557
20827
  }
@@ -20579,27 +20849,27 @@ Found ${errorCount} error${suffix}.`;
20579
20849
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
20580
20850
  process.exit(1);
20581
20851
  }
20582
- const vueTsconfigPath = join53(cacheDir, "tsconfig.vue-check.json");
20852
+ const vueTsconfigPath = join54(cacheDir, "tsconfig.vue-check.json");
20583
20853
  await writeFile17(vueTsconfigPath, JSON.stringify({
20584
20854
  compilerOptions: {
20585
20855
  rootDir: ".."
20586
20856
  },
20587
20857
  exclude: getProjectTypecheckExcludes(),
20588
- extends: resolve42("tsconfig.json"),
20858
+ extends: resolve43("tsconfig.json"),
20589
20859
  include: getProjectTypecheckIncludes()
20590
20860
  }, null, "\t"));
20591
20861
  const base = [
20592
20862
  vueTscBin,
20593
20863
  "--noEmit",
20594
20864
  "--project",
20595
- resolve42(vueTsconfigPath),
20865
+ resolve43(vueTsconfigPath),
20596
20866
  "--pretty"
20597
20867
  ];
20598
20868
  const cached = await run("vue-tsc", [
20599
20869
  ...base,
20600
20870
  "--incremental",
20601
20871
  "--tsBuildInfoFile",
20602
- join53(cacheDir, "vue-tsc.tsbuildinfo")
20872
+ join54(cacheDir, "vue-tsc.tsbuildinfo")
20603
20873
  ]);
20604
20874
  if (cached.exitCode === 0 || cached.output.length > 0)
20605
20875
  return cached;
@@ -20610,7 +20880,7 @@ Found ${errorCount} error${suffix}.`;
20610
20880
  console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
20611
20881
  process.exit(1);
20612
20882
  }
20613
- const angularTsconfigPath = join53(cacheDir, "tsconfig.angular-check.json");
20883
+ const angularTsconfigPath = join54(cacheDir, "tsconfig.angular-check.json");
20614
20884
  await writeFile17(angularTsconfigPath, JSON.stringify({
20615
20885
  angularCompilerOptions: {
20616
20886
  strictTemplates: true
@@ -20620,32 +20890,32 @@ Found ${errorCount} error${suffix}.`;
20620
20890
  rootDir: ".."
20621
20891
  },
20622
20892
  exclude: ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
20623
- extends: resolve42("tsconfig.json"),
20893
+ extends: resolve43("tsconfig.json"),
20624
20894
  include: [`../${angularDir}/**/*`]
20625
20895
  }, null, "\t"));
20626
- return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve42(angularTsconfigPath))}`);
20896
+ return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve43(angularTsconfigPath))}`);
20627
20897
  }, buildTscCheck = (cacheDir) => {
20628
20898
  const tscBin = findBin("tsc");
20629
20899
  if (!tscBin) {
20630
20900
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
20631
20901
  process.exit(1);
20632
20902
  }
20633
- const tscConfigPath = join53(cacheDir, "tsconfig.typecheck.json");
20903
+ const tscConfigPath = join54(cacheDir, "tsconfig.typecheck.json");
20634
20904
  return writeFile17(tscConfigPath, JSON.stringify({
20635
20905
  compilerOptions: {
20636
20906
  rootDir: ".."
20637
20907
  },
20638
20908
  exclude: getProjectTypecheckExcludes(),
20639
- extends: resolve42("tsconfig.json"),
20909
+ extends: resolve43("tsconfig.json"),
20640
20910
  include: getProjectTypecheckIncludes()
20641
20911
  }, null, "\t")).then(() => run("tsc", [
20642
20912
  tscBin,
20643
20913
  "--noEmit",
20644
20914
  "--project",
20645
- resolve42(tscConfigPath),
20915
+ resolve43(tscConfigPath),
20646
20916
  "--incremental",
20647
20917
  "--tsBuildInfoFile",
20648
- join53(cacheDir, "tsc.tsbuildinfo"),
20918
+ join54(cacheDir, "tsc.tsbuildinfo"),
20649
20919
  "--pretty"
20650
20920
  ]));
20651
20921
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -20654,16 +20924,16 @@ Found ${errorCount} error${suffix}.`;
20654
20924
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
20655
20925
  process.exit(1);
20656
20926
  }
20657
- const svelteTsconfigPath = join53(cacheDir, "tsconfig.svelte-check.json");
20927
+ const svelteTsconfigPath = join54(cacheDir, "tsconfig.svelte-check.json");
20658
20928
  await writeFile17(svelteTsconfigPath, JSON.stringify({
20659
- extends: resolve42("tsconfig.json"),
20929
+ extends: resolve43("tsconfig.json"),
20660
20930
  files: ABSOLUTE_TYPECHECK_FILES,
20661
20931
  include: [`../${svelteDir}/**/*`]
20662
20932
  }, null, "\t"));
20663
20933
  return run("svelte-check", [
20664
20934
  svelteBin,
20665
20935
  "--tsconfig",
20666
- resolve42(svelteTsconfigPath),
20936
+ resolve43(svelteTsconfigPath),
20667
20937
  "--threshold",
20668
20938
  "error",
20669
20939
  "--compiler-warnings",
@@ -20857,11 +21127,11 @@ var DEFAULT_RELAY_PORT = 8787, DEFAULT_REQUEST_TIMEOUT_MS = 30000, headersToObje
20857
21127
  url: url.pathname + url.search,
20858
21128
  ...bodyBytes && bodyBytes.length > 0 ? { bodyBase64: Buffer.from(bodyBytes).toString("base64") } : {}
20859
21129
  };
20860
- const responsePromise = new Promise((resolve43) => {
20861
- pending.set(id, resolve43);
21130
+ const responsePromise = new Promise((resolve44) => {
21131
+ pending.set(id, resolve44);
20862
21132
  });
20863
21133
  client.send(encodeTunnelMessage(message));
20864
- const timeout = new Promise((resolve43) => setTimeout(() => resolve43({ id, message: "timeout", type: "error" }), requestTimeoutMs));
21134
+ const timeout = new Promise((resolve44) => setTimeout(() => resolve44({ id, message: "timeout", type: "error" }), requestTimeoutMs));
20865
21135
  const result = await Promise.race([responsePromise, timeout]);
20866
21136
  pending.delete(id);
20867
21137
  if (result.type === "error") {
@@ -22472,6 +22742,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
22472
22742
  const serverEntryBasename = absServerEntry.slice(absServerEntry.lastIndexOf("/") + 1);
22473
22743
  const configBasename = configPath2 ? configPath2.slice(configPath2.lastIndexOf("/") + 1) : "absolute.config.ts";
22474
22744
  const ATOMIC_RECOVERY_WINDOW_MS = 1000;
22745
+ const ATOMIC_RECOVERY_DELAY_MS = 25;
22475
22746
  const recentlyHandled = new Map;
22476
22747
  const handleCandidate = (filename) => {
22477
22748
  if (filename.includes("/") || filename.includes("\\"))
@@ -22515,18 +22786,28 @@ var dev = async (serverEntry, configPath2, options = {}) => {
22515
22786
  handleCandidate(entry.name);
22516
22787
  }
22517
22788
  };
22789
+ let atomicRecoveryTimer;
22790
+ const scheduleAtomicRecovery = () => {
22791
+ if (atomicRecoveryTimer)
22792
+ clearTimeout(atomicRecoveryTimer);
22793
+ atomicRecoveryTimer = setTimeout(() => {
22794
+ atomicRecoveryTimer = undefined;
22795
+ recoveryScan();
22796
+ }, ATOMIC_RECOVERY_DELAY_MS);
22797
+ };
22518
22798
  const watcher = watch3(serverEntryDir, { recursive: false }, (event, filename) => {
22519
22799
  if (!filename)
22520
22800
  return;
22521
22801
  if (isAtomicWriteTemp(filename)) {
22522
- if (event === "rename") {
22523
- recoveryScan();
22524
- }
22802
+ if (event === "rename")
22803
+ scheduleAtomicRecovery();
22525
22804
  return;
22526
22805
  }
22527
22806
  handleCandidate(filename);
22528
22807
  });
22529
22808
  const closeWatcher = () => {
22809
+ if (atomicRecoveryTimer)
22810
+ clearTimeout(atomicRecoveryTimer);
22530
22811
  try {
22531
22812
  watcher.close();
22532
22813
  } catch {}
@@ -24763,7 +25044,7 @@ if (command === "dev") {
24763
25044
  console.error(" prepare [entry] [--outdir dir] Build production assets and server without launching");
24764
25045
  console.error(" start [entry] [--outdir dir] [--prebuilt] Start production server");
24765
25046
  console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
24766
- console.error(" mobile <init|sync|pair|remotes|doctor|test> Manage Capacitor projects, simulators, physical devices, Remote Macs, guided setup, and deep links");
25047
+ console.error(" mobile <init|sync|inspect|pair|remotes|doctor|test> Manage Capacitor projects, simulators, physical devices, Remote Macs, guided setup, and deep links");
24767
25048
  console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
24768
25049
  console.error(" db <backup|restore|seed> Backup/restore any Postgres DB (ORM-agnostic, upsert by PK) or run the seed script");
24769
25050
  console.error(" doctor [--fix] [--json] Diagnose the project (bun, type graph, config, framework dirs, env, port)");
@@ -2,6 +2,32 @@
2
2
  // src/dev/serverBootstrap.ts
3
3
  import { copyFileSync, existsSync, readdirSync, unlinkSync } from "fs";
4
4
  import { dirname, extname, join, resolve } from "path";
5
+
6
+ // src/dev/serverEntryCopies.ts
7
+ var ENTRY_COPY_OWNER_PATTERN = /^\.absolutejs-hmr-(\d+)-(?:bootstrap-)?\d+\.[^.]+$/;
8
+ var absoluteServerEntryCopyOwnerPid = (name) => {
9
+ const match = ENTRY_COPY_OWNER_PATTERN.exec(name);
10
+ if (!match)
11
+ return null;
12
+ const pid = Number(match[1]);
13
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
14
+ };
15
+ var isProcessAlive = (pid) => {
16
+ try {
17
+ process.kill(pid, 0);
18
+ return true;
19
+ } catch (error) {
20
+ return !(error instanceof Error && ("code" in error) && error.code === "ESRCH");
21
+ }
22
+ };
23
+ var isStaleAbsoluteServerEntryCopy = (name, currentPid = process.pid, ownerIsAlive = isProcessAlive) => {
24
+ const ownerPid = absoluteServerEntryCopyOwnerPid(name);
25
+ if (ownerPid === null)
26
+ return false;
27
+ return ownerPid === currentPid || !ownerIsAlive(ownerPid);
28
+ };
29
+
30
+ // src/dev/serverBootstrap.ts
5
31
  var originalEntry = process.env.ABSOLUTE_SERVER_ENTRY;
6
32
  if (!originalEntry) {
7
33
  throw new Error("ABSOLUTE_SERVER_ENTRY is required by the AbsoluteJS dev bootstrap");
@@ -24,6 +50,8 @@ if (!isHotReevaluation) {
24
50
  if (!name.startsWith(copyPrefix) || !name.endsWith(entryExtension)) {
25
51
  continue;
26
52
  }
53
+ if (!isStaleAbsoluteServerEntryCopy(name))
54
+ continue;
27
55
  removeEntryCopy(join(entryDir, name));
28
56
  }
29
57
  }
@@ -0,0 +1,3 @@
1
+ export declare const absoluteServerEntryCopyOwnerPid: (name: string) => number | null;
2
+ export declare const isProcessAlive: (pid: number) => boolean;
3
+ export declare const isStaleAbsoluteServerEntryCopy: (name: string, currentPid?: number, ownerIsAlive?: (pid: number) => boolean) => boolean;
@@ -0,0 +1,110 @@
1
+ import type { NormalizedAbsoluteMobileConfig } from './config';
2
+ import { inspectAbsoluteMobileRelease } from './releaseDoctor';
3
+ export declare const ABSOLUTE_MOBILE_INSPECTION_FORMAT: 1;
4
+ type MobilePackageInspection = {
5
+ declared: string;
6
+ installed?: string;
7
+ name: string;
8
+ };
9
+ type MobileBundleInspection = {
10
+ appBuild?: string;
11
+ auth?: boolean;
12
+ capabilities?: string[];
13
+ entryResolved?: boolean;
14
+ frameworks?: string[];
15
+ issue?: string;
16
+ manifest: string;
17
+ pageCount?: number;
18
+ routeCount?: number;
19
+ runtime?: string;
20
+ status: 'invalid' | 'missing' | 'valid';
21
+ sync?: boolean;
22
+ };
23
+ export type AbsoluteMobileProjectInspection = {
24
+ bundle: MobileBundleInspection;
25
+ capabilities: {
26
+ current: string[];
27
+ embeddedMatchesCurrent?: boolean;
28
+ issue?: string;
29
+ plugins: string[];
30
+ };
31
+ config: {
32
+ appId: string;
33
+ appName: string;
34
+ bundleDirectory: string;
35
+ deepLinkHosts: string[];
36
+ deepLinkScheme?: string;
37
+ engine: 'capacitor';
38
+ entry: string;
39
+ iosVersion?: string;
40
+ nativeProjectDirectory: string;
41
+ platforms: string[];
42
+ productionOrigin: string;
43
+ };
44
+ format: typeof ABSOLUTE_MOBILE_INSPECTION_FORMAT;
45
+ nativeProjects: Array<{
46
+ initialized: boolean;
47
+ path: string;
48
+ platform: string;
49
+ }>;
50
+ packages: MobilePackageInspection[];
51
+ release: {
52
+ checks: Array<{
53
+ id: string;
54
+ status: 'fail' | 'pass' | 'warn';
55
+ }>;
56
+ ready: boolean;
57
+ };
58
+ runtime: {
59
+ absolutejs: string;
60
+ };
61
+ };
62
+ export type InspectAbsoluteMobileProjectOptions = {
63
+ absolutejsVersion?: string;
64
+ inspectRelease?: typeof inspectAbsoluteMobileRelease;
65
+ };
66
+ export declare const inspectAbsoluteMobileProject: (config: NormalizedAbsoluteMobileConfig, projectRoot: string, options?: InspectAbsoluteMobileProjectOptions) => Promise<{
67
+ bundle: MobileBundleInspection;
68
+ capabilities: {
69
+ plugins: string[];
70
+ issue?: string | undefined;
71
+ embeddedMatchesCurrent?: boolean | undefined;
72
+ current: string[];
73
+ };
74
+ config: {
75
+ appId: string;
76
+ appName: string;
77
+ bundleDirectory: string;
78
+ deepLinkHosts: string[];
79
+ deepLinkScheme: string | undefined;
80
+ engine: "capacitor";
81
+ entry: string;
82
+ iosVersion: string | undefined;
83
+ nativeProjectDirectory: string;
84
+ platforms: import("..").MobilePlatform[];
85
+ productionOrigin: string;
86
+ };
87
+ format: 1;
88
+ nativeProjects: {
89
+ initialized: boolean;
90
+ path: string;
91
+ platform: import("..").MobilePlatform;
92
+ }[];
93
+ packages: {
94
+ name: string;
95
+ installed?: string | undefined;
96
+ declared: string;
97
+ }[];
98
+ release: {
99
+ checks: {
100
+ id: string;
101
+ status: "pass" | "fail";
102
+ }[];
103
+ ready: boolean;
104
+ };
105
+ runtime: {
106
+ absolutejs: string;
107
+ };
108
+ }>;
109
+ export declare const renderAbsoluteMobileProjectInspection: (report: AbsoluteMobileProjectInspection) => string;
110
+ export {};
package/package.json CHANGED
@@ -506,7 +506,7 @@
506
506
  ]
507
507
  }
508
508
  },
509
- "version": "0.20.0-beta.36",
509
+ "version": "0.20.0-beta.37",
510
510
  "workspaces": [
511
511
  "tests/fixtures/*",
512
512
  "tests/fixtures/_packages/*"