@absolutejs/absolute 0.20.0-beta.35 → 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/dist/cli/index.js CHANGED
@@ -2635,22 +2635,22 @@ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array
2635
2635
  stdout: ""
2636
2636
  };
2637
2637
  }
2638
- }, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root) => {
2638
+ }, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root, options = {}) => {
2639
2639
  const entries = await readdir2(current, { withFileTypes: true });
2640
2640
  const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
2641
2641
  const path = join9(current, entry.name);
2642
2642
  const projectRelative = relative3(root, path).replaceAll("\\", "/");
2643
- const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public");
2643
+ const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public" && options.includePublicBundle !== true);
2644
2644
  if (ignored)
2645
2645
  return [];
2646
2646
  if (entry.isDirectory())
2647
- return fingerprintFiles(root, path);
2647
+ return fingerprintFiles(root, path, options);
2648
2648
  return entry.isFile() ? [path] : [];
2649
2649
  }));
2650
2650
  return nested.flat();
2651
- }, fingerprintAbsoluteIosNativeProject = async (nativeDirectory) => {
2651
+ }, fingerprintAbsoluteIosNativeProject = async (nativeDirectory, options = {}) => {
2652
2652
  const hasher = createHash3("sha256");
2653
- const files = await fingerprintFiles(nativeDirectory);
2653
+ const files = await fingerprintFiles(nativeDirectory, nativeDirectory, options);
2654
2654
  const contents = await Promise.all(files.map((file) => readFile4(file)));
2655
2655
  files.forEach((file, index) => {
2656
2656
  hasher.update(relative3(nativeDirectory, file).replaceAll("\\", "/"));
@@ -3299,7 +3299,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3299
3299
  return;
3300
3300
  });
3301
3301
  }
3302
- }, fingerprintAbsoluteIosDevProject = async (project) => fingerprintAbsoluteIosNativeProject(project.nativeDirectory), simulatorInventory = (xcrun, capture) => {
3302
+ }, fingerprintAbsoluteIosDevProject = async (project, options = {}) => fingerprintAbsoluteIosNativeProject(project.nativeDirectory, options), simulatorInventory = (xcrun, capture) => {
3303
3303
  const result = capture([
3304
3304
  xcrun,
3305
3305
  "simctl",
@@ -3671,10 +3671,13 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3671
3671
  transition("syncing");
3672
3672
  await requireSuccess2([project.cap, "sync", "ios"], "Capacitor iOS synchronization", run, { cwd: project.projectRoot, signal: options.signal });
3673
3673
  transition("configuring");
3674
- await writeDevProjection(project, options.port, options.https === true, serverHost);
3674
+ if (options.embeddedBundle !== true)
3675
+ await writeDevProjection(project, options.port, options.https === true, serverHost);
3675
3676
  throwIfAborted2(options.signal);
3676
3677
  const fingerprintStartedAt = performance.now();
3677
- const fingerprintPromise = fingerprintAbsoluteIosDevProject(project).then((fingerprint2) => {
3678
+ const fingerprintPromise = fingerprintAbsoluteIosDevProject(project, {
3679
+ includePublicBundle: options.embeddedBundle === true
3680
+ }).then((fingerprint2) => {
3678
3681
  timings.fingerprinting = performance.now() - fingerprintStartedAt;
3679
3682
  return fingerprint2;
3680
3683
  });
@@ -3735,7 +3738,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
3735
3738
  await requireSuccess2(iosLaunchCommand(project, udid, deviceIdentifier !== undefined), "iOS app launch", run, { signal: options.signal });
3736
3739
  transition("ready");
3737
3740
  timings.total = performance.now() - startedAt;
3738
- log(`iOS ${targetKind} connected with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
3741
+ log(options.embeddedBundle === true ? `iOS ${targetKind} connected with the embedded bundle and backend on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).` : `iOS ${targetKind} connected with HMR on port ${options.port} in ${getDurationString(timings.total)} (${nativeCacheHit ? "native cache hit" : "native build installed"}).`);
3739
3742
  log(`iOS startup: ${timingSummary(timings)}.`);
3740
3743
  let closed = false;
3741
3744
  const close = async () => {
@@ -18993,16 +18996,270 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
18993
18996
  };
18994
18997
  var init_releasePublisher = () => {};
18995
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
+
18996
19253
  // src/cli/scripts/mobile.ts
18997
19254
  var exports_mobile = {};
18998
19255
  __export(exports_mobile, {
18999
19256
  runMobile: () => runMobile
19000
19257
  });
19001
- import { access as access11, mkdir as mkdir14, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
19002
- 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";
19003
19260
  import { createInterface } from "readline/promises";
19004
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) => {
19005
- const manifest = JSON.parse(await readFile20(join52(projectRoot, "package.json"), "utf8"));
19262
+ const manifest = JSON.parse(await readFile21(join53(projectRoot, "package.json"), "utf8"));
19006
19263
  if (!isRecord15(manifest))
19007
19264
  throw new TypeError("Application package.json must contain an object.");
19008
19265
  const names = new Set;
@@ -19015,7 +19272,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19015
19272
  return names;
19016
19273
  }, resolvedPackageVersion = async (projectRoot, packageName) => {
19017
19274
  try {
19018
- 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"));
19019
19276
  return isRecord15(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
19020
19277
  } catch {
19021
19278
  return;
@@ -19056,9 +19313,9 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19056
19313
  }
19057
19314
  return value;
19058
19315
  }, capacitorExecutable = async (projectRoot) => {
19059
- const executable = join52(projectRoot, "node_modules", ".bin", "cap");
19316
+ const executable = join53(projectRoot, "node_modules", ".bin", "cap");
19060
19317
  try {
19061
- await access11(executable);
19318
+ await access12(executable);
19062
19319
  return executable;
19063
19320
  } catch {
19064
19321
  throw new TypeError(`Capacitor is not installed in this app. Run: bun add ${CAPACITOR_PACKAGES.join(" ")}`);
@@ -19080,6 +19337,17 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19080
19337
  const config = await loadConfig(configPath2);
19081
19338
  const mobile = normalizeAbsoluteMobileConfig(requireMobileConfig(config.mobile), projectRoot);
19082
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;
19083
19351
  }, remoteProfilePath = () => process.env.ABSOLUTE_REMOTE_MAC_PROFILE_PATH || undefined, pairRemoteMac = async (args) => {
19084
19352
  if (args[0] !== "mac" || !args[1] || !args[2])
19085
19353
  throw new TypeError("Usage: absolute mobile pair mac <name> <user@host> [--port n] [--workspace path]");
@@ -19144,7 +19412,7 @@ var NOT_FOUND3 = -1, isRecord15 = (value) => typeof value === "object" && value
19144
19412
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
19145
19413
  }, associations = async (args) => {
19146
19414
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
19147
- const outputDirectory = resolve41(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19415
+ const outputDirectory = resolve42(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
19148
19416
  if (args.includes("--verify")) {
19149
19417
  const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
19150
19418
  console.log(`Verified ${result2.results.length} hosted association files`);
@@ -19377,7 +19645,7 @@ Mobile release transport checks failed.`);
19377
19645
  const durationMs = Math.round(performance.now() - startedAt);
19378
19646
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
19379
19647
  console.log(`Artifact: ${release.artifactPath}`);
19380
- console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
19648
+ console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
19381
19649
  return release;
19382
19650
  } finally {
19383
19651
  sendTelemetryEvent("mobile:android-release-build", {
@@ -19480,7 +19748,7 @@ Mobile release transport checks failed.`);
19480
19748
  const durationMs = Math.round(performance.now() - startedAt);
19481
19749
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
19482
19750
  console.log(`Artifact: ${release.artifactPath}`);
19483
- console.log(`Metadata: ${join52(release.releaseRoot, "release.json")}`);
19751
+ console.log(`Metadata: ${join53(release.releaseRoot, "release.json")}`);
19484
19752
  return release;
19485
19753
  } finally {
19486
19754
  sendTelemetryEvent("mobile:ios-release-build", {
@@ -19587,7 +19855,7 @@ Mobile release transport checks failed.`);
19587
19855
  checks.push({
19588
19856
  id: "sync.storage-schema",
19589
19857
  label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
19590
- path: join52(projectRoot, "package.json"),
19858
+ path: join53(projectRoot, "package.json"),
19591
19859
  platform: "host",
19592
19860
  status: "pass"
19593
19861
  });
@@ -19595,7 +19863,7 @@ Mobile release transport checks failed.`);
19595
19863
  checks.push({
19596
19864
  id: "sync.storage-schema",
19597
19865
  label: "Offline schema metadata is invalid",
19598
- path: join52(projectRoot, "package.json"),
19866
+ path: join53(projectRoot, "package.json"),
19599
19867
  platform: "host",
19600
19868
  remediation: error instanceof Error ? error.message : String(error),
19601
19869
  status: "fail"
@@ -19680,7 +19948,7 @@ Emulator setup verification:`);
19680
19948
  }
19681
19949
  return { https: args.includes("--https"), port };
19682
19950
  }
19683
- 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);
19684
19952
  if (instances.length !== 1) {
19685
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>.");
19686
19954
  }
@@ -19723,8 +19991,8 @@ Emulator setup verification:`);
19723
19991
  }
19724
19992
  return selected;
19725
19993
  }, safeArtifactRoot = (projectRoot, value) => {
19726
- const root = resolve41(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
19727
- 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)}/`)) {
19728
19996
  throw new TypeError("mobile test --artifacts must remain inside the project.");
19729
19997
  }
19730
19998
  return root;
@@ -19749,10 +20017,10 @@ Emulator setup verification:`);
19749
20017
  });
19750
20018
  }, writeAndroidFailureArtifacts = async (options) => {
19751
20019
  await mkdir14(options.artifactRoot, { recursive: true });
19752
- 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(() => {
19753
20021
  return;
19754
20022
  }) : undefined;
19755
- const diagnosticPath = join52(options.artifactRoot, "android-failure.json");
20023
+ const diagnosticPath = join53(options.artifactRoot, "android-failure.json");
19756
20024
  await writeFile16(diagnosticPath, `${JSON.stringify({
19757
20025
  diagnostics: options.session?.diagnostics ?? [],
19758
20026
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -19816,7 +20084,7 @@ Emulator setup verification:`);
19816
20084
  console.log(JSON.stringify(report, null, 2));
19817
20085
  else
19818
20086
  printAndroidTestReport(report);
19819
- 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;
19820
20088
  await writeRequestedAndroidReport({
19821
20089
  adb,
19822
20090
  args,
@@ -19884,14 +20152,14 @@ Emulator setup verification:`);
19884
20152
  const port = Number(explicit);
19885
20153
  if (!Number.isInteger(port) || port < 1 || port > 65535)
19886
20154
  throw new TypeError("mobile test --port must be a valid TCP port.");
19887
- 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);
19888
20156
  return {
19889
20157
  https: instance2?.https ?? args.includes("--https"),
19890
20158
  instance: instance2,
19891
20159
  port
19892
20160
  };
19893
20161
  }
19894
- 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);
19895
20163
  if (instances.length !== 1)
19896
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>.");
19897
20165
  const [instance] = instances;
@@ -20006,7 +20274,7 @@ Emulator setup verification:`);
20006
20274
  return result;
20007
20275
  }, writeIosFailureArtifacts = async (options) => {
20008
20276
  await mkdir14(options.artifactRoot, { recursive: true });
20009
- const screenshot = join52(options.artifactRoot, "ios-failure.png");
20277
+ const screenshot = join53(options.artifactRoot, "ios-failure.png");
20010
20278
  const screenshotResult = captureCommand4([
20011
20279
  options.xcrun,
20012
20280
  "simctl",
@@ -20015,7 +20283,7 @@ Emulator setup verification:`);
20015
20283
  "screenshot",
20016
20284
  screenshot
20017
20285
  ]);
20018
- const diagnosticPath = join52(options.artifactRoot, "ios-failure.json");
20286
+ const diagnosticPath = join53(options.artifactRoot, "ios-failure.json");
20019
20287
  await writeFile16(diagnosticPath, `${JSON.stringify({
20020
20288
  appId: options.appId,
20021
20289
  error: options.error instanceof Error ? options.error.message : String(options.error),
@@ -20041,8 +20309,8 @@ Emulator setup verification:`);
20041
20309
  }, absolutejsVersionForReport = async () => {
20042
20310
  let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
20043
20311
  const versions = await Promise.all([
20044
- resolve41(import.meta.dir, "..", "..", "package.json"),
20045
- resolve41(import.meta.dir, "..", "..", "..", "package.json")
20312
+ resolve42(import.meta.dir, "..", "..", "package.json"),
20313
+ resolve42(import.meta.dir, "..", "..", "..", "package.json")
20046
20314
  ].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
20047
20315
  for (const version2 of versions) {
20048
20316
  if (version2 === "unknown")
@@ -20261,7 +20529,7 @@ Emulator setup verification:`);
20261
20529
  ], "iOS app launch");
20262
20530
  await waitForIosHmrClient({ https, port, timeoutMs });
20263
20531
  await mkdir14(artifactRoot, { recursive: true });
20264
- const screenshot = join52(artifactRoot, "ios-simulator.png");
20532
+ const screenshot = join53(artifactRoot, "ios-simulator.png");
20265
20533
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
20266
20534
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
20267
20535
  const report = {
@@ -20374,6 +20642,10 @@ Emulator setup verification:`);
20374
20642
  await doctor(args.slice(1));
20375
20643
  return;
20376
20644
  }
20645
+ if (command === "inspect") {
20646
+ await inspectMobile(args.slice(1));
20647
+ return;
20648
+ }
20377
20649
  if (command === "test" && args[1] === "android") {
20378
20650
  await testAndroid(args.slice(2));
20379
20651
  return;
@@ -20398,7 +20670,7 @@ Emulator setup verification:`);
20398
20670
  await publishIos(args.slice(2));
20399
20671
  return;
20400
20672
  }
20401
- 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]");
20402
20674
  };
20403
20675
  var init_mobile = __esm(() => {
20404
20676
  init_dependencies();
@@ -20432,6 +20704,7 @@ var init_mobile = __esm(() => {
20432
20704
  init_nativeAuth();
20433
20705
  init_syncSchema();
20434
20706
  init_deviceCapabilities();
20707
+ init_mobileInspect();
20435
20708
  CAPACITOR_PACKAGES = [
20436
20709
  "@capacitor/core",
20437
20710
  "@capacitor/app",
@@ -20467,10 +20740,10 @@ var exports_typecheck = {};
20467
20740
  __export(exports_typecheck, {
20468
20741
  typecheck: () => typecheck
20469
20742
  });
20470
- import { resolve as resolve42, join as join53 } from "path";
20743
+ import { resolve as resolve43, join as join54 } from "path";
20471
20744
  import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
20472
20745
  import { mkdir as mkdir15, writeFile as writeFile17 } from "fs/promises";
20473
- 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) => {
20474
20747
  if (!existsSync42(resolveConfigPath(configPath2))) {
20475
20748
  const defaultService = {};
20476
20749
  return [defaultService];
@@ -20492,7 +20765,7 @@ var isCommandService3 = (service) => service.kind === "command" || Array.isArray
20492
20765
  const exitCode = await proc.exited;
20493
20766
  return { exitCode, name, output: (stdout + stderr).trim() };
20494
20767
  }, shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`, runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]), findBin = (name) => {
20495
- const local = resolve42("node_modules", ".bin", name);
20768
+ const local = resolve43("node_modules", ".bin", name);
20496
20769
  return existsSync42(local) ? local : null;
20497
20770
  }, ANSI_COLOR_REGEX, ANSI_PURPLE_REGEX, ANSI_CYAN_REGEX, ANSI_TOKEN_END_REGEX, stripAnsi4 = (str) => str.replace(ANSI_COLOR_REGEX, ""), formatSvelteOutput = (output) => {
20498
20771
  const cwd = `${process.cwd()}/`;
@@ -20540,15 +20813,15 @@ Found ${errorCount} error${suffix}.`;
20540
20813
  return formatted;
20541
20814
  }, ABSOLUTE_INTERNAL_EXCLUDES, resolveAbsoluteTypeFile = (fileName) => {
20542
20815
  const candidates = [
20543
- resolve42("node_modules/@absolutejs/absolute/dist/types", fileName),
20544
- resolve42(import.meta.dir, "../types", fileName),
20545
- resolve42(import.meta.dir, "../../types", fileName),
20546
- 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)
20547
20820
  ];
20548
20821
  return candidates.find((candidate) => existsSync42(candidate)) ?? candidates[0];
20549
20822
  }, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
20550
20823
  try {
20551
- return JSON.parse(readFileSync40(resolve42("tsconfig.json"), "utf-8"));
20824
+ return JSON.parse(readFileSync40(resolve43("tsconfig.json"), "utf-8"));
20552
20825
  } catch {
20553
20826
  return {};
20554
20827
  }
@@ -20576,27 +20849,27 @@ Found ${errorCount} error${suffix}.`;
20576
20849
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
20577
20850
  process.exit(1);
20578
20851
  }
20579
- const vueTsconfigPath = join53(cacheDir, "tsconfig.vue-check.json");
20852
+ const vueTsconfigPath = join54(cacheDir, "tsconfig.vue-check.json");
20580
20853
  await writeFile17(vueTsconfigPath, JSON.stringify({
20581
20854
  compilerOptions: {
20582
20855
  rootDir: ".."
20583
20856
  },
20584
20857
  exclude: getProjectTypecheckExcludes(),
20585
- extends: resolve42("tsconfig.json"),
20858
+ extends: resolve43("tsconfig.json"),
20586
20859
  include: getProjectTypecheckIncludes()
20587
20860
  }, null, "\t"));
20588
20861
  const base = [
20589
20862
  vueTscBin,
20590
20863
  "--noEmit",
20591
20864
  "--project",
20592
- resolve42(vueTsconfigPath),
20865
+ resolve43(vueTsconfigPath),
20593
20866
  "--pretty"
20594
20867
  ];
20595
20868
  const cached = await run("vue-tsc", [
20596
20869
  ...base,
20597
20870
  "--incremental",
20598
20871
  "--tsBuildInfoFile",
20599
- join53(cacheDir, "vue-tsc.tsbuildinfo")
20872
+ join54(cacheDir, "vue-tsc.tsbuildinfo")
20600
20873
  ]);
20601
20874
  if (cached.exitCode === 0 || cached.output.length > 0)
20602
20875
  return cached;
@@ -20607,7 +20880,7 @@ Found ${errorCount} error${suffix}.`;
20607
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");
20608
20881
  process.exit(1);
20609
20882
  }
20610
- const angularTsconfigPath = join53(cacheDir, "tsconfig.angular-check.json");
20883
+ const angularTsconfigPath = join54(cacheDir, "tsconfig.angular-check.json");
20611
20884
  await writeFile17(angularTsconfigPath, JSON.stringify({
20612
20885
  angularCompilerOptions: {
20613
20886
  strictTemplates: true
@@ -20617,32 +20890,32 @@ Found ${errorCount} error${suffix}.`;
20617
20890
  rootDir: ".."
20618
20891
  },
20619
20892
  exclude: ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
20620
- extends: resolve42("tsconfig.json"),
20893
+ extends: resolve43("tsconfig.json"),
20621
20894
  include: [`../${angularDir}/**/*`]
20622
20895
  }, null, "\t"));
20623
- return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve42(angularTsconfigPath))}`);
20896
+ return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve43(angularTsconfigPath))}`);
20624
20897
  }, buildTscCheck = (cacheDir) => {
20625
20898
  const tscBin = findBin("tsc");
20626
20899
  if (!tscBin) {
20627
20900
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
20628
20901
  process.exit(1);
20629
20902
  }
20630
- const tscConfigPath = join53(cacheDir, "tsconfig.typecheck.json");
20903
+ const tscConfigPath = join54(cacheDir, "tsconfig.typecheck.json");
20631
20904
  return writeFile17(tscConfigPath, JSON.stringify({
20632
20905
  compilerOptions: {
20633
20906
  rootDir: ".."
20634
20907
  },
20635
20908
  exclude: getProjectTypecheckExcludes(),
20636
- extends: resolve42("tsconfig.json"),
20909
+ extends: resolve43("tsconfig.json"),
20637
20910
  include: getProjectTypecheckIncludes()
20638
20911
  }, null, "\t")).then(() => run("tsc", [
20639
20912
  tscBin,
20640
20913
  "--noEmit",
20641
20914
  "--project",
20642
- resolve42(tscConfigPath),
20915
+ resolve43(tscConfigPath),
20643
20916
  "--incremental",
20644
20917
  "--tsBuildInfoFile",
20645
- join53(cacheDir, "tsc.tsbuildinfo"),
20918
+ join54(cacheDir, "tsc.tsbuildinfo"),
20646
20919
  "--pretty"
20647
20920
  ]));
20648
20921
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -20651,16 +20924,16 @@ Found ${errorCount} error${suffix}.`;
20651
20924
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
20652
20925
  process.exit(1);
20653
20926
  }
20654
- const svelteTsconfigPath = join53(cacheDir, "tsconfig.svelte-check.json");
20927
+ const svelteTsconfigPath = join54(cacheDir, "tsconfig.svelte-check.json");
20655
20928
  await writeFile17(svelteTsconfigPath, JSON.stringify({
20656
- extends: resolve42("tsconfig.json"),
20929
+ extends: resolve43("tsconfig.json"),
20657
20930
  files: ABSOLUTE_TYPECHECK_FILES,
20658
20931
  include: [`../${svelteDir}/**/*`]
20659
20932
  }, null, "\t"));
20660
20933
  return run("svelte-check", [
20661
20934
  svelteBin,
20662
20935
  "--tsconfig",
20663
- resolve42(svelteTsconfigPath),
20936
+ resolve43(svelteTsconfigPath),
20664
20937
  "--threshold",
20665
20938
  "error",
20666
20939
  "--compiler-warnings",
@@ -20854,11 +21127,11 @@ var DEFAULT_RELAY_PORT = 8787, DEFAULT_REQUEST_TIMEOUT_MS = 30000, headersToObje
20854
21127
  url: url.pathname + url.search,
20855
21128
  ...bodyBytes && bodyBytes.length > 0 ? { bodyBase64: Buffer.from(bodyBytes).toString("base64") } : {}
20856
21129
  };
20857
- const responsePromise = new Promise((resolve43) => {
20858
- pending.set(id, resolve43);
21130
+ const responsePromise = new Promise((resolve44) => {
21131
+ pending.set(id, resolve44);
20859
21132
  });
20860
21133
  client.send(encodeTunnelMessage(message));
20861
- 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));
20862
21135
  const result = await Promise.race([responsePromise, timeout]);
20863
21136
  pending.delete(id);
20864
21137
  if (result.type === "error") {
@@ -22469,6 +22742,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
22469
22742
  const serverEntryBasename = absServerEntry.slice(absServerEntry.lastIndexOf("/") + 1);
22470
22743
  const configBasename = configPath2 ? configPath2.slice(configPath2.lastIndexOf("/") + 1) : "absolute.config.ts";
22471
22744
  const ATOMIC_RECOVERY_WINDOW_MS = 1000;
22745
+ const ATOMIC_RECOVERY_DELAY_MS = 25;
22472
22746
  const recentlyHandled = new Map;
22473
22747
  const handleCandidate = (filename) => {
22474
22748
  if (filename.includes("/") || filename.includes("\\"))
@@ -22512,18 +22786,28 @@ var dev = async (serverEntry, configPath2, options = {}) => {
22512
22786
  handleCandidate(entry.name);
22513
22787
  }
22514
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
+ };
22515
22798
  const watcher = watch3(serverEntryDir, { recursive: false }, (event, filename) => {
22516
22799
  if (!filename)
22517
22800
  return;
22518
22801
  if (isAtomicWriteTemp(filename)) {
22519
- if (event === "rename") {
22520
- recoveryScan();
22521
- }
22802
+ if (event === "rename")
22803
+ scheduleAtomicRecovery();
22522
22804
  return;
22523
22805
  }
22524
22806
  handleCandidate(filename);
22525
22807
  });
22526
22808
  const closeWatcher = () => {
22809
+ if (atomicRecoveryTimer)
22810
+ clearTimeout(atomicRecoveryTimer);
22527
22811
  try {
22528
22812
  watcher.close();
22529
22813
  } catch {}
@@ -24760,7 +25044,7 @@ if (command === "dev") {
24760
25044
  console.error(" prepare [entry] [--outdir dir] Build production assets and server without launching");
24761
25045
  console.error(" start [entry] [--outdir dir] [--prebuilt] Start production server");
24762
25046
  console.error(" compile [entry] [--outdir dir] [--outfile path] Compile standalone executable");
24763
- 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");
24764
25048
  console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
24765
25049
  console.error(" db <backup|restore|seed> Backup/restore any Postgres DB (ORM-agnostic, upsert by PK) or run the seed script");
24766
25050
  console.error(" doctor [--fix] [--json] Diagnose the project (bun, type graph, config, framework dirs, env, port)");