@absolutejs/absolute 0.20.0-beta.66 → 0.20.0-beta.68

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
@@ -1939,6 +1939,426 @@ var init_updateRuntime = __esm(() => {
1939
1939
  init_syncSchema();
1940
1940
  });
1941
1941
 
1942
+ // src/mobile/expoNativeObservability.ts
1943
+ import { join as join8 } from "path";
1944
+ var HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
1945
+ `, moduleConfig, podspec, iosModule, androidBuild, androidManifest, androidModule, expoNativeObservabilityRuntimeSource, absoluteExpoNativeObservabilityFiles = (project) => new Map([
1946
+ [
1947
+ join8(project, "modules/absolute-mobile-observability/expo-module.config.json"),
1948
+ moduleConfig
1949
+ ],
1950
+ [
1951
+ join8(project, "modules/absolute-mobile-observability/ios/AbsoluteMobileObservability.podspec"),
1952
+ podspec
1953
+ ],
1954
+ [
1955
+ join8(project, "modules/absolute-mobile-observability/ios/AbsoluteMobileObservabilityModule.swift"),
1956
+ iosModule
1957
+ ],
1958
+ [
1959
+ join8(project, "modules/absolute-mobile-observability/android/build.gradle"),
1960
+ androidBuild
1961
+ ],
1962
+ [
1963
+ join8(project, "modules/absolute-mobile-observability/android/src/main/AndroidManifest.xml"),
1964
+ androidManifest
1965
+ ],
1966
+ [
1967
+ join8(project, "modules/absolute-mobile-observability/android/src/main/java/expo/modules/absolutemobileobservability/AbsoluteMobileObservabilityModule.kt"),
1968
+ androidModule
1969
+ ],
1970
+ [
1971
+ join8(project, "src/generated/AbsoluteNativeObservability.ts"),
1972
+ expoNativeObservabilityRuntimeSource
1973
+ ]
1974
+ ]), absoluteExpoNativeObservabilityPaths = (project) => [
1975
+ ...absoluteExpoNativeObservabilityFiles(project).keys()
1976
+ ];
1977
+ var init_expoNativeObservability = __esm(() => {
1978
+ moduleConfig = `${JSON.stringify({
1979
+ android: {
1980
+ modules: [
1981
+ "expo.modules.absolutemobileobservability.AbsoluteMobileObservabilityModule"
1982
+ ]
1983
+ },
1984
+ apple: { modules: ["AbsoluteMobileObservabilityModule"] },
1985
+ platforms: ["apple", "android"]
1986
+ }, null, 2)}
1987
+ `;
1988
+ podspec = `${HEADER}Pod::Spec.new do |s|
1989
+ s.name = 'AbsoluteMobileObservability'
1990
+ s.version = '0.0.1'
1991
+ s.summary = 'AbsoluteJS provider-neutral native process diagnostics'
1992
+ s.description = 'Generated MetricKit bridge for an AbsoluteJS Expo application.'
1993
+ s.author = 'AbsoluteJS'
1994
+ s.homepage = 'https://absolutejs.com'
1995
+ s.platforms = { :ios => '16.4' }
1996
+ s.source = { git: '' }
1997
+ s.static_framework = true
1998
+ s.dependency 'ExpoModulesCore'
1999
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
2000
+ s.source_files = '**/*.{h,m,mm,swift,hpp,cpp}'
2001
+ end
2002
+ `;
2003
+ iosModule = `${HEADER}import ExpoModulesCore
2004
+ import MetricKit
2005
+
2006
+ public final class AbsoluteMobileObservabilityModule: Module, MXMetricManagerSubscriber {
2007
+ private static let maximumReports = 8
2008
+ private static let maximumReportBytes = 64 * 1024
2009
+ private static let queue = DispatchQueue(label: "dev.absolute.expo-mobile-observability")
2010
+
2011
+ public func definition() -> ModuleDefinition {
2012
+ Name("AbsoluteMobileObservability")
2013
+
2014
+ OnCreate { MXMetricManager.shared.add(self) }
2015
+ OnDestroy { MXMetricManager.shared.remove(self) }
2016
+
2017
+ AsyncFunction("pending") { () -> [String: Any] in
2018
+ Self.queue.sync {
2019
+ let reports: [[String: Any]] = Self.files().compactMap { file in
2020
+ guard let data = try? Data(contentsOf: file),
2021
+ let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil }
2022
+ return value
2023
+ }
2024
+ return ["reports": reports]
2025
+ }
2026
+ }
2027
+
2028
+ AsyncFunction("acknowledge") { (options: [String: Any]) in
2029
+ let accepted = Set(options["ids"] as? [String] ?? [])
2030
+ Self.queue.sync {
2031
+ for file in Self.files() {
2032
+ guard let data = try? Data(contentsOf: file),
2033
+ let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
2034
+ let id = value["id"] as? String,
2035
+ accepted.contains(id) else { continue }
2036
+ try? FileManager.default.removeItem(at: file)
2037
+ }
2038
+ }
2039
+ }
2040
+ }
2041
+
2042
+ private static func root() -> URL? {
2043
+ guard let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return nil }
2044
+ let root = support.appendingPathComponent("AbsoluteMobileObservability", isDirectory: true)
2045
+ try? FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
2046
+ return root
2047
+ }
2048
+
2049
+ private static func files() -> [URL] {
2050
+ guard let root = root() else { return [] }
2051
+ return ((try? FileManager.default.contentsOfDirectory(at: root, includingPropertiesForKeys: nil)) ?? [])
2052
+ .filter { $0.pathExtension == "json" }
2053
+ .sorted { $0.lastPathComponent < $1.lastPathComponent }
2054
+ }
2055
+
2056
+ private static func enqueue(kind: String, occurredAt: Double, details: [AnyHashable: Any]) {
2057
+ guard let root = root() else { return }
2058
+ let id = UUID().uuidString.lowercased()
2059
+ var report: [String: Any] = [
2060
+ "details": Dictionary(uniqueKeysWithValues: details.map { (String(describing: $0.key), $0.value) }),
2061
+ "id": id,
2062
+ "kind": kind,
2063
+ "occurredAt": occurredAt,
2064
+ "platform": "ios"
2065
+ ]
2066
+ guard JSONSerialization.isValidJSONObject(report),
2067
+ let original = try? JSONSerialization.data(withJSONObject: report) else { return }
2068
+ if original.count > maximumReportBytes {
2069
+ report["details"] = [
2070
+ "diagnosticPrefix": String(decoding: original.prefix(24 * 1024), as: UTF8.self),
2071
+ "originalBytes": original.count,
2072
+ "truncated": true
2073
+ ]
2074
+ }
2075
+ guard let data = try? JSONSerialization.data(withJSONObject: report),
2076
+ data.count <= maximumReportBytes else { return }
2077
+ let timestamp = String(format: "%013.0f", occurredAt)
2078
+ try? data.write(to: root.appendingPathComponent("\\(timestamp)-\\(id).json"), options: .atomic)
2079
+ let overflow = max(0, files().count - maximumReports)
2080
+ for file in files().prefix(overflow) { try? FileManager.default.removeItem(at: file) }
2081
+ }
2082
+
2083
+ public func didReceive(_ payloads: [MXDiagnosticPayload]) {
2084
+ Self.queue.async {
2085
+ for payload in payloads {
2086
+ let at = payload.timeStampEnd.timeIntervalSince1970 * 1000
2087
+ for value in payload.crashDiagnostics ?? [] { Self.enqueue(kind: "crash", occurredAt: at, details: value.dictionaryRepresentation()) }
2088
+ for value in payload.hangDiagnostics ?? [] { Self.enqueue(kind: "hang", occurredAt: at, details: value.dictionaryRepresentation()) }
2089
+ for value in payload.cpuExceptionDiagnostics ?? [] { Self.enqueue(kind: "cpu-exception", occurredAt: at, details: value.dictionaryRepresentation()) }
2090
+ for value in payload.diskWriteExceptionDiagnostics ?? [] { Self.enqueue(kind: "disk-write-exception", occurredAt: at, details: value.dictionaryRepresentation()) }
2091
+ if #available(iOS 16.0, *) {
2092
+ for value in payload.appLaunchDiagnostics ?? [] { Self.enqueue(kind: "app-launch", occurredAt: at, details: value.dictionaryRepresentation()) }
2093
+ }
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+ `;
2099
+ androidBuild = `${HEADER}plugins {
2100
+ id 'com.android.library'
2101
+ id 'expo-module-gradle-plugin'
2102
+ }
2103
+
2104
+ group = 'expo.modules.absolutemobileobservability'
2105
+ version = '0.0.1'
2106
+
2107
+ android {
2108
+ namespace 'expo.modules.absolutemobileobservability'
2109
+ defaultConfig {
2110
+ versionCode 1
2111
+ versionName '0.0.1'
2112
+ }
2113
+ lintOptions { abortOnError false }
2114
+ }
2115
+ `;
2116
+ androidManifest = `${HEADER}<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
2117
+ `;
2118
+ androidModule = `${HEADER}package expo.modules.absolutemobileobservability
2119
+
2120
+ import android.app.ActivityManager
2121
+ import android.app.ApplicationExitInfo
2122
+ import android.os.Build
2123
+ import android.util.Base64
2124
+ import expo.modules.kotlin.modules.Module
2125
+ import expo.modules.kotlin.modules.ModuleDefinition
2126
+ import java.io.ByteArrayOutputStream
2127
+ import java.io.File
2128
+ import java.io.FileInputStream
2129
+ import java.io.FileOutputStream
2130
+ import java.io.InputStream
2131
+ import java.nio.charset.StandardCharsets
2132
+ import java.util.UUID
2133
+ import org.json.JSONArray
2134
+ import org.json.JSONObject
2135
+
2136
+ class AbsoluteMobileObservabilityModule : Module() {
2137
+ companion object {
2138
+ private const val MAX_REPORTS = 8
2139
+ private const val MAX_TRACE_BYTES = 32 * 1024
2140
+ private const val ACKNOWLEDGED_KEY = "acknowledged"
2141
+ private const val PREFERENCES = "AbsoluteMobileObservability"
2142
+ }
2143
+
2144
+ override fun definition() = ModuleDefinition {
2145
+ Name("AbsoluteMobileObservability")
2146
+
2147
+ AsyncFunction("pending") {
2148
+ collect()
2149
+ mapOf("reports" to files().mapNotNull { readReport(it) })
2150
+ }
2151
+
2152
+ AsyncFunction("acknowledge") { options: Map<String, List<String>> ->
2153
+ val accepted = options["ids"]?.toSet() ?: emptySet()
2154
+ val acknowledged = acknowledged().toMutableSet()
2155
+ files().forEach { file ->
2156
+ val report = readReport(file)
2157
+ val id = report?.get("id") as? String
2158
+ if (id != null && accepted.contains(id)) {
2159
+ acknowledged.add(id)
2160
+ file.delete()
2161
+ }
2162
+ }
2163
+ writeAcknowledged(acknowledged)
2164
+ }
2165
+ }
2166
+
2167
+ private fun context() = appContext.reactContext
2168
+
2169
+ private fun root(): File? {
2170
+ val context = context() ?: return null
2171
+ return File(context.filesDir, "absolute-mobile-observability").also { it.mkdirs() }
2172
+ }
2173
+
2174
+ private fun files(): List<File> = root()?.listFiles { _, name -> name.endsWith(".json") }
2175
+ ?.sortedBy { it.name } ?: emptyList()
2176
+
2177
+ private fun acknowledged(): Set<String> {
2178
+ val context = context() ?: return emptySet()
2179
+ return try {
2180
+ val values = JSONArray(context.getSharedPreferences(PREFERENCES, 0).getString(ACKNOWLEDGED_KEY, "[]"))
2181
+ (0 until values.length()).map { values.optString(it) }.toSet()
2182
+ } catch (_: Exception) { emptySet() }
2183
+ }
2184
+
2185
+ private fun writeAcknowledged(values: Set<String>) {
2186
+ val context = context() ?: return
2187
+ val bounded = values.sorted().takeLast(64)
2188
+ context.getSharedPreferences(PREFERENCES, 0).edit()
2189
+ .putString(ACKNOWLEDGED_KEY, JSONArray(bounded).toString()).apply()
2190
+ }
2191
+
2192
+ private fun kind(reason: Int): String? = when (reason) {
2193
+ ApplicationExitInfo.REASON_ANR -> "anr"
2194
+ ApplicationExitInfo.REASON_CRASH -> "crash"
2195
+ ApplicationExitInfo.REASON_CRASH_NATIVE -> "native-crash"
2196
+ ApplicationExitInfo.REASON_LOW_MEMORY -> "low-memory"
2197
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "resource-exhaustion"
2198
+ ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization-failure"
2199
+ ApplicationExitInfo.REASON_SIGNALED -> "signal"
2200
+ else -> null
2201
+ }
2202
+
2203
+ private fun trace(info: ApplicationExitInfo): ByteArray? = try {
2204
+ info.traceInputStream?.use { input ->
2205
+ val output = ByteArrayOutputStream()
2206
+ val buffer = ByteArray(4096)
2207
+ var total = 0
2208
+ while (true) {
2209
+ val count = input.read(buffer, 0, minOf(buffer.size, MAX_TRACE_BYTES + 1 - total))
2210
+ if (count <= 0) break
2211
+ output.write(buffer, 0, count)
2212
+ total += count
2213
+ if (total > MAX_TRACE_BYTES) return null
2214
+ }
2215
+ output.toByteArray()
2216
+ }
2217
+ } catch (_: Exception) { null }
2218
+
2219
+ private fun report(info: ApplicationExitInfo): JSONObject? {
2220
+ val kind = kind(info.reason) ?: return null
2221
+ val id = "android:" + info.timestamp + ":" + info.reason + ":" + info.status
2222
+ return try {
2223
+ val details = JSONObject()
2224
+ .put("importance", info.importance)
2225
+ .put("pssKb", info.pss)
2226
+ .put("reason", info.reason)
2227
+ .put("rssKb", info.rss)
2228
+ .put("status", info.status)
2229
+ info.description?.let { details.put("description", it) }
2230
+ trace(info)?.takeIf { it.isNotEmpty() }?.let { trace ->
2231
+ if (info.reason == ApplicationExitInfo.REASON_CRASH_NATIVE) {
2232
+ details.put("trace", Base64.encodeToString(trace, Base64.NO_WRAP))
2233
+ details.put("traceEncoding", "android-tombstone-protobuf-base64")
2234
+ } else {
2235
+ details.put("trace", String(trace, StandardCharsets.UTF_8))
2236
+ details.put("traceEncoding", "utf-8")
2237
+ }
2238
+ }
2239
+ JSONObject().put("details", details).put("id", id).put("kind", kind)
2240
+ .put("occurredAt", info.timestamp).put("platform", "android")
2241
+ } catch (_: Exception) { null }
2242
+ }
2243
+
2244
+ private fun collect() {
2245
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return
2246
+ val context = context() ?: return
2247
+ val directory = root() ?: return
2248
+ val manager = context.getSystemService(ActivityManager::class.java) ?: return
2249
+ val ignored = (acknowledged() + files().mapNotNull { readReport(it)?.get("id") as? String }).toMutableSet()
2250
+ manager.getHistoricalProcessExitReasons(null, 0, 16).forEach { exit ->
2251
+ val report = report(exit) ?: return@forEach
2252
+ val id = report.optString("id")
2253
+ if (ignored.contains(id)) return@forEach
2254
+ val target = File(directory, String.format(java.util.Locale.ROOT, "%013d-%s.json", exit.timestamp, UUID.randomUUID()))
2255
+ try {
2256
+ FileOutputStream(target).use { it.write(report.toString().toByteArray(StandardCharsets.UTF_8)) }
2257
+ ignored.add(id)
2258
+ } catch (_: Exception) {}
2259
+ }
2260
+ files().dropLast(MAX_REPORTS).forEach { it.delete() }
2261
+ }
2262
+
2263
+ private fun read(input: InputStream): ByteArray {
2264
+ val output = ByteArrayOutputStream()
2265
+ val buffer = ByteArray(4096)
2266
+ while (true) {
2267
+ val count = input.read(buffer)
2268
+ if (count < 0) break
2269
+ output.write(buffer, 0, count)
2270
+ }
2271
+ return output.toByteArray()
2272
+ }
2273
+
2274
+ private fun readReport(file: File): Map<String, Any?>? = try {
2275
+ FileInputStream(file).use { JSONObject(String(read(it), StandardCharsets.UTF_8)).toMap() }
2276
+ } catch (_: Exception) { null }
2277
+
2278
+ private fun JSONObject.toMap(): Map<String, Any?> = keys().asSequence().associateWith { key ->
2279
+ when (val value = get(key)) {
2280
+ is JSONObject -> value.toMap()
2281
+ is JSONArray -> (0 until value.length()).map { index ->
2282
+ when (val item = value.get(index)) {
2283
+ is JSONObject -> item.toMap()
2284
+ else -> item
2285
+ }
2286
+ }
2287
+ JSONObject.NULL -> null
2288
+ else -> value
2289
+ }
2290
+ }
2291
+ }
2292
+ `;
2293
+ expoNativeObservabilityRuntimeSource = `${HEADER}import { requireNativeModule } from 'expo';
2294
+ import { AppState, Platform } from 'react-native';
2295
+ import { ABSOLUTE_MOBILE_MANIFEST } from './webAssets';
2296
+
2297
+ type NativeObservabilityModule = {
2298
+ acknowledge(options: { ids: string[] }): Promise<void>;
2299
+ pending(): Promise<{ reports: unknown[] }>;
2300
+ };
2301
+ type NativeReport = { details: Record<string, unknown>; id: string; kind: string; occurredAt: number; platform: 'android' | 'ios' };
2302
+ const native = requireNativeModule<NativeObservabilityModule>('AbsoluteMobileObservability');
2303
+ const KINDS = new Set(['anr', 'app-launch', 'cpu-exception', 'crash', 'disk-write-exception', 'hang', 'initialization-failure', 'low-memory', 'native-crash', 'resource-exhaustion', 'signal']);
2304
+ const ID = /^[A-Za-z0-9._:-]{1,128}$/;
2305
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);
2306
+ const redactText = (value: string) => value.slice(0, 8192)
2307
+ .replace(/\\b(Bearer)\\s+[A-Za-z0-9._~+/-]+=*/gi, '$1 [REDACTED]')
2308
+ .replace(/([?&](?:access_token|api_?key|authorization|code|id_token|password|refresh_token|secret|token)=)[^&#\\s]*/gi, '$1[REDACTED]')
2309
+ .replace(/\\b((?:access_?token|api_?key|authorization|client_?secret|cookie|id_?token|password|passwd|refresh_?token|secret|token)\\s*[:=]\\s*)[^,\\s;]+/gi, '$1[REDACTED]')
2310
+ .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g, '[REDACTED]');
2311
+ const redact = (value: unknown, depth = 0): unknown => {
2312
+ if (depth >= 8) return '[TRUNCATED]';
2313
+ if (typeof value === 'string') return redactText(value);
2314
+ if (value === null || typeof value === 'number' || typeof value === 'boolean') return value;
2315
+ if (Array.isArray(value)) return value.slice(0, 64).map(entry => redact(entry, depth + 1));
2316
+ if (!isRecord(value)) return String(value).slice(0, 8192);
2317
+ return Object.fromEntries(Object.entries(value).slice(0, 128).map(([key, entry]) => [key, /(?:authorization|cookie|password|secret|token)/i.test(key) ? '[REDACTED]' : redact(entry, depth + 1)]));
2318
+ };
2319
+ const parse = (value: unknown): NativeReport | undefined => {
2320
+ if (!isRecord(value) || !isRecord(value.details) || typeof value.id !== 'string' || !ID.test(value.id) || typeof value.kind !== 'string' || !KINDS.has(value.kind) || typeof value.occurredAt !== 'number' || !Number.isFinite(value.occurredAt) || value.occurredAt <= 0 || value.platform !== 'android' && value.platform !== 'ios') return;
2321
+ const details = redact(value.details);
2322
+ if (!isRecord(details) || new TextEncoder().encode(JSON.stringify(details)).byteLength > 64 * 1024) return;
2323
+ return { details, id: value.id, kind: value.kind, occurredAt: value.occurredAt, platform: value.platform };
2324
+ };
2325
+ const sampled = (id: string, rate: number) => {
2326
+ if (rate >= 1) return true;
2327
+ let hash = 2166136261;
2328
+ for (const character of id) { hash ^= character.codePointAt(0) ?? 0; hash = Math.imul(hash, 16777619); }
2329
+ return (hash >>> 0) / 4294967296 < rate;
2330
+ };
2331
+ let drainPromise: Promise<void> | undefined;
2332
+ let started = false;
2333
+ const drain = () => {
2334
+ drainPromise ??= (async () => {
2335
+ const manifest = ABSOLUTE_MOBILE_MANIFEST;
2336
+ const config = manifest?.observability;
2337
+ if (!manifest || !config || Platform.OS !== 'android' && Platform.OS !== 'ios') return;
2338
+ const reports = (await native.pending()).reports.slice(0, 8).map(parse).filter((report): report is NativeReport => report !== undefined);
2339
+ const discarded = reports.filter(report => !sampled(report.id, config.sampleRate));
2340
+ if (discarded.length) await native.acknowledge({ ids: discarded.map(report => report.id) });
2341
+ const selected = reports.filter(report => sampled(report.id, config.sampleRate));
2342
+ if (!selected.length) return;
2343
+ const response = await fetch(config.endpoint, {
2344
+ body: JSON.stringify({ ...(config.environment ? { environment: config.environment } : {}), events: selected.map(report => ({ at: report.occurredAt, extra: { nativeDiagnostic: report.details, nativeDiagnosticId: report.id }, groupingKey: 'absolute-native:' + report.platform + ':' + report.kind, level: ['app-launch', 'cpu-exception', 'disk-write-exception', 'low-memory', 'resource-exhaustion', 'signal'].includes(report.kind) ? 'warning' : 'error', message: 'Native process diagnostic \u2014 ' + report.kind, name: 'AbsoluteMobileNativeDiagnostic', tags: { absoluteMobile: 'true', mobileAppBuild: manifest.appBuild, mobileEngine: 'expo', mobileFailurePhase: 'native-process', mobileManifestFormat: '1', mobileNativeRuntime: 'expo', mobilePlatform: report.platform, mobileRuntime: manifest.runtime } })), project: config.project, release: manifest.appBuild, v: 1 }),
2345
+ headers: { 'content-type': 'application/json' },
2346
+ method: 'POST'
2347
+ });
2348
+ if (response.ok) await native.acknowledge({ ids: selected.map(report => report.id) });
2349
+ })().catch(() => undefined).finally(() => { drainPromise = undefined; });
2350
+ return drainPromise;
2351
+ };
2352
+ export const startAbsoluteExpoNativeObservability = () => {
2353
+ if (!started) {
2354
+ started = true;
2355
+ AppState.addEventListener('change', state => { if (state === 'active') void drain(); });
2356
+ }
2357
+ return drain();
2358
+ };
2359
+ `;
2360
+ });
2361
+
1942
2362
  // src/mobile/expoProject.ts
1943
2363
  import {
1944
2364
  access,
@@ -1952,7 +2372,7 @@ import {
1952
2372
  writeFile
1953
2373
  } from "fs/promises";
1954
2374
  import { createHash as createHash3 } from "crypto";
1955
- import { basename as basename2, dirname as dirname4, join as join8, relative as relative2, resolve as resolve5, sep } from "path";
2375
+ import { basename as basename2, dirname as dirname4, join as join9, relative as relative2, resolve as resolve5, sep } from "path";
1956
2376
  var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
1957
2377
  `, EXPO_ASSET_EXTENSION = ".absasset", EXPO_PROJECT_MARKER = ".absolutejs-expo-project", exists = async (path) => {
1958
2378
  try {
@@ -2023,7 +2443,7 @@ var EXPO_GENERATED_HEADER = `// Generated by AbsoluteJS. Edit absolute.config.ts
2023
2443
  }
2024
2444
  return segment;
2025
2445
  });
2026
- }, routeFile = (project, route) => join8(project, "app", ...routeSegments(route), "index.tsx"), packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
2446
+ }, routeFile = (project, route) => join9(project, "app", ...routeSegments(route), "index.tsx"), packageDependencies = (plan) => Object.fromEntries(plan.requiredPackages.map((spec) => {
2027
2447
  const separator = spec.lastIndexOf("@");
2028
2448
  return [spec.slice(0, separator), spec.slice(separator + 1)];
2029
2449
  })), expoPackage = (auth, sync, devices, updates) => ({
@@ -2210,16 +2630,19 @@ config.resolver.nodeModulesPaths = [
2210
2630
  config.watchFolders = [appRoot];
2211
2631
 
2212
2632
  module.exports = config;
2213
- `, layoutSource = (auth, sync, updates) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
2633
+ `, layoutSource = (auth, sync, updates, observability) => `${EXPO_GENERATED_HEADER}import { Stack } from 'expo-router';
2214
2634
  import '../src/generated/AbsoluteDevices';
2215
2635
  ${auth ? `import { useEffect, useState } from 'react';
2216
2636
  import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';` : ""}
2217
2637
  ${sync ? "import { startAbsoluteExpoSync } from '../src/generated/AbsoluteSync';" : ""}
2218
2638
  ${updates ? `import { useEffect as useUpdateEffect } from 'react';
2219
2639
  import { startAbsoluteExpoUpdates } from '../src/generated/AbsoluteUpdates';` : ""}
2640
+ ${observability ? `import { useEffect as useNativeObservabilityEffect } from 'react';
2641
+ import { startAbsoluteExpoNativeObservability } from '../src/generated/AbsoluteNativeObservability';` : ""}
2220
2642
 
2221
2643
  export default function AbsoluteLayout() {
2222
2644
  ${updates ? "useUpdateEffect(() => { void startAbsoluteExpoUpdates(); }, []);" : ""}
2645
+ ${observability ? "useNativeObservabilityEffect(() => { void startAbsoluteExpoNativeObservability(); }, []);" : ""}
2223
2646
  ${auth ? `const [ready, setReady] = useState(false);
2224
2647
  useEffect(() => {
2225
2648
  let active = true;
@@ -3010,8 +3433,19 @@ export default createAbsoluteNativeRoute(ApplicationNativeRoute, ${JSON.stringif
3010
3433
  await writeFile(temporary, source, { flag: "wx" });
3011
3434
  await rename(temporary, path);
3012
3435
  return true;
3436
+ }, pruneManagedFiles = async (paths) => {
3437
+ const removed = await Promise.all(paths.map(async (path) => {
3438
+ if (!await exists(path))
3439
+ return false;
3440
+ const source = await readFile(path, "utf8");
3441
+ if (!source.startsWith(EXPO_GENERATED_HEADER) && !path.endsWith("expo-module.config.json"))
3442
+ return false;
3443
+ await rm(path, { force: true });
3444
+ return true;
3445
+ }));
3446
+ return removed.filter(Boolean).length;
3013
3447
  }, pruneStaleManagedExpoRoutes = async (project, expected) => {
3014
- const appDirectory = join8(project, "app");
3448
+ const appDirectory = join9(project, "app");
3015
3449
  if (!await exists(appDirectory))
3016
3450
  return 0;
3017
3451
  const files = await walkFiles(appDirectory);
@@ -3050,7 +3484,7 @@ export default createAbsoluteNativeRoute(ApplicationNativeRoute, ${JSON.stringif
3050
3484
  if (missing) {
3051
3485
  throw new TypeError(`Expo native route ${missing.route} references missing module ${missing.module}.`);
3052
3486
  }
3053
- const marker = join8(project, EXPO_PROJECT_MARKER);
3487
+ const marker = join9(project, EXPO_PROJECT_MARKER);
3054
3488
  if (await exists(project) && !await exists(marker)) {
3055
3489
  const entries = await readdir(project);
3056
3490
  if (entries.length > 0 && !options.force) {
@@ -3063,7 +3497,7 @@ export default createAbsoluteNativeRoute(ApplicationNativeRoute, ${JSON.stringif
3063
3497
  const authEnabled = Boolean(auth);
3064
3498
  const files = new Map([
3065
3499
  [
3066
- join8(project, ".gitignore"),
3500
+ join9(project, ".gitignore"),
3067
3501
  `.expo/
3068
3502
  android/
3069
3503
  ios/
@@ -3071,77 +3505,82 @@ node_modules/
3071
3505
  `
3072
3506
  ],
3073
3507
  [
3074
- join8(project, "app.json"),
3508
+ join9(project, "app.json"),
3075
3509
  jsonSource(expoAppConfig(config, authEnabled, syncEnabled, devices, runtimeFingerprint))
3076
3510
  ],
3077
- [join8(project, "app.config.js"), expoDynamicAppConfig],
3511
+ [join9(project, "app.config.js"), expoDynamicAppConfig],
3078
3512
  [
3079
- join8(project, "package.json"),
3513
+ join9(project, "package.json"),
3080
3514
  jsonSource(expoPackage(authEnabled, syncEnabled, devices, config.updates !== undefined))
3081
3515
  ],
3082
- [join8(project, "metro.config.js"), metroConfig(projectRoot)],
3516
+ [join9(project, "metro.config.js"), metroConfig(projectRoot)],
3083
3517
  [
3084
- join8(project, "plugins", "withAbsoluteDevelopmentCa.js"),
3518
+ join9(project, "plugins", "withAbsoluteDevelopmentCa.js"),
3085
3519
  expoDevelopmentCaPlugin
3086
3520
  ],
3087
3521
  [
3088
- join8(project, "tsconfig.json"),
3522
+ join9(project, "tsconfig.json"),
3089
3523
  jsonSource(expoTsConfig(projectRoot, project, authEnabled, syncEnabled))
3090
3524
  ],
3091
3525
  [
3092
- join8(project, "app", "_layout.tsx"),
3093
- layoutSource(authEnabled, syncEnabled, config.updates !== undefined)
3526
+ join9(project, "app", "_layout.tsx"),
3527
+ layoutSource(authEnabled, syncEnabled, config.updates !== undefined, config.observability !== undefined)
3094
3528
  ],
3095
3529
  [
3096
- join8(project, "app", "__absolute", "native", "index.tsx"),
3530
+ join9(project, "app", "__absolute", "native", "index.tsx"),
3097
3531
  nativeDiagnosticSource
3098
3532
  ],
3099
3533
  [
3100
- join8(project, "src", "generated", "AbsoluteDevices.ts"),
3534
+ join9(project, "src", "generated", "AbsoluteDevices.ts"),
3101
3535
  devicesRuntimeSource(config, devices, authEnabled)
3102
3536
  ],
3103
3537
  [
3104
- join8(project, "src", "generated", "AbsoluteNativeRoute.tsx"),
3538
+ join9(project, "src", "generated", "AbsoluteNativeRoute.tsx"),
3105
3539
  nativeRouteRuntimeSource(authEnabled)
3106
3540
  ],
3107
3541
  [
3108
- join8(project, "src", "generated", "AbsoluteWebHost.tsx"),
3542
+ join9(project, "src", "generated", "AbsoluteWebHost.tsx"),
3109
3543
  webHostSource(config, auth, syncEnabled)
3110
3544
  ]
3111
3545
  ]);
3546
+ if (config.observability) {
3547
+ for (const [path, source] of absoluteExpoNativeObservabilityFiles(project))
3548
+ files.set(path, source);
3549
+ }
3112
3550
  if (config.updates) {
3113
- files.set(join8(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource(config));
3551
+ files.set(join9(project, "src", "generated", "AbsoluteUpdates.ts"), updatesRuntimeSource(config));
3114
3552
  }
3115
3553
  const expoCodeSigning = config.updates?.expoCodeSigning;
3116
3554
  if (expoCodeSigning)
3117
- files.set(join8(project, "certs", "absolute-mobile-update.pem"), expoCodeSigning.certificatePem);
3555
+ files.set(join9(project, "certs", "absolute-mobile-update.pem"), expoCodeSigning.certificatePem);
3118
3556
  if (auth) {
3119
- files.set(join8(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
3557
+ files.set(join9(project, "src", "generated", "AbsoluteAuth.ts"), authRuntimeSource(auth, config.appId));
3120
3558
  }
3121
3559
  if (syncSchema) {
3122
- files.set(join8(project, "src", "generated", "AbsoluteSync.ts"), syncRuntimeSource(config, syncSchema));
3560
+ files.set(join9(project, "src", "generated", "AbsoluteSync.ts"), syncRuntimeSource(config, syncSchema));
3123
3561
  }
3124
- const webAssetsPath = join8(project, "src", "generated", "webAssets.ts");
3562
+ const webAssetsPath = join9(project, "src", "generated", "webAssets.ts");
3125
3563
  if (!await exists(webAssetsPath)) {
3126
3564
  files.set(webAssetsPath, emptyWebAssetsSource);
3127
3565
  }
3128
3566
  if (!config.expoNativeRoutes["/"]) {
3129
- files.set(join8(project, "app", "index.tsx"), webRouteSource);
3567
+ files.set(join9(project, "app", "index.tsx"), webRouteSource);
3130
3568
  }
3131
- files.set(join8(project, "app", "[...absolute].tsx"), catchAllRouteSource);
3132
- const nativeRouteRuntime = join8(project, "src", "generated", "AbsoluteNativeRoute.tsx");
3569
+ files.set(join9(project, "app", "[...absolute].tsx"), catchAllRouteSource);
3570
+ const nativeRouteRuntime = join9(project, "src", "generated", "AbsoluteNativeRoute.tsx");
3133
3571
  for (const [route, module] of routeModules) {
3134
- const wrapper = route === "/" ? join8(project, "app", "index.tsx") : routeFile(project, route);
3572
+ const wrapper = route === "/" ? join9(project, "app", "index.tsx") : routeFile(project, route);
3135
3573
  files.set(wrapper, nativeWrapperSource(wrapper, module, nativeRouteRuntime, route));
3136
3574
  }
3137
- const removed = await pruneStaleManagedExpoRoutes(project, new Set([...files.keys()].filter((path) => path.startsWith(`${join8(project, "app")}${sep}`))));
3575
+ const removedRoutes = await pruneStaleManagedExpoRoutes(project, new Set([...files.keys()].filter((path) => path.startsWith(`${join9(project, "app")}${sep}`))));
3576
+ const removedObservability = config.observability ? 0 : await pruneManagedFiles(absoluteExpoNativeObservabilityPaths(project));
3138
3577
  const changes = await Promise.all([...files].map(([path, source]) => writeManagedFile(path, source, true)));
3139
- const changed = removed + changes.filter(Boolean).length;
3578
+ const changed = removedRoutes + removedObservability + changes.filter(Boolean).length;
3140
3579
  return { changed, path: project, written: [...files.keys()] };
3141
3580
  }, walkFiles = async (root, directory = root) => {
3142
3581
  const entries = await readdir(directory, { withFileTypes: true });
3143
3582
  const nested = await Promise.all(entries.map((entry) => {
3144
- const path = join8(directory, entry.name);
3583
+ const path = join9(directory, entry.name);
3145
3584
  if (entry.isDirectory())
3146
3585
  return walkFiles(root, path);
3147
3586
  if (entry.isFile())
@@ -3204,11 +3643,11 @@ export const materializeAbsoluteWebBundle = async () => {
3204
3643
  `, syncAbsoluteExpoWebAssets = async (config) => {
3205
3644
  if (config.engine !== "expo")
3206
3645
  throw new TypeError("Expo asset sync requires mobile.engine: expo.");
3207
- const marker = join8(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
3646
+ const marker = join9(config.nativeProjectDirectory, EXPO_PROJECT_MARKER);
3208
3647
  if (!await exists(marker)) {
3209
3648
  throw new TypeError("Expo asset sync requires an AbsoluteJS-managed Expo project. Run mobile init first.");
3210
3649
  }
3211
- const manifestPath = join8(config.bundleDirectory, "absolute-mobile-manifest.json");
3650
+ const manifestPath = join9(config.bundleDirectory, "absolute-mobile-manifest.json");
3212
3651
  const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
3213
3652
  const nativeDataManifest = expoNativeDataManifest(manifest);
3214
3653
  const { appBuild } = nativeDataManifest;
@@ -3222,16 +3661,16 @@ export const materializeAbsoluteWebBundle = async () => {
3222
3661
  bundleHash.update("\x00");
3223
3662
  });
3224
3663
  const bundleId = `amexpo_${bundleHash.digest("hex")}`;
3225
- const destination = join8(config.nativeProjectDirectory, "assets", "absolute");
3664
+ const destination = join9(config.nativeProjectDirectory, "assets", "absolute");
3226
3665
  await mkdir(dirname4(destination), { recursive: true });
3227
- const staging = await mkdtemp(join8(dirname4(destination), `.${basename2(destination)}.stage-`));
3666
+ const staging = await mkdtemp(join9(dirname4(destination), `.${basename2(destination)}.stage-`));
3228
3667
  let assets;
3229
3668
  try {
3230
3669
  assets = await Promise.all(files.map(async (source, index) => {
3231
3670
  const name = `${String(index).padStart(6, "0")}${EXPO_ASSET_EXTENSION}`;
3232
- await cp(source, join8(staging, name));
3671
+ await cp(source, join9(staging, name));
3233
3672
  return {
3234
- asset: portableRelative(join8(config.nativeProjectDirectory, "src", "generated"), join8(destination, name)),
3673
+ asset: portableRelative(join9(config.nativeProjectDirectory, "src", "generated"), join9(destination, name)),
3235
3674
  path: relative2(config.bundleDirectory, source).replaceAll("\\", "/")
3236
3675
  };
3237
3676
  }));
@@ -3240,7 +3679,7 @@ export const materializeAbsoluteWebBundle = async () => {
3240
3679
  await rm(staging, { force: true, recursive: true });
3241
3680
  throw error;
3242
3681
  }
3243
- const generated = join8(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
3682
+ const generated = join9(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
3244
3683
  await writeManagedFile(generated, assetModuleSource(assets, bundleId, nativeDataManifest), true);
3245
3684
  return { appBuild, assets: assets.length, bundleId, path: destination };
3246
3685
  };
@@ -3249,6 +3688,7 @@ var init_expoProject = __esm(() => {
3249
3688
  init_syncSchema();
3250
3689
  init_deviceCapabilities();
3251
3690
  init_updateRuntime();
3691
+ init_expoNativeObservability();
3252
3692
  expoDynamicAppConfig = `${EXPO_GENERATED_HEADER}const config = require('./app.json');
3253
3693
 
3254
3694
  if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
@@ -3585,7 +4025,7 @@ var init_utils = __esm(() => {
3585
4025
  // src/mobile/emulatorDoctor.ts
3586
4026
  import { access as access3 } from "fs/promises";
3587
4027
  import { homedir as homedir3 } from "os";
3588
- import { join as join10 } from "path";
4028
+ import { join as join11 } from "path";
3589
4029
  var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command) => {
3590
4030
  try {
3591
4031
  const result = Bun.spawnSync(command, {
@@ -3630,15 +4070,15 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
3630
4070
  }
3631
4071
  }, absoluteManagedAndroidSdkRoot = (host2, env = process.env) => {
3632
4072
  if (host2 === "windows") {
3633
- return join10(env.LOCALAPPDATA ?? join10(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
4073
+ return join11(env.LOCALAPPDATA ?? join11(homedir3(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
3634
4074
  }
3635
4075
  if (host2 === "wsl") {
3636
4076
  const localAppData = windowsLocalAppDataFromWsl();
3637
4077
  if (localAppData) {
3638
- return join10(localAppData, "AbsoluteJS", "Android", "Sdk");
4078
+ return join11(localAppData, "AbsoluteJS", "Android", "Sdk");
3639
4079
  }
3640
4080
  }
3641
- return join10(homedir3(), ".absolutejs", "android-sdk");
4081
+ return join11(homedir3(), ".absolutejs", "android-sdk");
3642
4082
  }, pathExists = async (path) => {
3643
4083
  try {
3644
4084
  await access3(path);
@@ -3691,7 +4131,7 @@ var ABSOLUTE_ANDROID_AVD_NAME = "AbsoluteJS_API_36", captureCommand = (command)
3691
4131
  const capture = input.capture ?? captureCommand;
3692
4132
  const androidRoot = input.androidRoot === null ? undefined : input.androidRoot ?? env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2, env);
3693
4133
  const windowsAndroidTools = host2 === "windows" || host2 === "wsl";
3694
- const android = (segments) => androidRoot ? join10(androidRoot, ...segments) : undefined;
4134
+ const android = (segments) => androidRoot ? join11(androidRoot, ...segments) : undefined;
3695
4135
  const paths = (values) => values.filter((value) => Boolean(value));
3696
4136
  const adb = await findExecutable("adb", paths([
3697
4137
  android(["platform-tools", windowsAndroidTools ? "adb.exe" : "adb"])
@@ -3858,7 +4298,7 @@ import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
3858
4298
  import {
3859
4299
  dirname as dirname5,
3860
4300
  isAbsolute,
3861
- join as join11,
4301
+ join as join12,
3862
4302
  relative as relative4,
3863
4303
  resolve as resolve8,
3864
4304
  sep as sep2,
@@ -4031,13 +4471,13 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4031
4471
  return;
4032
4472
  throw new DOMException("Android development startup was cancelled.", "AbortError");
4033
4473
  }, journalPaths = (projectRoot) => {
4034
- const root = join11(projectRoot, ".absolutejs", "mobile", "dev-session");
4474
+ const root = join12(projectRoot, ".absolutejs", "mobile", "dev-session");
4035
4475
  return {
4036
- backup: join11(root, "capacitor.config.backup.json"),
4037
- caBackup: join11(root, "absolutejs_dev_ca.backup.pem"),
4038
- journal: join11(root, "journal.json"),
4039
- manifestBackup: join11(root, "AndroidManifest.backup.xml"),
4040
- networkConfigBackup: join11(root, "absolutejs_dev_network_security.backup.xml"),
4476
+ backup: join12(root, "capacitor.config.backup.json"),
4477
+ caBackup: join12(root, "absolutejs_dev_ca.backup.pem"),
4478
+ journal: join12(root, "journal.json"),
4479
+ manifestBackup: join12(root, "AndroidManifest.backup.xml"),
4480
+ networkConfigBackup: join12(root, "absolutejs_dev_network_security.backup.xml"),
4041
4481
  root
4042
4482
  };
4043
4483
  }, isInside = (root, path) => {
@@ -4045,7 +4485,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4045
4485
  const resolvedPath = resolve8(path);
4046
4486
  const relativePath = relative4(resolvedRoot, resolvedPath);
4047
4487
  return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep2}`) && !isAbsolute(relativePath);
4048
- }, isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join11(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
4488
+ }, isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), nativeCachePath = (projectRoot) => join12(projectRoot, ".absolutejs", "mobile", "cache", "android-debug.json"), parseNativeCache = (value) => {
4049
4489
  if (!isRecord2(value))
4050
4490
  return null;
4051
4491
  const { appId, fingerprint, format, installations } = value;
@@ -4077,7 +4517,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4077
4517
  });
4078
4518
  }
4079
4519
  }, nativeDependencySources = async (nativeDirectory) => {
4080
- const settings = await readFile4(join11(nativeDirectory, "capacitor.settings.gradle"), "utf8");
4520
+ const settings = await readFile4(join12(nativeDirectory, "capacitor.settings.gradle"), "utf8");
4081
4521
  const pattern = new RegExp(CAPACITOR_PROJECT_DIRECTORY_PATTERN.source, CAPACITOR_PROJECT_DIRECTORY_PATTERN.flags);
4082
4522
  const dependencies = [...settings.matchAll(pattern)].map((match) => ({
4083
4523
  name: (match[1] ?? "").slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_"),
@@ -4117,7 +4557,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4117
4557
  }, collectNativeDirectory = async (root, label, directory, ignorePublicBundle) => {
4118
4558
  const entries = await readdir2(directory, { withFileTypes: true });
4119
4559
  entries.sort((left, right) => left.name.localeCompare(right.name));
4120
- const records = await Promise.all(entries.map((entry) => collectNativePath(root, label, join11(directory, entry.name), entry.isDirectory(), entry.isFile(), entry.isSymbolicLink(), ignorePublicBundle)));
4560
+ const records = await Promise.all(entries.map((entry) => collectNativePath(root, label, join12(directory, entry.name), entry.isDirectory(), entry.isFile(), entry.isSymbolicLink(), ignorePublicBundle)));
4121
4561
  return records.flat();
4122
4562
  }, hashNativeTree = async (root, label, ignorePublicBundle) => {
4123
4563
  const resolvedRoot = await realpath(root);
@@ -4246,11 +4686,11 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4246
4686
  await mkdir2(paths.root, { recursive: true });
4247
4687
  await writeFile3(paths.backup, source, { flag: "wx" });
4248
4688
  await writeFile3(paths.manifestBackup, manifestSource, { flag: "wx" });
4249
- const resourceRoot = join11(dirname5(nativeManifestPath), "res");
4250
- const caPath = join11(resourceRoot, "raw", "absolutejs_dev_ca.pem");
4689
+ const resourceRoot = join12(dirname5(nativeManifestPath), "res");
4690
+ const caPath = join12(resourceRoot, "raw", "absolutejs_dev_ca.pem");
4251
4691
  const existingNetworkConfig = manifestSource.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
4252
4692
  const networkConfigName = existingNetworkConfig ?? "absolutejs_dev_network_security";
4253
- const networkConfigPath = join11(resourceRoot, "xml", `${networkConfigName}.xml`);
4693
+ const networkConfigPath = join12(resourceRoot, "xml", `${networkConfigName}.xml`);
4254
4694
  const backupProjectedFile = async ([path, backupPath]) => {
4255
4695
  if (!await pathExists2(path))
4256
4696
  return { path };
@@ -4343,7 +4783,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4343
4783
  }
4344
4784
  return result.stdout.trim();
4345
4785
  }, mirroredCapacitorDependencies = async (project, capture) => {
4346
- const settingsPath = join11(project.nativeDirectory, "capacitor.settings.gradle");
4786
+ const settingsPath = join12(project.nativeDirectory, "capacitor.settings.gradle");
4347
4787
  const settings = await readFile4(settingsPath, "utf8");
4348
4788
  const dependencies = [];
4349
4789
  const rewrittenSettings = settings.replace(CAPACITOR_PROJECT_DIRECTORY_PATTERN, (_statement, projectName, sourcePath) => {
@@ -4389,7 +4829,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4389
4829
  ].join("; ");
4390
4830
  return Buffer.from(source, "utf16le").toString("base64");
4391
4831
  }, gradleArtifactPath = (nativeDirectory, task, windows = false) => {
4392
- const pathJoin = windows ? win32.join : join11;
4832
+ const pathJoin = windows ? win32.join : join12;
4393
4833
  if (task === "assembleDebug") {
4394
4834
  return pathJoin(nativeDirectory, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
4395
4835
  }
@@ -4402,7 +4842,7 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4402
4842
  if (task !== "assembleRelease" || await pathExists2(primary)) {
4403
4843
  return primary;
4404
4844
  }
4405
- return join11(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
4845
+ return join12(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
4406
4846
  }, buildAbsoluteAndroidGradleArtifact = async (options) => {
4407
4847
  const { project, task } = options;
4408
4848
  const capture = options.capture ?? captureCommand2;
@@ -4630,18 +5070,18 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4630
5070
  if (!adb || options.target !== "device" && !emulator) {
4631
5071
  throw new Error("Android SDK tools disappeared after readiness checks.");
4632
5072
  }
4633
- const cap = join11(projectRoot, "node_modules", ".bin", host2 === "windows" ? "cap.cmd" : "cap");
5073
+ const cap = join12(projectRoot, "node_modules", ".bin", host2 === "windows" ? "cap.cmd" : "cap");
4634
5074
  if (!await pathExists2(cap)) {
4635
5075
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
4636
5076
  }
4637
5077
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
4638
5078
  await mkdir2(config.bundleDirectory, { recursive: true });
4639
- const placeholder = join11(config.bundleDirectory, "index.html");
5079
+ const placeholder = join12(config.bundleDirectory, "index.html");
4640
5080
  if (!await pathExists2(placeholder)) {
4641
5081
  await writeFile3(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
4642
5082
  `);
4643
5083
  }
4644
- const nativeDirectory = join11(config.nativeProjectDirectory, "android");
5084
+ const nativeDirectory = join12(config.nativeProjectDirectory, "android");
4645
5085
  if (!await pathExists2(nativeDirectory)) {
4646
5086
  if (!options.createNativeProject) {
4647
5087
  throw new Error("Android native project has not been created.");
@@ -4702,8 +5142,8 @@ var ANDROID_BOOT_TIMEOUT_MS = 180000, ANDROID_BOOT_POLL_MS = 1000, ANDROID_CAPTU
4702
5142
  await requireSuccess([project.cap, "sync", "android"], "Capacitor Android synchronization", run, { cwd: project.projectRoot, env, signal: options.signal });
4703
5143
  throwIfAborted(options.signal);
4704
5144
  transition("configuring");
4705
- const nativeConfigPath = join11(project.nativeDirectory, "app", "src", "main", "assets", "capacitor.config.json");
4706
- const nativeManifestPath = join11(project.nativeDirectory, "app", "src", "main", "AndroidManifest.xml");
5145
+ const nativeConfigPath = join12(project.nativeDirectory, "app", "src", "main", "assets", "capacitor.config.json");
5146
+ const nativeManifestPath = join12(project.nativeDirectory, "app", "src", "main", "AndroidManifest.xml");
4707
5147
  let connectedSerial;
4708
5148
  let nativeLogs = null;
4709
5149
  const closeNativeLogs = async () => {
@@ -4862,7 +5302,7 @@ var init_androidEmulatorController = __esm(() => {
4862
5302
  import { createHash as createHash5 } from "crypto";
4863
5303
  import { cp as cp2, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm3 } from "fs/promises";
4864
5304
  import { tmpdir } from "os";
4865
- import { basename as basename3, join as join12 } from "path";
5305
+ import { basename as basename3, join as join13 } from "path";
4866
5306
  var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION = "15859902", LICENSE_ACCEPTANCE_RESPONSES = 100, COMMAND_LINE_TOOLS, defaultRun = async (command, options = {}) => {
4867
5307
  const subprocess = Bun.spawn(command, {
4868
5308
  env: options.env,
@@ -4894,7 +5334,7 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
4894
5334
  exitCode: result.exitCode,
4895
5335
  stdout: result.stdout.toString()
4896
5336
  };
4897
- }, commandPath = (root, host2, tool) => join12(root, "cmdline-tools", "latest", "bin", host2 === "windows" || host2 === "wsl" ? `${tool}.bat` : tool), executablePath = (root, host2, directory, tool) => join12(root, directory, host2 === "windows" || host2 === "wsl" ? `${tool}.exe` : tool), windowsPath = (path) => {
5337
+ }, commandPath = (root, host2, tool) => join13(root, "cmdline-tools", "latest", "bin", host2 === "windows" || host2 === "wsl" ? `${tool}.bat` : tool), executablePath = (root, host2, directory, tool) => join13(root, directory, host2 === "windows" || host2 === "wsl" ? `${tool}.exe` : tool), windowsPath = (path) => {
4898
5338
  const match = /^\/mnt\/([a-z])\/(.*)$/i.exec(path);
4899
5339
  if (!match)
4900
5340
  return path;
@@ -4968,10 +5408,10 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
4968
5408
  if (digest !== release.sha256) {
4969
5409
  throw new Error(`Android command-line-tools checksum mismatch: expected ${release.sha256}, received ${digest}.`);
4970
5410
  }
4971
- const temporary = await mkdtemp2(join12(tmpdir(), "absolutejs-android-sdk-"));
5411
+ const temporary = await mkdtemp2(join13(tmpdir(), "absolutejs-android-sdk-"));
4972
5412
  try {
4973
- const archive = join12(temporary, "command-line-tools.zip");
4974
- const extracted = join12(temporary, "extracted");
5413
+ const archive = join13(temporary, "command-line-tools.zip");
5414
+ const extracted = join13(temporary, "extracted");
4975
5415
  await Bun.write(archive, bytes);
4976
5416
  await mkdir3(extracted, { recursive: true });
4977
5417
  const extraction = plan.host === "windows" ? [
@@ -4988,12 +5428,12 @@ var ANDROID_API = 36, ANDROID_BUILD_TOOLS = "36.0.0", COMMAND_LINE_TOOLS_VERSION
4988
5428
  if (await input.run(extraction) !== 0) {
4989
5429
  throw new Error("Failed to extract Android command-line tools.");
4990
5430
  }
4991
- const destination = join12(plan.androidRoot, "cmdline-tools", "latest");
4992
- await mkdir3(join12(plan.androidRoot, "cmdline-tools"), {
5431
+ const destination = join13(plan.androidRoot, "cmdline-tools", "latest");
5432
+ await mkdir3(join13(plan.androidRoot, "cmdline-tools"), {
4993
5433
  recursive: true
4994
5434
  });
4995
5435
  await rm3(destination, { force: true, recursive: true });
4996
- await cp2(join12(extracted, "cmdline-tools"), destination, {
5436
+ await cp2(join13(extracted, "cmdline-tools"), destination, {
4997
5437
  recursive: true
4998
5438
  });
4999
5439
  } finally {
@@ -5178,7 +5618,7 @@ import {
5178
5618
  stat,
5179
5619
  writeFile as writeFile4
5180
5620
  } from "fs/promises";
5181
- import { dirname as dirname6, isAbsolute as isAbsolute2, join as join13, relative as relative5, resolve as resolve9, sep as sep3 } from "path";
5621
+ import { dirname as dirname6, isAbsolute as isAbsolute2, join as join14, relative as relative5, resolve as resolve9, sep as sep3 } from "path";
5182
5622
  var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5183
5623
  if (value === undefined)
5184
5624
  return;
@@ -5235,7 +5675,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5235
5675
  }, ignoredFingerprintDirectories, fingerprintFiles = async (root, current = root, options = {}) => {
5236
5676
  const entries = await readdir3(current, { withFileTypes: true });
5237
5677
  const nested = await Promise.all(entries.sort((left, right) => left.name.localeCompare(right.name)).map(async (entry) => {
5238
- const path = join13(current, entry.name);
5678
+ const path = join14(current, entry.name);
5239
5679
  const projectRelative = relative5(root, path).replaceAll("\\", "/");
5240
5680
  const ignored = entry.isDirectory() && (ignoredFingerprintDirectories.has(entry.name) || projectRelative === "App/App/public" && options.includePublicBundle !== true);
5241
5681
  if (ignored)
@@ -5269,7 +5709,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5269
5709
  return;
5270
5710
  const entries = await readdir3(root, { withFileTypes: true });
5271
5711
  const matches = await Promise.all(entries.map(async (entry) => {
5272
- const path = join13(root, entry.name);
5712
+ const path = join14(root, entry.name);
5273
5713
  if (entry.isDirectory() && entry.name.endsWith(extension))
5274
5714
  return path;
5275
5715
  if (entry.isFile() && entry.name.endsWith(extension))
@@ -5282,7 +5722,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5282
5722
  return [];
5283
5723
  const entries = await readdir3(root, { withFileTypes: true });
5284
5724
  const matches = await Promise.all(entries.map(async (entry) => {
5285
- const path = join13(root, entry.name);
5725
+ const path = join14(root, entry.name);
5286
5726
  if (entry.name.endsWith(extension))
5287
5727
  return [path];
5288
5728
  if (!entry.isDirectory() || options.excludedDirectories?.has(entry.name))
@@ -5351,10 +5791,10 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5351
5791
  type: value.type
5352
5792
  };
5353
5793
  }, installRelease = async (artifactPath, metadata, outputRoot) => {
5354
- const releaseRoot = join13(outputRoot, metadata.releaseId);
5355
- const destination = join13(releaseRoot, "App.ipa");
5794
+ const releaseRoot = join14(outputRoot, metadata.releaseId);
5795
+ const destination = join14(releaseRoot, "App.ipa");
5356
5796
  if (await pathExists3(releaseRoot)) {
5357
- const value = JSON.parse(await readFile6(join13(releaseRoot, "release.json"), "utf8"));
5797
+ const value = JSON.parse(await readFile6(join14(releaseRoot, "release.json"), "utf8"));
5358
5798
  if (!isRecord3(value) || value.artifact !== "App.ipa" || Object.entries(metadata).some(([key, expected]) => Reflect.get(value, key) !== expected)) {
5359
5799
  throw new TypeError(`Immutable iOS release ${metadata.releaseId} does not match its content.`);
5360
5800
  }
@@ -5374,14 +5814,14 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5374
5814
  };
5375
5815
  }
5376
5816
  await mkdir4(dirname6(releaseRoot), { recursive: true });
5377
- const staging = await mkdtemp3(join13(dirname6(releaseRoot), ".ios-stage-"));
5817
+ const staging = await mkdtemp3(join14(dirname6(releaseRoot), ".ios-stage-"));
5378
5818
  try {
5379
- await copyFile2(artifactPath, join13(staging, "App.ipa"));
5819
+ await copyFile2(artifactPath, join14(staging, "App.ipa"));
5380
5820
  const complete = {
5381
5821
  ...metadata,
5382
5822
  artifact: "App.ipa"
5383
5823
  };
5384
- await writeFile4(join13(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
5824
+ await writeFile4(join14(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
5385
5825
  `, { flag: "wx" });
5386
5826
  await rename4(staging, releaseRoot);
5387
5827
  return { artifactPath: destination, metadata: complete, releaseRoot };
@@ -5398,10 +5838,10 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5398
5838
  const marketingVersion = options.config.iosVersion;
5399
5839
  if (!marketingVersion)
5400
5840
  throw new TypeError("iOS release builds require mobile.ios.version in absolutejs.config.ts.");
5401
- const manifest = requireManifest(JSON.parse(await readFile6(join13(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
5841
+ const manifest = requireManifest(JSON.parse(await readFile6(join14(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
5402
5842
  if (manifest.appId !== options.config.appId)
5403
5843
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
5404
- const nativeDirectory = join13(options.config.nativeProjectDirectory, "ios");
5844
+ const nativeDirectory = join14(options.config.nativeProjectDirectory, "ios");
5405
5845
  const xcode = await resolveAbsoluteIosXcodeProject(nativeDirectory, {
5406
5846
  scheme: options.scheme,
5407
5847
  workspacePath: options.workspacePath
@@ -5414,10 +5854,10 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5414
5854
  }
5415
5855
  const stagingParent = resolve9(options.projectRoot, ".absolutejs/mobile");
5416
5856
  await mkdir4(stagingParent, { recursive: true });
5417
- const staging = await mkdtemp3(join13(stagingParent, ".ios-build-"));
5418
- const archivePath = join13(staging, "App.xcarchive");
5419
- const exportPath = join13(staging, "export");
5420
- const exportPlist = join13(staging, "ExportOptions.plist");
5857
+ const staging = await mkdtemp3(join14(stagingParent, ".ios-build-"));
5858
+ const archivePath = join14(staging, "App.xcarchive");
5859
+ const exportPath = join14(staging, "export");
5860
+ const exportPlist = join14(staging, "ExportOptions.plist");
5421
5861
  await mkdir4(exportPath, { recursive: true });
5422
5862
  await writeFile4(exportPlist, exportOptions());
5423
5863
  const run = options.run ?? defaultRun2;
@@ -5445,7 +5885,7 @@ var ABSOLUTE_IOS_RELEASE_FORMAT = 1, developmentTeamArgument = (value) => {
5445
5885
  ], { cwd: nativeDirectory, env: options.env });
5446
5886
  if (archiveExit !== 0)
5447
5887
  throw new TypeError("Xcode failed to archive the iOS app.");
5448
- const archivedApp = await findByExtension(join13(archivePath, "Products", "Applications"), ".app");
5888
+ const archivedApp = await findByExtension(join14(archivePath, "Products", "Applications"), ".app");
5449
5889
  const capture = options.capture ?? defaultCapture2;
5450
5890
  const signed = archivedApp ? capture([
5451
5891
  "codesign",
@@ -5529,7 +5969,7 @@ import {
5529
5969
  writeFile as writeFile5
5530
5970
  } from "fs/promises";
5531
5971
  import { isIP as isIP2 } from "net";
5532
- import { dirname as dirname7, isAbsolute as isAbsolute3, join as join14, relative as relative6, resolve as resolve10, sep as sep4 } from "path";
5972
+ import { dirname as dirname7, isAbsolute as isAbsolute3, join as join15, relative as relative6, resolve as resolve10, sep as sep4 } from "path";
5533
5973
  var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000, BOOT_POLL_MS = 1000, DEV_JOURNAL_FORMAT2 = 1, NATIVE_CACHE_FORMAT2 = 1, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), pathExists4 = async (path) => {
5534
5974
  try {
5535
5975
  await access7(path);
@@ -5747,14 +6187,14 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5747
6187
  const leftPro = left.name.includes("Pro") ? 1 : 0;
5748
6188
  return rightPro - leftPro;
5749
6189
  })[0], journalPaths2 = (projectRoot) => {
5750
- const root = join14(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
6190
+ const root = join15(projectRoot, ".absolutejs", "mobile", "ios-dev-session");
5751
6191
  return {
5752
- configBackup: join14(root, "capacitor-config.backup"),
5753
- infoBackup: join14(root, "Info.plist.backup"),
5754
- journal: join14(root, "journal.json"),
6192
+ configBackup: join15(root, "capacitor-config.backup"),
6193
+ infoBackup: join15(root, "Info.plist.backup"),
6194
+ journal: join15(root, "journal.json"),
5755
6195
  root
5756
6196
  };
5757
- }, nativeCachePath2 = (projectRoot) => join14(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
6197
+ }, nativeCachePath2 = (projectRoot) => join15(projectRoot, ".absolutejs", "mobile", "ios-native-cache.json"), isInside2 = (root, path) => {
5758
6198
  const value = relative6(resolve10(root), resolve10(path));
5759
6199
  return value === "" || !value.startsWith(`..${sep4}`) && value !== ".." && !isAbsolute3(value);
5760
6200
  }, parseJournal2 = (value) => {
@@ -5811,8 +6251,8 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5811
6251
  }, writeDevProjection = async (project, port, https, serverHost = "localhost") => {
5812
6252
  const paths = journalPaths2(project.projectRoot);
5813
6253
  await repairAbsoluteIosDevSession(project.projectRoot);
5814
- const nativeConfigPath = join14(project.nativeDirectory, "App", "App", "capacitor.config.json");
5815
- const infoPath = join14(project.nativeDirectory, "App", "App", "Info.plist");
6254
+ const nativeConfigPath = join15(project.nativeDirectory, "App", "App", "capacitor.config.json");
6255
+ const infoPath = join15(project.nativeDirectory, "App", "App", "Info.plist");
5816
6256
  const [configSource, infoSource] = await Promise.all([
5817
6257
  readFile7(nativeConfigPath, "utf8"),
5818
6258
  readFile7(infoPath, "utf8")
@@ -5973,12 +6413,12 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5973
6413
  ]);
5974
6414
  return result.exitCode === 0 && result.stdout.includes(project.config.appId);
5975
6415
  }, buildIosDebugApp = async (project, udid, fingerprint, run, signal) => {
5976
- const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash7("sha256").update(project.config.appId).digest("hex").slice(0, 16));
6416
+ const derivedDataPath = join15(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash7("sha256").update(project.config.appId).digest("hex").slice(0, 16));
5977
6417
  await mkdir5(derivedDataPath, { recursive: true });
5978
6418
  await requireSuccess2([
5979
6419
  project.xcodebuild,
5980
6420
  "-workspace",
5981
- join14(project.nativeDirectory, "App", "App.xcworkspace"),
6421
+ join15(project.nativeDirectory, "App", "App.xcworkspace"),
5982
6422
  "-scheme",
5983
6423
  "App",
5984
6424
  "-configuration",
@@ -5989,17 +6429,17 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
5989
6429
  derivedDataPath,
5990
6430
  "build"
5991
6431
  ], "iOS simulator build", run, { cwd: project.nativeDirectory, signal });
5992
- const appPath = join14(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
6432
+ const appPath = join15(derivedDataPath, "Build", "Products", "Debug-iphonesimulator", "App.app");
5993
6433
  if (!await pathExists4(appPath))
5994
6434
  throw new Error(`Xcode did not produce the simulator app at ${appPath}.`);
5995
6435
  return appPath;
5996
6436
  }, buildPhysicalIosDebugApp = async (project, identifier, run, signal) => {
5997
- const derivedDataPath = join14(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash7("sha256").update(project.config.appId).digest("hex").slice(0, 16));
6437
+ const derivedDataPath = join15(project.projectRoot, ".absolutejs", "mobile", "ios-derived-data", createHash7("sha256").update(project.config.appId).digest("hex").slice(0, 16));
5998
6438
  await mkdir5(derivedDataPath, { recursive: true });
5999
6439
  await requireSuccess2([
6000
6440
  project.xcodebuild,
6001
6441
  "-workspace",
6002
- join14(project.nativeDirectory, "App", "App.xcworkspace"),
6442
+ join15(project.nativeDirectory, "App", "App.xcworkspace"),
6003
6443
  "-scheme",
6004
6444
  "App",
6005
6445
  "-configuration",
@@ -6011,7 +6451,7 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
6011
6451
  "-allowProvisioningUpdates",
6012
6452
  "build"
6013
6453
  ], "iOS physical-device build (configure automatic signing and a Development Team in Xcode if this is the first run)", run, { cwd: project.nativeDirectory, signal });
6014
- const appPath = join14(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
6454
+ const appPath = join15(derivedDataPath, "Build", "Products", "Debug-iphoneos", "App.app");
6015
6455
  if (!await pathExists4(appPath))
6016
6456
  throw new Error(`Xcode did not produce the physical-device app at ${appPath}.`);
6017
6457
  return appPath;
@@ -6131,16 +6571,16 @@ var ABSOLUTE_IOS_SIMULATOR_NAME = "AbsoluteJS iPhone", BOOT_TIMEOUT_MS = 180000,
6131
6571
  const xcodebuild = checks.find((check) => check.id === "ios.xcodebuild")?.path;
6132
6572
  if (!xcrun || !xcodebuild)
6133
6573
  throw new Error("Xcode tools disappeared after readiness checks.");
6134
- const cap = join14(projectRoot, "node_modules", ".bin", "cap");
6574
+ const cap = join15(projectRoot, "node_modules", ".bin", "cap");
6135
6575
  if (!await pathExists4(cap))
6136
6576
  throw new Error("Capacitor CLI is not installed. Run absolute mobile init first.");
6137
6577
  await writeAbsoluteCapacitorConfig(config, { projectRoot });
6138
6578
  await mkdir5(config.bundleDirectory, { recursive: true });
6139
- const placeholder = join14(config.bundleDirectory, "index.html");
6579
+ const placeholder = join15(config.bundleDirectory, "index.html");
6140
6580
  if (!await pathExists4(placeholder))
6141
6581
  await writeFile5(placeholder, `<!doctype html><title>AbsoluteJS mobile development</title>
6142
6582
  `);
6143
- const nativeDirectory = join14(config.nativeProjectDirectory, "ios");
6583
+ const nativeDirectory = join15(config.nativeProjectDirectory, "ios");
6144
6584
  if (!await pathExists4(nativeDirectory)) {
6145
6585
  if (!options.createNativeProject)
6146
6586
  throw new Error("iOS native project has not been created.");
@@ -6437,13 +6877,13 @@ import { isIP as isIP3 } from "net";
6437
6877
  import {
6438
6878
  dirname as dirname8,
6439
6879
  isAbsolute as isAbsolute4,
6440
- join as join15,
6880
+ join as join16,
6441
6881
  posix,
6442
6882
  relative as relative7,
6443
6883
  resolve as resolvePath,
6444
6884
  sep as sep5
6445
6885
  } from "path";
6446
- var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, REMOTE_RELEASE_LEASE_HEARTBEAT_MS = 15000, REMOTE_RELEASE_LEASE_STALE_SECONDS = 120, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join15(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
6886
+ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TIMEOUT_MS = 2000, REMOTE_RELEASE_LEASE_HEARTBEAT_MS = 15000, REMOTE_RELEASE_LEASE_STALE_SECONDS = 120, PROFILE_NAME, SSH_DESTINATION, defaultProfilePath = () => join16(homedir4(), ".absolutejs", "mobile", "remote-macs.json"), emptyStore = () => ({
6447
6887
  format: PROFILE_FORMAT,
6448
6888
  profiles: {}
6449
6889
  }), loadStore = async (path = defaultProfilePath()) => {
@@ -6619,9 +7059,9 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6619
7059
  throw new TypeError("Remote Expo execution requires mobile.engine: expo.");
6620
7060
  return createAbsoluteRemoteIosDevProject(config, projectRoot, profile);
6621
7061
  }, createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
6622
- cap: join15(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
7062
+ cap: join16(resolvePath(projectRoot), "node_modules", ".bin", "cap"),
6623
7063
  config,
6624
- nativeDirectory: join15(config.nativeProjectDirectory, "ios"),
7064
+ nativeDirectory: join16(config.nativeProjectDirectory, "ios"),
6625
7065
  profile,
6626
7066
  projectRoot: resolvePath(projectRoot),
6627
7067
  remote: true,
@@ -6772,8 +7212,8 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6772
7212
  return { ...artifact, remotePath, uploaded: true };
6773
7213
  }, materializeAbsoluteRemoteMacAgent = async (projectRoot) => {
6774
7214
  const shippedCandidates = [
6775
- join15(import.meta.dir, "remoteMacAgentEntry.js"),
6776
- join15(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
7215
+ join16(import.meta.dir, "remoteMacAgentEntry.js"),
7216
+ join16(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
6777
7217
  ];
6778
7218
  let path;
6779
7219
  for (const candidate of shippedCandidates) {
@@ -6784,13 +7224,13 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6784
7224
  }
6785
7225
  if (!path) {
6786
7226
  const sourceCandidates = [
6787
- join15(import.meta.dir, "remoteMacAgentEntry.ts"),
6788
- join15(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
7227
+ join16(import.meta.dir, "remoteMacAgentEntry.ts"),
7228
+ join16(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
6789
7229
  ];
6790
7230
  const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
6791
7231
  if (!source)
6792
7232
  throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
6793
- const outdir = join15(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
7233
+ const outdir = join16(resolvePath(projectRoot), ".absolutejs", "mobile", "remote-agent");
6794
7234
  await mkdir6(outdir, { recursive: true });
6795
7235
  const result = await Bun.build({
6796
7236
  entrypoints: [source],
@@ -6800,7 +7240,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6800
7240
  });
6801
7241
  if (!result.success)
6802
7242
  throw new AggregateError(result.logs, "Failed to build the AbsoluteJS remote Mac agent.");
6803
- path = join15(outdir, "remoteMacAgentEntry.js");
7243
+ path = join16(outdir, "remoteMacAgentEntry.js");
6804
7244
  }
6805
7245
  const bytes = await Bun.file(path).arrayBuffer();
6806
7246
  const sha256 = createHash8("sha256").update(new Uint8Array(bytes)).digest("hex");
@@ -6978,9 +7418,9 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6978
7418
  }
6979
7419
  }, retrieveAbsoluteRemoteIosRelease = async (project, metadata, outputDirectory) => {
6980
7420
  const releaseRoot = posix.join(project.remoteProjectRoot, ".absolutejs", "mobile", "releases", "ios", metadata.releaseId);
6981
- const stagingParent = join15(project.projectRoot, ".absolutejs", "mobile");
7421
+ const stagingParent = join16(project.projectRoot, ".absolutejs", "mobile");
6982
7422
  await mkdir6(stagingParent, { recursive: true });
6983
- const staging = await mkdtemp4(join15(stagingParent, ".remote-ios-"));
7423
+ const staging = await mkdtemp4(join16(stagingParent, ".remote-ios-"));
6984
7424
  try {
6985
7425
  const remoteMetadata = requireRemoteSuccess(await defaultTransport.capture([
6986
7426
  ...absoluteRemoteMacSshBase(project.profile),
@@ -6990,7 +7430,7 @@ var PROFILE_FORMAT = 1, REMOTE_STDIN_FLUSH_ATTEMPTS = 3, REMOTE_SESSION_CLOSE_TI
6990
7430
  const downloadedMetadata = JSON.parse(remoteMetadata);
6991
7431
  if (JSON.stringify(requireAbsoluteIosReleaseMetadata(downloadedMetadata)) !== JSON.stringify(metadata))
6992
7432
  throw new TypeError("Remote iOS release metadata changed during retrieval.");
6993
- const artifactPath = join15(staging, "App.ipa");
7433
+ const artifactPath = join16(staging, "App.ipa");
6994
7434
  const download = Bun.spawn([
6995
7435
  ...absoluteRemoteMacSshBase(project.profile),
6996
7436
  "/bin/sh -lc",
@@ -7528,7 +7968,7 @@ import {
7528
7968
  import { X509Certificate as X509Certificate3 } from "crypto";
7529
7969
  import { isIP as isIP4 } from "net";
7530
7970
  import { platform as platform2 } from "os";
7531
- import { join as join16 } from "path";
7971
+ import { join as join17 } from "path";
7532
7972
  var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE_HOSTS, CERTIFICATE_HOSTNAME_PATTERN, devLog = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[36m[dev]\x1B[0m ${msg}`), devWarn = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[33m[dev]\x1B[0m \x1B[33m${msg}\x1B[0m`), certFilesExist = () => existsSync5(CERT_PATH) && existsSync5(KEY_PATH), normalizeDevCertificateHosts = (hosts = []) => {
7533
7973
  const normalized = new Set(DEFAULT_CERTIFICATE_HOSTS);
7534
7974
  for (const host2 of hosts) {
@@ -7741,7 +8181,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
7741
8181
  }
7742
8182
  }, mkcertCaRoot = () => runCapture(["mkcert", "-CAROOT"]), getDevCertificateAuthorityPath = () => {
7743
8183
  const caRoot = hasMkcert() ? mkcertCaRoot() : null;
7744
- const rootCertificate = caRoot ? join16(caRoot, "rootCA.pem") : null;
8184
+ const rootCertificate = caRoot ? join17(caRoot, "rootCA.pem") : null;
7745
8185
  if (rootCertificate && existsSync5(rootCertificate))
7746
8186
  return rootCertificate;
7747
8187
  if (certFilesExist())
@@ -7756,13 +8196,13 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
7756
8196
  const caRoot = mkcertCaRoot();
7757
8197
  if (!caRoot)
7758
8198
  return false;
7759
- const rootCa = join16(caRoot, "rootCA.pem");
8199
+ const rootCa = join17(caRoot, "rootCA.pem");
7760
8200
  if (!existsSync5(rootCa))
7761
8201
  return false;
7762
8202
  const winTemp = windowsTempDir();
7763
8203
  if (!winTemp)
7764
8204
  return false;
7765
- const staged = join16(winTemp, "absolutejs-mkcert-rootCA.crt");
8205
+ const staged = join17(winTemp, "absolutejs-mkcert-rootCA.crt");
7766
8206
  try {
7767
8207
  copyFileSync(rootCa, staged);
7768
8208
  } catch {
@@ -7798,7 +8238,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
7798
8238
  devLog("Trusted the local CA in the Windows store \u2014 Chrome/Edge on Windows now accept dev HTTPS");
7799
8239
  } else {
7800
8240
  const caRoot = mkcertCaRoot();
7801
- const hint = caRoot ? toWindowsPath(join16(caRoot, "rootCA.pem")) : null;
8241
+ const hint = caRoot ? toWindowsPath(join17(caRoot, "rootCA.pem")) : null;
7802
8242
  devWarn("Could not auto-trust the local CA on Windows; Windows browsers may warn.");
7803
8243
  if (hint) {
7804
8244
  console.log(` Run in PowerShell: Import-Certificate -FilePath "${hint}" -CertStoreLocation Cert:\\CurrentUser\\Root`);
@@ -7814,9 +8254,9 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, DEFAULT_CERTIFICATE
7814
8254
  return true;
7815
8255
  };
7816
8256
  var init_devCert = __esm(() => {
7817
- CERT_DIR = join16(process.cwd(), ".absolutejs");
7818
- CERT_PATH = join16(CERT_DIR, "cert.pem");
7819
- KEY_PATH = join16(CERT_DIR, "key.pem");
8257
+ CERT_DIR = join17(process.cwd(), ".absolutejs");
8258
+ CERT_PATH = join17(CERT_DIR, "cert.pem");
8259
+ KEY_PATH = join17(CERT_DIR, "key.pem");
7820
8260
  DEFAULT_CERTIFICATE_HOSTS = ["localhost", "127.0.0.1", "::1"];
7821
8261
  CERTIFICATE_HOSTNAME_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/u;
7822
8262
  });
@@ -8521,7 +8961,7 @@ var isRecord5 = (value) => typeof value === "object" && value !== null, getIslan
8521
8961
  var init_islands = () => {};
8522
8962
 
8523
8963
  // src/build/islandEntries.ts
8524
- import { dirname as dirname10, extname as extname2, join as join19, relative as relative10, resolve as resolve14 } from "path";
8964
+ import { dirname as dirname10, extname as extname2, join as join20, relative as relative10, resolve as resolve14 } from "path";
8525
8965
  import ts2 from "typescript";
8526
8966
  var frameworks, isRecord6 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
8527
8967
  if (isRecord6(mod.islandRegistry))
@@ -9224,7 +9664,7 @@ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-
9224
9664
  // src/mobile/buildRelease.ts
9225
9665
  import { createHash as createHash11 } from "crypto";
9226
9666
  import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
9227
- import { basename as basename7, dirname as dirname11, extname as extname4, join as join20, relative as relative11, resolve as resolve15 } from "path";
9667
+ import { basename as basename7, dirname as dirname11, extname as extname4, join as join21, relative as relative11, resolve as resolve15 } from "path";
9228
9668
  var sha256 = (bytes) => createHash11("sha256").update(bytes).digest("hex"), STATIC_SCRIPT_PATTERN, rewriteStaticScriptPaths = (source, manifest) => source.replace(STATIC_SCRIPT_PATTERN, (match, prefix, path, suffix) => {
9229
9669
  if (path.endsWith("/htmx.min.js"))
9230
9670
  return match;
@@ -9237,7 +9677,7 @@ var sha256 = (bytes) => createHash11("sha256").update(bytes).digest("hex"), STAT
9237
9677
  if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
9238
9678
  return resolvedAsset;
9239
9679
  }
9240
- return join20(buildDirectory, assetPath.replace(/^\/+/, ""));
9680
+ return join21(buildDirectory, assetPath.replace(/^\/+/, ""));
9241
9681
  }, pageFor = async (metadata, manifest, buildDirectory) => {
9242
9682
  const assetPath = manifest[metadata.bundleKey];
9243
9683
  if (!assetPath) {
@@ -9248,7 +9688,7 @@ var sha256 = (bytes) => createHash11("sha256").update(bytes).digest("hex"), STAT
9248
9688
  const source = await readFile9(resolvedAssetPath, "utf8");
9249
9689
  const rewritten = rewriteStaticScriptPaths(source, manifest);
9250
9690
  const documentHash = sha256(new TextEncoder().encode(rewritten));
9251
- resolvedAssetPath = join20(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
9691
+ resolvedAssetPath = join21(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
9252
9692
  await mkdir8(dirname11(resolvedAssetPath), { recursive: true });
9253
9693
  await writeFile7(resolvedAssetPath, rewritten);
9254
9694
  }
@@ -9465,44 +9905,44 @@ import {
9465
9905
  writeFile as writeFile8
9466
9906
  } from "fs/promises";
9467
9907
  import { existsSync as existsSync10 } from "fs";
9468
- import { basename as basename8, dirname as dirname12, extname as extname5, join as join21, relative as relative12, resolve as resolve16 } from "path";
9908
+ import { basename as basename8, dirname as dirname12, extname as extname5, join as join22, relative as relative12, resolve as resolve16 } from "path";
9469
9909
  var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-mobile-bootstrap.js", INDEX_FILE = "index.html", CLIENT_CSS_DEPENDENCY_PATTERN, CLIENT_MARKUP_DEPENDENCY_PATTERN, CAPACITOR_CLIENT_FRAMEWORKS, CLIENT_ASSET_DIRECTORIES, errorHasCode = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, shellBootstrapModule = () => {
9470
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync10);
9910
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync10);
9471
9911
  if (candidate)
9472
9912
  return candidate;
9473
9913
  throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
9474
9914
  }, shellAuthModule = () => {
9475
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellAuth.${extension}`)).find(existsSync10);
9915
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellAuth.${extension}`)).find(existsSync10);
9476
9916
  if (candidate)
9477
9917
  return candidate;
9478
9918
  throw new TypeError("AbsoluteJS mobile auth shell module is missing.");
9479
9919
  }, shellExpoAuthModule = () => {
9480
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellExpoAuth.${extension}`)).find(existsSync10);
9920
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellExpoAuth.${extension}`)).find(existsSync10);
9481
9921
  if (candidate)
9482
9922
  return candidate;
9483
9923
  throw new TypeError("AbsoluteJS Expo auth bridge module is missing.");
9484
9924
  }, shellSyncModule = () => {
9485
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellSync.${extension}`)).find(existsSync10);
9925
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellSync.${extension}`)).find(existsSync10);
9486
9926
  if (candidate)
9487
9927
  return candidate;
9488
9928
  throw new TypeError("AbsoluteJS mobile Sync shell module is missing.");
9489
9929
  }, shellExpoSyncModule = () => {
9490
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellExpoSync.${extension}`)).find(existsSync10);
9930
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellExpoSync.${extension}`)).find(existsSync10);
9491
9931
  if (candidate)
9492
9932
  return candidate;
9493
9933
  throw new TypeError("AbsoluteJS Expo Sync bridge module is missing.");
9494
9934
  }, shellPushModule = () => {
9495
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellPush.${extension}`)).find(existsSync10);
9935
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellPush.${extension}`)).find(existsSync10);
9496
9936
  if (candidate)
9497
9937
  return candidate;
9498
9938
  throw new TypeError("AbsoluteJS mobile push shell module is missing.");
9499
9939
  }, shellExpoDevicesModule = () => {
9500
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellExpoDevices.${extension}`)).find(existsSync10);
9940
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellExpoDevices.${extension}`)).find(existsSync10);
9501
9941
  if (candidate)
9502
9942
  return candidate;
9503
9943
  throw new TypeError("AbsoluteJS Expo device bridge module is missing.");
9504
9944
  }, shellUpdateModule = () => {
9505
- const candidate = ["js", "ts"].map((extension) => join21(import.meta.dir, `shellUpdate.${extension}`)).find(existsSync10);
9945
+ const candidate = ["js", "ts"].map((extension) => join22(import.meta.dir, `shellUpdate.${extension}`)).find(existsSync10);
9506
9946
  if (candidate)
9507
9947
  return candidate;
9508
9948
  throw new TypeError("AbsoluteJS mobile update shell module is missing.");
@@ -9549,8 +9989,8 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
9549
9989
  const segments = specifier.split("/");
9550
9990
  const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? "";
9551
9991
  const subpath = specifier.slice(packageName.length);
9552
- const packageDirectory = join21(resolve16(projectRoot), "node_modules", packageName);
9553
- const manifest = JSON.parse(await readFile10(join21(packageDirectory, "package.json"), "utf8"));
9992
+ const packageDirectory = join22(resolve16(projectRoot), "node_modules", packageName);
9993
+ const manifest = JSON.parse(await readFile10(join22(packageDirectory, "package.json"), "utf8"));
9554
9994
  const exports = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "exports") : undefined;
9555
9995
  const entry = typeof exports === "object" && exports !== null ? Reflect.get(exports, subpath ? `.${subpath}` : ".") : undefined;
9556
9996
  const target = importEntryTarget(entry);
@@ -9587,7 +10027,7 @@ var MANIFEST_FILE = "absolute-mobile-manifest.json", BOOTSTRAP_FILE = "absolute-
9587
10027
  const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
9588
10028
  ` : "";
9589
10029
  const capabilityOptions = shellCapabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
9590
- const entryPath = join21(staging, ".absolute-mobile-entry.ts");
10030
+ const entryPath = join22(staging, ".absolute-mobile-entry.ts");
9591
10031
  const baseAdapterModule = capacitor ? await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor") : shellExpoDevicesModule();
9592
10032
  const adapterImport = capacitor ? `import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};` : `import { createAbsoluteExpoBridgeFetch, installAbsoluteExpoWebDeviceAdapter } from ${JSON.stringify(baseAdapterModule)};`;
9593
10033
  const adapterInstall = capacitor ? `installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });` : `installAbsoluteExpoWebDeviceAdapter(${JSON.stringify(deviceCapabilities.capabilities)});`;
@@ -9621,7 +10061,7 @@ void startAbsoluteMobileShell(${shellOptions});
9621
10061
  if (!build.success || build.outputs.length !== 1) {
9622
10062
  throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
9623
10063
  }
9624
- await rename7(build.outputs[0]?.path ?? "", join21(staging, BOOTSTRAP_FILE));
10064
+ await rename7(build.outputs[0]?.path ?? "", join22(staging, BOOTSTRAP_FILE));
9625
10065
  await rm7(entryPath, { force: true });
9626
10066
  }, removePreviousBundle = async (backup, moved) => {
9627
10067
  if (!moved)
@@ -9655,17 +10095,17 @@ void startAbsoluteMobileShell(${shellOptions});
9655
10095
  const extension = extname5(page.bundlePath) || ".js";
9656
10096
  const localBundlePath = `./pages/${page.bundleHash}${extension}`;
9657
10097
  const source = sourceAssetPath(buildDirectory, page.bundlePath);
9658
- await copyFile4(source, join21(staging, localBundlePath));
10098
+ await copyFile4(source, join22(staging, localBundlePath));
9659
10099
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
9660
10100
  let localStylePath;
9661
10101
  if (page.styleBundlePath && page.styleBundleHash) {
9662
10102
  const styleExtension = extname5(page.styleBundlePath) || ".css";
9663
10103
  localStylePath = `./styles/${page.styleBundleHash}${styleExtension}`;
9664
10104
  const styleSource = sourceAssetPath(buildDirectory, page.styleBundlePath);
9665
- await mkdir9(dirname12(join21(staging, localStylePath)), {
10105
+ await mkdir9(dirname12(join22(staging, localStylePath)), {
9666
10106
  recursive: true
9667
10107
  });
9668
- await copyFile4(styleSource, join21(staging, localStylePath));
10108
+ await copyFile4(styleSource, join22(staging, localStylePath));
9669
10109
  await copyAbsoluteClientDependencies(styleSource, buildDirectory, staging, copiedDependencies);
9670
10110
  }
9671
10111
  return {
@@ -9680,11 +10120,11 @@ void startAbsoluteMobileShell(${shellOptions});
9680
10120
  return value;
9681
10121
  };
9682
10122
  const release = safeLeaf(appBuild, "Mobile app build");
9683
- const privateRoot = join21(projectRoot, "sourcemaps");
9684
- const releaseRoot = join21(privateRoot, "mobile", release);
10123
+ const privateRoot = join22(projectRoot, "sourcemaps");
10124
+ const releaseRoot = join22(privateRoot, "mobile", release);
9685
10125
  const copied = [];
9686
10126
  const available = pages.flatMap((page) => {
9687
- const source = join21(privateRoot, `${basename8(page.bundlePath)}.map`);
10127
+ const source = join22(privateRoot, `${basename8(page.bundlePath)}.map`);
9688
10128
  return existsSync10(source) ? [{ page, source }] : [];
9689
10129
  });
9690
10130
  if (available.length > 0)
@@ -9695,11 +10135,11 @@ void startAbsoluteMobileShell(${shellOptions});
9695
10135
  throw new TypeError("Mobile page extension is not a safe source-map key.");
9696
10136
  const bundleHash = safeLeaf(page.bundleHash, "Mobile page bundle hash");
9697
10137
  const file = `${bundleHash}${extension}.map`;
9698
- await copyFile4(source, join21(releaseRoot, file));
10138
+ await copyFile4(source, join22(releaseRoot, file));
9699
10139
  copied.push({ bundleHash, file, pageId: page.pageId });
9700
10140
  }));
9701
10141
  if (copied.length > 0) {
9702
- await writeFile8(join21(releaseRoot, "manifest.json"), `${JSON.stringify({
10142
+ await writeFile8(join22(releaseRoot, "manifest.json"), `${JSON.stringify({
9703
10143
  appBuild,
9704
10144
  format: 1,
9705
10145
  pages: copied.sort((left, right) => left.pageId.localeCompare(right.pageId))
@@ -9744,7 +10184,7 @@ void startAbsoluteMobileShell(${shellOptions});
9744
10184
  return;
9745
10185
  copied.add(specifier);
9746
10186
  const source = sourceAssetPath(buildDirectory, specifier);
9747
- const destination = join21(staging, specifier.replace(/^\/+/, ""));
10187
+ const destination = join22(staging, specifier.replace(/^\/+/, ""));
9748
10188
  await mkdir9(dirname12(destination), { recursive: true });
9749
10189
  await copyFile4(source, destination);
9750
10190
  await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
@@ -9757,13 +10197,13 @@ void startAbsoluteMobileShell(${shellOptions});
9757
10197
  }
9758
10198
  const destination = options.config.bundleDirectory;
9759
10199
  await mkdir9(dirname12(destination), { recursive: true });
9760
- const staging = await mkdtemp5(join21(dirname12(destination), `.${basename8(destination)}.stage-`));
10200
+ const staging = await mkdtemp5(join22(dirname12(destination), `.${basename8(destination)}.stage-`));
9761
10201
  try {
9762
- const pageDirectory = join21(staging, "pages");
10202
+ const pageDirectory = join22(staging, "pages");
9763
10203
  await mkdir9(pageDirectory, { recursive: true });
9764
10204
  await Promise.all(CLIENT_ASSET_DIRECTORIES.map((directory) => ({
9765
- destination: join21(staging, directory),
9766
- source: join21(options.buildDirectory, directory)
10205
+ destination: join22(staging, directory),
10206
+ source: join22(options.buildDirectory, directory)
9767
10207
  })).filter(({ source }) => existsSync10(source)).map(({ destination: assetDestination, source }) => cp3(source, assetDestination, { recursive: true })));
9768
10208
  const copiedDependencies = new Set;
9769
10209
  const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
@@ -9800,9 +10240,9 @@ void startAbsoluteMobileShell(${shellOptions});
9800
10240
  } : {}
9801
10241
  };
9802
10242
  await Promise.all([
9803
- writeFile8(join21(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
10243
+ writeFile8(join22(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
9804
10244
  `),
9805
- writeFile8(join21(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
10245
+ writeFile8(join22(staging, INDEX_FILE), indexHtml(options.config.appName, options.config.productionOrigin)),
9806
10246
  buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true, `absolutejs.${options.auth?.clientId ?? options.config.appId}.`, options.config.engine, options.config.engine === "capacitor" && options.config.updates !== undefined, options.deviceCapabilities, options.projectRoot)
9807
10247
  ]);
9808
10248
  await installBundle(staging, destination);
@@ -9859,7 +10299,7 @@ import {
9859
10299
  rm as rm8,
9860
10300
  writeFile as writeFile9
9861
10301
  } from "fs/promises";
9862
- import { dirname as dirname13, join as join22, resolve as resolvePath3 } from "path";
10302
+ import { dirname as dirname13, join as join23, resolve as resolvePath3 } from "path";
9863
10303
  var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "current.json", BUNDLES_DIRECTORY = "bundles", ARTIFACT_FILE = "artifact.json", BUNDLE_ID_PATTERN, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code, bundleIdFor = (currentReleaseId, releases) => {
9864
10304
  const identity = JSON.stringify({
9865
10305
  currentReleaseId,
@@ -9889,16 +10329,16 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
9889
10329
  releases
9890
10330
  };
9891
10331
  }, writeRelease = async (root, release) => {
9892
- const directory = join22(root, release.artifact.releaseId);
9893
- const producerPath = join22(directory, release.artifact.producer.module);
10332
+ const directory = join23(root, release.artifact.releaseId);
10333
+ const producerPath = join23(directory, release.artifact.producer.module);
9894
10334
  await mkdir10(dirname13(producerPath), { recursive: true });
9895
10335
  await Promise.all([
9896
- writeFile9(join22(directory, ARTIFACT_FILE), `${JSON.stringify(release.artifact, null, "\t")}
10336
+ writeFile9(join23(directory, ARTIFACT_FILE), `${JSON.stringify(release.artifact, null, "\t")}
9897
10337
  `),
9898
10338
  writeFile9(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
9899
10339
  ]);
9900
10340
  }, installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
9901
- const destination = join22(bundlesRoot, bundleId);
10341
+ const destination = join23(bundlesRoot, bundleId);
9902
10342
  try {
9903
10343
  await access8(destination);
9904
10344
  return destination;
@@ -9906,7 +10346,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
9906
10346
  if (!errorHasCode2(error, "ENOENT"))
9907
10347
  throw error;
9908
10348
  }
9909
- const staging = await mkdtemp6(join22(bundlesRoot, ".stage-"));
10349
+ const staging = await mkdtemp6(join23(bundlesRoot, ".stage-"));
9910
10350
  try {
9911
10351
  await Promise.all(releases.map((release) => writeRelease(staging, release)));
9912
10352
  await rename8(staging, destination);
@@ -9935,7 +10375,7 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
9935
10375
  return release;
9936
10376
  });
9937
10377
  const root = resolvePath3(input.root);
9938
- const bundlesRoot = join22(root, BUNDLES_DIRECTORY);
10378
+ const bundlesRoot = join23(root, BUNDLES_DIRECTORY);
9939
10379
  await mkdir10(bundlesRoot, { recursive: true });
9940
10380
  const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
9941
10381
  await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
@@ -9945,8 +10385,8 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
9945
10385
  format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
9946
10386
  releases: artifacts
9947
10387
  };
9948
- const pointerPath = join22(root, CURRENT_BUNDLE_FILE);
9949
- const temporaryPointerPath = join22(root, `.current-${crypto.randomUUID()}.json`);
10388
+ const pointerPath = join23(root, CURRENT_BUNDLE_FILE);
10389
+ const temporaryPointerPath = join23(root, `.current-${crypto.randomUUID()}.json`);
9950
10390
  await writeFile9(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
9951
10391
  `, { flag: "wx" });
9952
10392
  await rename8(temporaryPointerPath, pointerPath);
@@ -9954,12 +10394,12 @@ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1, CURRENT_BUNDLE_FILE = "curre
9954
10394
  }, readAbsoluteMobileMaterializedReleases = async (root) => {
9955
10395
  const resolvedRoot = resolvePath3(root);
9956
10396
  try {
9957
- const serialized = await readFile11(join22(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
10397
+ const serialized = await readFile11(join23(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
9958
10398
  const parsed = JSON.parse(serialized);
9959
10399
  const index = parseBundleIndex(parsed);
9960
- const bundleRoot = join22(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
10400
+ const bundleRoot = join23(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
9961
10401
  return Promise.all(index.releases.map(async (artifact) => {
9962
- const producer = Bun.file(join22(bundleRoot, artifact.releaseId, artifact.producer.module));
10402
+ const producer = Bun.file(join23(bundleRoot, artifact.releaseId, artifact.producer.module));
9963
10403
  await verifyAbsoluteMobileCompatibilityProducer({
9964
10404
  artifact,
9965
10405
  producer
@@ -9980,7 +10420,7 @@ var init_materializedBundle = __esm(() => {
9980
10420
 
9981
10421
  // src/mobile/buildPipeline.ts
9982
10422
  import { readFile as readFile12 } from "fs/promises";
9983
- import { join as join23, resolve as resolve17 } from "path";
10423
+ import { join as join24, resolve as resolve17 } from "path";
9984
10424
  import { pathToFileURL } from "url";
9985
10425
  var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes")), isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string"), serverExportName = (loaded, app) => {
9986
10426
  if (loaded.server === app)
@@ -10011,9 +10451,9 @@ var isElysiaApp = (value) => typeof value === "object" && value !== null && type
10011
10451
  }, finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
10012
10452
  const buildDirectory = resolve17(options.buildDirectory);
10013
10453
  const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
10014
- const root = join23(buildDirectory, ".absolutejs", "mobile-compatibility");
10454
+ const root = join24(buildDirectory, ".absolutejs", "mobile-compatibility");
10015
10455
  const [manifestSource, previous] = await Promise.all([
10016
- readFile12(join23(buildDirectory, "manifest.json"), "utf8"),
10456
+ readFile12(join24(buildDirectory, "manifest.json"), "utf8"),
10017
10457
  readAbsoluteMobileMaterializedReleases(root)
10018
10458
  ]);
10019
10459
  const manifest = JSON.parse(manifestSource);
@@ -10681,7 +11121,7 @@ __export(exports_prerender, {
10681
11121
  routeToFilename: () => routeToFilename
10682
11122
  });
10683
11123
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync15 } from "fs";
10684
- import { join as join24 } from "path";
11124
+ import { join as join25 } from "path";
10685
11125
  var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_TIMEOUT_MS = 30000, DEFAULT_FETCH_TIMEOUT_MS = 1e4, PRERENDER_BYPASS_HEADER = "X-Absolute-Prerender-Bypass", routeToFilename = (route) => route === "/" ? "index.html" : `${route.slice(1).replace(/\//g, "-")}.html`, writeTimestamp = async (htmlPath) => {
10686
11126
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
10687
11127
  await Bun.write(metaPath, String(Date.now()));
@@ -10751,7 +11191,7 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
10751
11191
  if (!isCompleteHtml(html))
10752
11192
  return false;
10753
11193
  const fileName = routeToFilename(route);
10754
- const filePath = join24(prerenderDir, fileName);
11194
+ const filePath = join25(prerenderDir, fileName);
10755
11195
  await Bun.write(filePath, html);
10756
11196
  await writeTimestamp(filePath);
10757
11197
  return true;
@@ -10781,13 +11221,13 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
10781
11221
  return;
10782
11222
  }
10783
11223
  const fileName = routeToFilename(route);
10784
- const filePath = join24(prerenderDir, fileName);
11224
+ const filePath = join25(prerenderDir, fileName);
10785
11225
  await Bun.write(filePath, html);
10786
11226
  await writeTimestamp(filePath);
10787
11227
  result.routes.set(route, filePath);
10788
11228
  log?.(` Pre-rendered ${route} \u2192 ${fileName} (${html.length} bytes)`);
10789
11229
  }, prerender = async (port, outDir, staticConfig, log) => {
10790
- const prerenderDir = join24(outDir, "_prerendered");
11230
+ const prerenderDir = join25(outDir, "_prerendered");
10791
11231
  mkdirSync6(prerenderDir, { recursive: true });
10792
11232
  const baseUrl = `http://localhost:${port}`;
10793
11233
  let routes;
@@ -11223,7 +11663,7 @@ var init_nativeRewrite = __esm(() => {
11223
11663
 
11224
11664
  // src/build/rewriteImportsPlugin.ts
11225
11665
  import { readdir as readdir4 } from "fs/promises";
11226
- import { join as join25 } from "path";
11666
+ import { join as join26 } from "path";
11227
11667
  var escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), jsRewriteImports = (content, replacements) => {
11228
11668
  let result = content;
11229
11669
  for (const [specifier, webPath] of replacements) {
@@ -11302,7 +11742,7 @@ ${content}`;
11302
11742
  const entries = await readdir4(dir);
11303
11743
  for (const entry of entries) {
11304
11744
  if (entry.endsWith(".js"))
11305
- allFiles.push(join25(dir, entry));
11745
+ allFiles.push(join26(dir, entry));
11306
11746
  }
11307
11747
  } catch {}
11308
11748
  }
@@ -11378,7 +11818,7 @@ var init_rewriteImports = __esm(() => {
11378
11818
  // src/cli/scripts/start.ts
11379
11819
  var {env: env2 } = globalThis.Bun;
11380
11820
  import { existsSync as existsSync12, readFileSync as readFileSync16, rmSync as rmSync4 } from "fs";
11381
- import { basename as basename9, join as join26, resolve as resolve21 } from "path";
11821
+ import { basename as basename9, join as join27, resolve as resolve21 } from "path";
11382
11822
  var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, resolvePackageVersion = (candidates) => {
11383
11823
  for (const candidate of candidates) {
11384
11824
  const version2 = readPackageVersion2(candidate);
@@ -11610,7 +12050,7 @@ var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
11610
12050
  if (!build)
11611
12051
  throw new Error("Could not locate build module");
11612
12052
  await build(buildConfig);
11613
- rmSync4(join26(resolvedOutdir, "_prerendered"), {
12053
+ rmSync4(join27(resolvedOutdir, "_prerendered"), {
11614
12054
  force: true,
11615
12055
  recursive: true
11616
12056
  });
@@ -11914,16 +12354,16 @@ __export(exports_build, {
11914
12354
  build: () => build
11915
12355
  });
11916
12356
  import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync18 } from "fs";
11917
- import { join as join27, resolve as resolve23 } from "path";
12357
+ import { join as join28, resolve as resolve23 } from "path";
11918
12358
  var PROFILE_TOP = 15, PROFILE_COL = 8, FRAMEWORK_KEYS, cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, printProfile = (buildDir) => {
11919
- const traceDir = join27(buildDir, ".absolute-trace");
12359
+ const traceDir = join28(buildDir, ".absolute-trace");
11920
12360
  if (!existsSync14(traceDir))
11921
12361
  return;
11922
12362
  const files = readdirSync3(traceDir).filter((file) => file.endsWith(".json")).sort();
11923
12363
  const latest = files[files.length - 1];
11924
12364
  if (latest === undefined)
11925
12365
  return;
11926
- const trace = JSON.parse(readFileSync18(join27(traceDir, latest), "utf-8"));
12366
+ const trace = JSON.parse(readFileSync18(join28(traceDir, latest), "utf-8"));
11927
12367
  const events = Array.isArray(trace.events) ? trace.events : [];
11928
12368
  if (events.length === 0)
11929
12369
  return;
@@ -12386,7 +12826,7 @@ __export(exports_ls, {
12386
12826
  runLs: () => runLs
12387
12827
  });
12388
12828
  import { existsSync as existsSync17, readFileSync as readFileSync20, statSync } from "fs";
12389
- import { basename as basename11, extname as extname7, join as join28, relative as relative15 } from "path";
12829
+ import { basename as basename11, extname as extname7, join as join29, relative as relative15 } from "path";
12390
12830
  var DEFAULT_BUILD_DIR = "build", LABEL_ORDER, ARTIFACT_SUFFIXES, FRAMEWORK_FIELDS, readStringField = (source, key) => {
12391
12831
  const value = Reflect.get(source, key);
12392
12832
  return typeof value === "string" ? value : undefined;
@@ -12408,13 +12848,13 @@ var DEFAULT_BUILD_DIR = "build", LABEL_ORDER, ARTIFACT_SUFFIXES, FRAMEWORK_FIELD
12408
12848
  const dir = readStringField(source, framework.field);
12409
12849
  return dir === undefined ? [] : [
12410
12850
  {
12411
- dir: join28(baseDir, dir),
12851
+ dir: join29(baseDir, dir),
12412
12852
  label: framework.label,
12413
12853
  pattern: framework.pattern
12414
12854
  }
12415
12855
  ];
12416
12856
  }), scanFramework = async (spec) => {
12417
- const { pageFiles } = await scanConventions(join28(spec.dir, "pages"), spec.pattern);
12857
+ const { pageFiles } = await scanConventions(join29(spec.dir, "pages"), spec.pattern);
12418
12858
  if (pageFiles.length === 0)
12419
12859
  return null;
12420
12860
  const pages = pageFiles.map((file) => ({
@@ -12438,10 +12878,10 @@ var DEFAULT_BUILD_DIR = "build", LABEL_ORDER, ARTIFACT_SUFFIXES, FRAMEWORK_FIELD
12438
12878
  }, resolveDiskPath = (buildDir, value) => {
12439
12879
  if (existsSync17(value))
12440
12880
  return value;
12441
- const underBuild = join28(buildDir, value);
12881
+ const underBuild = join29(buildDir, value);
12442
12882
  if (existsSync17(underBuild))
12443
12883
  return underBuild;
12444
- return join28(process.cwd(), value);
12884
+ return join29(process.cwd(), value);
12445
12885
  }, fileSize = (diskPath) => {
12446
12886
  try {
12447
12887
  return statSync(diskPath).size;
@@ -12449,7 +12889,7 @@ var DEFAULT_BUILD_DIR = "build", LABEL_ORDER, ARTIFACT_SUFFIXES, FRAMEWORK_FIELD
12449
12889
  return 0;
12450
12890
  }
12451
12891
  }, readManifestSizes = (manifestDir) => {
12452
- const manifest = JSON.parse(readFileSync20(join28(manifestDir, "manifest.json"), "utf-8"));
12892
+ const manifest = JSON.parse(readFileSync20(join29(manifestDir, "manifest.json"), "utf-8"));
12453
12893
  const sizes = new Map;
12454
12894
  Object.entries(manifest).forEach(([key, value]) => {
12455
12895
  sizes.set(key, fileSize(resolveDiskPath(manifestDir, value)));
@@ -12468,7 +12908,7 @@ var DEFAULT_BUILD_DIR = "build", LABEL_ORDER, ARTIFACT_SUFFIXES, FRAMEWORK_FIELD
12468
12908
  }))
12469
12909
  })), manifestAge = (manifestPath) => getDurationString(Date.now() - statSync(manifestPath).mtimeMs), firstBuildDir = (candidates) => candidates.map((candidate) => {
12470
12910
  const dir = readStringField(candidate.source, "buildDirectory");
12471
- return dir === undefined ? undefined : join28(candidate.baseDir, dir);
12911
+ return dir === undefined ? undefined : join29(candidate.baseDir, dir);
12472
12912
  }).find((dir) => dir !== undefined), resolveSizesDir = (args, candidates) => parseFlagValue(args, "--outdir") ?? firstBuildDir(candidates) ?? DEFAULT_BUILD_DIR, formatSize = (bytes) => {
12473
12913
  if (bytes === null || bytes === 0)
12474
12914
  return "-";
@@ -12566,7 +13006,7 @@ ${colors.dim}${frameworkCount} ${frameworkCount === 1 ? "framework" : "framework
12566
13006
  return;
12567
13007
  }
12568
13008
  const sizesDir = resolveSizesDir(args, candidates);
12569
- const manifestPath = join28(sizesDir, "manifest.json");
13009
+ const manifestPath = join29(sizesDir, "manifest.json");
12570
13010
  if (!existsSync17(manifestPath)) {
12571
13011
  printDim(`No build at ${relativeOrSelf(manifestPath)}. Run \`absolute build\` first, or pass \`--outdir <dir>\`.`);
12572
13012
  return;
@@ -14033,7 +14473,7 @@ var init_frameworks = __esm(() => {
14033
14473
  });
14034
14474
 
14035
14475
  // src/cli/generate/context.ts
14036
- import { dirname as dirname17, isAbsolute as isAbsolute5, join as join29, relative as relative16, resolve as resolve27 } from "path";
14476
+ import { dirname as dirname17, isAbsolute as isAbsolute5, join as join30, relative as relative16, resolve as resolve27 } from "path";
14037
14477
  var asString = (value) => typeof value === "string" ? value : undefined, isRecord11 = (value) => typeof value === "object" && value !== null, resolveDir = (cwd, value) => isAbsolute5(value) ? value : resolve27(cwd, value), resolveStylesDir = (cwd, config) => {
14038
14478
  const styles = config.stylesConfig;
14039
14479
  if (typeof styles === "string")
@@ -14096,7 +14536,7 @@ var asString = (value) => typeof value === "string" ? value : undefined, isRecor
14096
14536
  message: `Multiple frameworks configured (${configured.join(", ")}). Pass --framework <name>.`,
14097
14537
  ok: false
14098
14538
  };
14099
- }, sharedDirFor = (project, framework) => join29(frontendRootFor(project, framework), "shared"), toModuleSpecifier = (fromDir, toFileNoExt) => {
14539
+ }, sharedDirFor = (project, framework) => join30(frontendRootFor(project, framework), "shared"), toModuleSpecifier = (fromDir, toFileNoExt) => {
14100
14540
  const rel = relative16(fromDir, toFileNoExt).split("\\").join("/");
14101
14541
  return rel.startsWith(".") ? rel : `./${rel}`;
14102
14542
  };
@@ -14126,7 +14566,7 @@ var emptyOutcome = () => ({
14126
14566
  // src/cli/generate/routeWiring.ts
14127
14567
  import ts8 from "typescript";
14128
14568
  import { existsSync as existsSync21, readFileSync as readFileSync24, readdirSync as readdirSync4, writeFileSync as writeFileSync9 } from "fs";
14129
- import { dirname as dirname18, join as join30 } from "path";
14569
+ import { dirname as dirname18, join as join31 } from "path";
14130
14570
  var DEFAULT_SEPARATOR = `
14131
14571
  `, BOUNDARY_USE, applyEdits = (text2, edits) => {
14132
14572
  const ordered = [...edits].sort((first, second) => second.start - first.start);
@@ -14285,14 +14725,14 @@ ${newLines.join(`
14285
14725
  for (const name of readdirSync4(pluginsDir)) {
14286
14726
  if (!name.endsWith(".ts"))
14287
14727
  continue;
14288
- const candidate = join30(pluginsDir, name);
14728
+ const candidate = join31(pluginsDir, name);
14289
14729
  if (hasChain(candidate))
14290
14730
  return candidate;
14291
14731
  }
14292
14732
  return null;
14293
14733
  }, findRoutingFile = (serverEntry) => {
14294
- const pluginsDir = join30(dirname18(serverEntry), "plugins");
14295
- const preferred = join30(pluginsDir, "pagesPlugin.ts");
14734
+ const pluginsDir = join31(dirname18(serverEntry), "plugins");
14735
+ const preferred = join31(pluginsDir, "pagesPlugin.ts");
14296
14736
  if (hasChain(preferred))
14297
14737
  return preferred;
14298
14738
  const scanned = firstChainFile(pluginsDir);
@@ -14384,7 +14824,7 @@ var init_routeWiring = __esm(() => {
14384
14824
 
14385
14825
  // src/cli/generate/generateApi.ts
14386
14826
  import { existsSync as existsSync22, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
14387
- import { dirname as dirname19, join as join31 } from "path";
14827
+ import { dirname as dirname19, join as join32 } from "path";
14388
14828
  var apiPluginTemplate = (pluginName, base) => `import { Elysia } from 'elysia';
14389
14829
 
14390
14830
  export const ${pluginName} = new Elysia()
@@ -14396,8 +14836,8 @@ export const ${pluginName} = new Elysia()
14396
14836
  const pluginName = `${camel}Plugin`;
14397
14837
  const base = `/api/${kebab}`;
14398
14838
  const outcome = { ...emptyOutcome(), route: base };
14399
- const pluginsDir = join31(dirname19(project.serverEntry), "plugins");
14400
- const fileAbs = join31(pluginsDir, `${pluginName}.ts`);
14839
+ const pluginsDir = join32(dirname19(project.serverEntry), "plugins");
14840
+ const fileAbs = join32(pluginsDir, `${pluginName}.ts`);
14401
14841
  if (existsSync22(fileAbs)) {
14402
14842
  outcome.notes.push(`${pluginName} already exists at ${fileAbs} \u2014 skipped.`);
14403
14843
  return outcome;
@@ -14472,7 +14912,7 @@ var init_componentTemplates = __esm(() => {
14472
14912
 
14473
14913
  // src/cli/generate/generateComponent.ts
14474
14914
  import { existsSync as existsSync23, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
14475
- import { dirname as dirname20, join as join32 } from "path";
14915
+ import { dirname as dirname20, join as join33 } from "path";
14476
14916
  var generateComponent = (project, framework, rawName) => {
14477
14917
  const def = frameworks6[framework];
14478
14918
  const pascal = toPascalCase(rawName);
@@ -14483,7 +14923,7 @@ var generateComponent = (project, framework, rawName) => {
14483
14923
  outcome.manual = { reason: "framework directory missing", snippet: "" };
14484
14924
  return outcome;
14485
14925
  }
14486
- const fileAbs = join32(frameworkDir, "components", def.componentFile({ kebab, pascal }));
14926
+ const fileAbs = join33(frameworkDir, "components", def.componentFile({ kebab, pascal }));
14487
14927
  if (existsSync23(fileAbs)) {
14488
14928
  outcome.notes.push(`${pascal} already exists at ${fileAbs} \u2014 skipped.`);
14489
14929
  return outcome;
@@ -14505,7 +14945,7 @@ var init_generateComponent = __esm(() => {
14505
14945
  // src/cli/generate/cssStrategy.ts
14506
14946
  import ts9 from "typescript";
14507
14947
  import { existsSync as existsSync24 } from "fs";
14508
- import { join as join33 } from "path";
14948
+ import { join as join34 } from "path";
14509
14949
  var CSS_SUFFIX = "CSS", SHARED_MIN_USES = 2, DEFAULT_CSS = `main {
14510
14950
  margin: 0 auto;
14511
14951
  max-width: 64rem;
@@ -14544,7 +14984,7 @@ var CSS_SUFFIX = "CSS", SHARED_MIN_USES = 2, DEFAULT_CSS = `main {
14544
14984
  return null;
14545
14985
  }, fileForKey = (stylesDir, assetKey2) => {
14546
14986
  const base = assetKey2.endsWith(CSS_SUFFIX) ? assetKey2.slice(0, -CSS_SUFFIX.length) : assetKey2;
14547
- return join33(stylesDir, `${toKebabCase(base)}.css`);
14987
+ return join34(stylesDir, `${toKebabCase(base)}.css`);
14548
14988
  }, planCss = (routingText, stylesDir, pascal, kebab) => {
14549
14989
  const sharedKey = detectSharedKey(routingText);
14550
14990
  if (sharedKey) {
@@ -14557,7 +14997,7 @@ var CSS_SUFFIX = "CSS", SHARED_MIN_USES = 2, DEFAULT_CSS = `main {
14557
14997
  shared: true
14558
14998
  };
14559
14999
  }
14560
- const cssFileAbs = join33(stylesDir, `${kebab}.css`);
15000
+ const cssFileAbs = join34(stylesDir, `${kebab}.css`);
14561
15001
  return {
14562
15002
  assetKey: `${pascal}${CSS_SUFFIX}`,
14563
15003
  contents: DEFAULT_CSS,
@@ -14818,14 +15258,14 @@ import {
14818
15258
  readdirSync as readdirSync5,
14819
15259
  writeFileSync as writeFileSync13
14820
15260
  } from "fs";
14821
- import { dirname as dirname22, join as join34, relative as relative17 } from "path";
15261
+ import { dirname as dirname22, join as join35, relative as relative17 } from "path";
14822
15262
  var writeNew = (path, contents) => {
14823
15263
  mkdirSync13(dirname22(path), { recursive: true });
14824
15264
  writeFileSync13(path, contents, "utf-8");
14825
15265
  }, toHref = (fromDir, toFile) => {
14826
15266
  const rel = relative17(fromDir, toFile).split("\\").join("/");
14827
15267
  return rel.startsWith(".") ? rel : `./${rel}`;
14828
- }, staticPageFiles = (project) => ["html", "htmx"].map((key) => project.frameworkDirs[key]).map((dir) => dir ? join34(dir, "pages") : null).filter((pagesDir) => pagesDir !== null && existsSync26(pagesDir)).flatMap((pagesDir) => readdirSync5(pagesDir).filter((name) => name.endsWith(".html")).map((name) => join34(pagesDir, name))), resyncPage = (file, items) => {
15268
+ }, staticPageFiles = (project) => ["html", "htmx"].map((key) => project.frameworkDirs[key]).map((dir) => dir ? join35(dir, "pages") : null).filter((pagesDir) => pagesDir !== null && existsSync26(pagesDir)).flatMap((pagesDir) => readdirSync5(pagesDir).filter((name) => name.endsWith(".html")).map((name) => join35(pagesDir, name))), resyncPage = (file, items) => {
14829
15269
  const html = readFileSync26(file, "utf-8");
14830
15270
  const synced = syncStaticNav(html, items);
14831
15271
  if (synced === null || synced === html)
@@ -14853,7 +15293,7 @@ var writeNew = (path, contents) => {
14853
15293
  outcome.manual = { reason: "framework directory missing", snippet: "" };
14854
15294
  return outcome;
14855
15295
  }
14856
- const pageFileAbs = join34(frameworkDir, "pages", def.pageFile({ kebab, pascal }));
15296
+ const pageFileAbs = join35(frameworkDir, "pages", def.pageFile({ kebab, pascal }));
14857
15297
  if (existsSync26(pageFileAbs)) {
14858
15298
  outcome.notes.push(`${pascal} already exists at ${pageFileAbs} \u2014 skipped.`);
14859
15299
  return outcome;
@@ -14861,7 +15301,7 @@ var writeNew = (path, contents) => {
14861
15301
  const routingFile = findRoutingFile(project.serverEntry);
14862
15302
  const routingText = routingFile ? readFileSync26(routingFile, "utf-8") : "";
14863
15303
  const css = planCss(routingText, project.stylesDir, pascal, kebab);
14864
- const navDataPath = join34(sharedDirFor(project, framework), "navData.ts");
15304
+ const navDataPath = join35(sharedDirFor(project, framework), "navData.ts");
14865
15305
  const nav = upsertNavItem(navDataPath, { href: route, label: title });
14866
15306
  const navImportPath = toModuleSpecifier(dirname22(pageFileAbs), navDataPath.replace(/\.ts$/, ""));
14867
15307
  writeNew(pageFileAbs, pageTemplates[framework]({
@@ -15241,9 +15681,9 @@ var init_catalog = __esm(() => {
15241
15681
 
15242
15682
  // src/cli/integrations/addPlugin.ts
15243
15683
  import { existsSync as existsSync27, readFileSync as readFileSync28 } from "fs";
15244
- import { join as join35 } from "path";
15684
+ import { join as join36 } from "path";
15245
15685
  var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readPackageJson = (cwd) => {
15246
- const path = join35(cwd, "package.json");
15686
+ const path = join36(cwd, "package.json");
15247
15687
  if (!existsSync27(path))
15248
15688
  return null;
15249
15689
  try {
@@ -15845,7 +16285,7 @@ var init_resolveAuthSettings = __esm(() => {
15845
16285
  // src/cli/config/auth/resolveAuthState.ts
15846
16286
  import ts13 from "typescript";
15847
16287
  import { existsSync as existsSync29, readdirSync as readdirSync6, readFileSync as readFileSync30 } from "fs";
15848
- import { join as join36, relative as relative19, resolve as resolve29 } from "path";
16288
+ import { join as join37, relative as relative19, resolve as resolve29 } from "path";
15849
16289
  var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs/absolute-auth", NPM_URL = "https://www.npmjs.com/package/@absolutejs/auth", SKIP_DIRS, MAX_FILES = 4000, SETUP_EXPORTS, isRecord13 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson2 = (path) => {
15850
16290
  if (!existsSync29(path))
15851
16291
  return null;
@@ -15859,7 +16299,7 @@ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutej
15859
16299
  const value = record?.[key];
15860
16300
  return typeof value === "string" ? value : null;
15861
16301
  }, declaredVersionFor = (cwd) => {
15862
- const pkg = readJson2(join36(cwd, "package.json"));
16302
+ const pkg = readJson2(join37(cwd, "package.json"));
15863
16303
  if (!pkg)
15864
16304
  return null;
15865
16305
  for (const field of ["dependencies", "devDependencies"]) {
@@ -15871,14 +16311,14 @@ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutej
15871
16311
  return version2;
15872
16312
  }
15873
16313
  return null;
15874
- }, installedVersionFor = (cwd) => stringField(readJson2(join36(cwd, "node_modules", AUTH_PACKAGE2, "package.json")), "version"), SOURCE_FILE, safeReaddir = (dir) => {
16314
+ }, installedVersionFor = (cwd) => stringField(readJson2(join37(cwd, "node_modules", AUTH_PACKAGE2, "package.json")), "version"), SOURCE_FILE, safeReaddir = (dir) => {
15875
16315
  try {
15876
16316
  return readdirSync6(dir, { withFileTypes: true });
15877
16317
  } catch {
15878
16318
  return [];
15879
16319
  }
15880
16320
  }, sortEntry = (dir, entry, found, dirs) => {
15881
- const full = join36(dir, entry.name);
16321
+ const full = join37(dir, entry.name);
15882
16322
  if (entry.isDirectory()) {
15883
16323
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
15884
16324
  return;
@@ -15976,7 +16416,7 @@ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutej
15976
16416
  scaffoldable: isScaffoldableFeature(feature.id)
15977
16417
  })), resolveAuthState = (cwd) => {
15978
16418
  const installedVersion = installedVersionFor(cwd);
15979
- const root = existsSync29(join36(cwd, "src")) ? join36(cwd, "src") : cwd;
16419
+ const root = existsSync29(join37(cwd, "src")) ? join37(cwd, "src") : cwd;
15980
16420
  let match = null;
15981
16421
  let setupPath = null;
15982
16422
  for (const file of candidateFiles(root)) {
@@ -16022,7 +16462,7 @@ var init_resolveAuthState = __esm(() => {
16022
16462
 
16023
16463
  // src/cli/config/auth/scaffoldAuthFeature.ts
16024
16464
  import { existsSync as existsSync30, writeFileSync as writeFileSync15 } from "fs";
16025
- import { dirname as dirname23, join as join37, relative as relative20, resolve as resolve30 } from "path";
16465
+ import { dirname as dirname23, join as join38, relative as relative20, resolve as resolve30 } from "path";
16026
16466
  var renderScaffold = (scaffold) => {
16027
16467
  const importNames = [...scaffold.imports, `type ${scaffold.typeName}`];
16028
16468
  const importLine = `import { ${importNames.join(", ")} } from '@absolutejs/auth';`;
@@ -16048,7 +16488,7 @@ ${body}
16048
16488
  const { setupPath } = resolveAuthState(cwd);
16049
16489
  if (setupPath)
16050
16490
  return dirname23(resolve30(cwd, setupPath));
16051
- const src = join37(cwd, "src");
16491
+ const src = join38(cwd, "src");
16052
16492
  return existsSync30(src) ? src : cwd;
16053
16493
  }, spreadFor = (scaffold) => `import { ${scaffold.exportName} } from './${scaffold.exportName}';
16054
16494
  // add to your auth() call:
@@ -16062,7 +16502,7 @@ ${scaffold.configKey}: ${scaffold.exportName}`, failure2 = (message) => ({
16062
16502
  const scaffold = AUTH_SCAFFOLDS[id];
16063
16503
  if (!scaffold)
16064
16504
  return failure2(`Unknown auth feature "${id}".`);
16065
- const filePath = join37(targetDir(cwd), `${scaffold.exportName}.ts`);
16505
+ const filePath = join38(targetDir(cwd), `${scaffold.exportName}.ts`);
16066
16506
  const relPath = relative20(cwd, filePath);
16067
16507
  if (existsSync30(filePath)) {
16068
16508
  return {
@@ -16092,11 +16532,11 @@ var init_scaffoldAuthFeature = __esm(() => {
16092
16532
 
16093
16533
  // src/cli/htmx/install.ts
16094
16534
  import { existsSync as existsSync31, mkdirSync as mkdirSync14, readFileSync as readFileSync31, writeFileSync as writeFileSync16 } from "fs";
16095
- import { join as join38 } from "path";
16535
+ import { join as join39 } from "path";
16096
16536
  var VENDORED_HTMX_VERSION = "2.0.6", vendoredHtmxFile = () => [
16097
- join38(import.meta.dir, "htmx.min.js"),
16098
- join38(import.meta.dir, "htmx", "htmx.min.js"),
16099
- join38(import.meta.dir, "..", "htmx", "htmx.min.js")
16537
+ join39(import.meta.dir, "htmx.min.js"),
16538
+ join39(import.meta.dir, "htmx", "htmx.min.js"),
16539
+ join39(import.meta.dir, "..", "htmx", "htmx.min.js")
16100
16540
  ].find((path) => existsSync31(path)) ?? null, detectHtmxVersion = (content) => {
16101
16541
  const match = content.match(/version:"([0-9.]+)"/);
16102
16542
  return match ? match[1] : null;
@@ -16108,7 +16548,7 @@ var VENDORED_HTMX_VERSION = "2.0.6", vendoredHtmxFile = () => [
16108
16548
  }
16109
16549
  return response.text();
16110
16550
  }, installedHtmxVersion = (htmxDir) => {
16111
- const file = join38(htmxDir, "htmx.min.js");
16551
+ const file = join39(htmxDir, "htmx.min.js");
16112
16552
  if (!existsSync31(file))
16113
16553
  return null;
16114
16554
  return detectHtmxVersion(readFileSync31(file, "utf-8"));
@@ -16117,7 +16557,7 @@ var VENDORED_HTMX_VERSION = "2.0.6", vendoredHtmxFile = () => [
16117
16557
  return file ? readFileSync31(file, "utf-8") : null;
16118
16558
  }, writeHtmx = (htmxDir, content) => {
16119
16559
  mkdirSync14(htmxDir, { recursive: true });
16120
- const file = join38(htmxDir, "htmx.min.js");
16560
+ const file = join39(htmxDir, "htmx.min.js");
16121
16561
  writeFileSync16(file, content, "utf-8");
16122
16562
  return file;
16123
16563
  };
@@ -16128,7 +16568,7 @@ var exports_add = {};
16128
16568
  __export(exports_add, {
16129
16569
  runAdd: () => runAdd
16130
16570
  });
16131
- import { dirname as dirname24, join as join39, relative as relative21 } from "path";
16571
+ import { dirname as dirname24, join as join40, relative as relative21 } from "path";
16132
16572
  var write2 = (text2) => process.stdout.write(`${text2}
16133
16573
  `), fail2 = (message) => {
16134
16574
  process.stdout.write(`${colors.red}${message}${colors.reset}
@@ -16143,7 +16583,7 @@ var write2 = (text2) => process.stdout.write(`${text2}
16143
16583
  }, frontendRoot = (project, cwd) => {
16144
16584
  const [firstKey] = configuredFrameworks(project);
16145
16585
  const firstDir = firstKey ? project.frameworkDirs[firstKey] : undefined;
16146
- return firstDir ? dirname24(firstDir) : join39(cwd, "src", "frontend");
16586
+ return firstDir ? dirname24(firstDir) : join40(cwd, "src", "frontend");
16147
16587
  }, addIntegrationCli = (id, install) => {
16148
16588
  const result = addIntegration(process.cwd(), id, { install });
16149
16589
  if (!result.ok) {
@@ -16207,7 +16647,7 @@ var write2 = (text2) => process.stdout.write(`${text2}
16207
16647
  write2(`${colors.yellow}!${colors.reset} ${frameworks6[framework].label} is already configured \u2014 nothing to do.`);
16208
16648
  return;
16209
16649
  }
16210
- const dirAbs = join39(frontendRoot(project, cwd), framework);
16650
+ const dirAbs = join40(frontendRoot(project, cwd), framework);
16211
16651
  const dirRel = `./${relative21(cwd, dirAbs).split("\\").join("/")}`;
16212
16652
  let depNote = "Skipped dependency install (--no-install).";
16213
16653
  if (!noInstall) {
@@ -16278,7 +16718,7 @@ __export(exports_analyze, {
16278
16718
  runAnalyze: () => runAnalyze
16279
16719
  });
16280
16720
  import { existsSync as existsSync32, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync17 } from "fs";
16281
- import { join as join40, resolve as resolve31 } from "path";
16721
+ import { join as join41, resolve as resolve31 } from "path";
16282
16722
  var BASELINE_FILE = ".absolute-size-baseline.json", TOP_CHANGES = 12, CATEGORY_WIDTH = 16, SIZE_WIDTH = 12, CHANGE_WIDTH = 10, CATEGORY_ORDER, categoryOf = (key) => {
16283
16723
  if (key.startsWith("Island"))
16284
16724
  return "Islands";
@@ -16298,17 +16738,17 @@ var BASELINE_FILE = ".absolute-size-baseline.json", TOP_CHANGES = 12, CATEGORY_W
16298
16738
  return 0;
16299
16739
  }
16300
16740
  }, readSizes = (manifestDir) => {
16301
- const manifestPath = join40(manifestDir, "manifest.json");
16741
+ const manifestPath = join41(manifestDir, "manifest.json");
16302
16742
  if (!existsSync32(manifestPath))
16303
16743
  return null;
16304
16744
  const manifest = JSON.parse(readFileSync32(manifestPath, "utf-8"));
16305
16745
  const sizes = {};
16306
16746
  for (const [key, value] of Object.entries(manifest)) {
16307
- sizes[key] = fileSize2(join40(manifestDir, value.replace(/^\//, "")));
16747
+ sizes[key] = fileSize2(join41(manifestDir, value.replace(/^\//, "")));
16308
16748
  }
16309
16749
  return sizes;
16310
16750
  }, readBaseline = (cwd) => {
16311
- const path = join40(cwd, BASELINE_FILE);
16751
+ const path = join41(cwd, BASELINE_FILE);
16312
16752
  if (!existsSync32(path))
16313
16753
  return null;
16314
16754
  try {
@@ -16397,7 +16837,7 @@ var BASELINE_FILE = ".absolute-size-baseline.json", TOP_CHANGES = 12, CATEGORY_W
16397
16837
  return;
16398
16838
  }
16399
16839
  if (args.includes("--save")) {
16400
- writeFileSync17(join40(cwd, BASELINE_FILE), `${JSON.stringify(sizes, null, 2)}
16840
+ writeFileSync17(join41(cwd, BASELINE_FILE), `${JSON.stringify(sizes, null, 2)}
16401
16841
  `);
16402
16842
  process.stdout.write(`${colors.green}\u2713${colors.reset} Saved size baseline (${Object.keys(sizes).length} entries) to ${BASELINE_FILE}
16403
16843
  `);
@@ -16785,9 +17225,9 @@ __export(exports_env, {
16785
17225
  scanEnvUsage: () => scanEnvUsage
16786
17226
  });
16787
17227
  import { existsSync as existsSync34, readFileSync as readFileSync34 } from "fs";
16788
- import { join as join41 } from "path";
17228
+ import { join as join42 } from "path";
16789
17229
  var {env: env3, Glob: Glob3 } = globalThis.Bun;
16790
- var EXTENSIONS = "ts,tsx,js,jsx,mjs,cjs,svelte,vue", STATUS_WIDTH2, keysInFile = (text2) => [...text2.matchAll(/getEnv\(\s*['"]([^'"]+)['"]\s*\)/g)].map((match) => match[1]).filter((key) => key !== undefined), scanPatterns = () => existsSync34(join41(process.cwd(), "src")) ? [`src/**/*.{${EXTENSIONS}}`] : [`*.{${EXTENSIONS}}`], scanEnvUsage = async () => {
17230
+ var EXTENSIONS = "ts,tsx,js,jsx,mjs,cjs,svelte,vue", STATUS_WIDTH2, keysInFile = (text2) => [...text2.matchAll(/getEnv\(\s*['"]([^'"]+)['"]\s*\)/g)].map((match) => match[1]).filter((key) => key !== undefined), scanPatterns = () => existsSync34(join42(process.cwd(), "src")) ? [`src/**/*.{${EXTENSIONS}}`] : [`*.{${EXTENSIONS}}`], scanEnvUsage = async () => {
16791
17231
  const scans = scanPatterns().map((pattern) => Array.fromAsync(new Glob3(pattern).scan({ cwd: process.cwd() })));
16792
17232
  const files = (await Promise.all(scans)).flat();
16793
17233
  const usage = new Map;
@@ -16854,7 +17294,7 @@ __export(exports_db, {
16854
17294
  runDb: () => runDb
16855
17295
  });
16856
17296
  import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync35, writeFileSync as writeFileSync18 } from "fs";
16857
- import { join as join42 } from "path";
17297
+ import { join as join43 } from "path";
16858
17298
  var {env: env4, spawn: spawn3, SQL } = globalThis.Bun;
16859
17299
  var BACKUP_FORMAT_VERSION = 1, RESTORE_CHUNK_ROWS = 500, URL_ENV_KEYS, JSON_DATA_TYPES, SEED_CANDIDATES, VALUE_FLAGS, paint = (text2, color) => `${color}${text2}${colors.reset}`, chunkRows = (items, size) => Array.from({ length: Math.ceil(items.length / size) }, (_, idx) => items.slice(idx * size, idx * size + size)), quoteIdent = (name) => `"${name.replace(/"/g, '""')}"`, resolveUrl = (explicit) => {
16860
17300
  const found = explicit ?? URL_ENV_KEYS.map((key) => env4[key]).find((value) => typeof value === "string" && value !== "");
@@ -16964,12 +17404,12 @@ var BACKUP_FORMAT_VERSION = 1, RESTORE_CHUNK_ROWS = 500, URL_ENV_KEYS, JSON_DATA
16964
17404
  tables,
16965
17405
  v: BACKUP_FORMAT_VERSION
16966
17406
  };
16967
- const dir = options.out ?? join42(process.cwd(), "backups");
17407
+ const dir = options.out ?? join43(process.cwd(), "backups");
16968
17408
  mkdirSync15(dir, { recursive: true });
16969
17409
  const json = JSON.stringify(payload, (_, value) => typeof value === "bigint" ? value.toString() : value);
16970
- const file = join42(dir, `backup-${payload.at.replace(/[:.]/g, "-")}.json`);
17410
+ const file = join43(dir, `backup-${payload.at.replace(/[:.]/g, "-")}.json`);
16971
17411
  writeFileSync18(file, json);
16972
- writeFileSync18(join42(dir, "latest.json"), json);
17412
+ writeFileSync18(join43(dir, "latest.json"), json);
16973
17413
  const total = chosen.reduce((sum, name) => sum + (tables[name]?.length ?? 0), 0);
16974
17414
  console.log(paint(`\u2713 backup \u2192 ${file}`, colors.green));
16975
17415
  console.log(paint(` ${chosen.length} tables, ${total} rows`, colors.dim));
@@ -16998,7 +17438,7 @@ var BACKUP_FORMAT_VERSION = 1, RESTORE_CHUNK_ROWS = 500, URL_ENV_KEYS, JSON_DATA
16998
17438
  const total = order.reduce((sum, name) => sum + (payload.tables[name]?.length ?? 0), 0);
16999
17439
  console.log(paint(`\u2713 restored ${order.length} tables, ${total} rows (idempotent upsert by primary key)`, colors.green));
17000
17440
  }, runSeed = async (entry) => {
17001
- const target = entry ?? SEED_CANDIDATES.find((candidate) => existsSync35(join42(process.cwd(), candidate)));
17441
+ const target = entry ?? SEED_CANDIDATES.find((candidate) => existsSync35(join43(process.cwd(), candidate)));
17002
17442
  if (target === undefined)
17003
17443
  throw new Error(`No seed script found (looked for ${SEED_CANDIDATES.join(", ")}). Pass a path: absolute db seed <file>.`);
17004
17444
  console.log(paint(`seeding via ${target}\u2026`, colors.cyan));
@@ -17033,7 +17473,7 @@ var BACKUP_FORMAT_VERSION = 1, RESTORE_CHUNK_ROWS = 500, URL_ENV_KEYS, JSON_DATA
17033
17473
  return;
17034
17474
  }
17035
17475
  if (sub === "restore") {
17036
- const file = positionalArgs(rest)[0] ?? join42(process.cwd(), "backups", "latest.json");
17476
+ const file = positionalArgs(rest)[0] ?? join43(process.cwd(), "backups", "latest.json");
17037
17477
  await runRestore(file, parseOptions(rest));
17038
17478
  return;
17039
17479
  }
@@ -17153,7 +17593,7 @@ import {
17153
17593
  writeFileSync as writeFileSync19
17154
17594
  } from "fs";
17155
17595
  import { createRequire } from "module";
17156
- import { dirname as dirname25, join as join43, resolve as resolve32, sep as sep6 } from "path";
17596
+ import { dirname as dirname25, join as join44, resolve as resolve32, sep as sep6 } from "path";
17157
17597
  var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17158
17598
  try {
17159
17599
  const parsed = JSON.parse(readFileSync36(path, "utf-8"));
@@ -17175,7 +17615,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17175
17615
  }, packageJsonFromEntry = (entry, expectedName) => {
17176
17616
  let directory = dirname25(entry);
17177
17617
  for (;; ) {
17178
- const candidate = join43(directory, "package.json");
17618
+ const candidate = join44(directory, "package.json");
17179
17619
  const manifest = readManifest(candidate);
17180
17620
  if (manifest && manifestName(manifest, "") === expectedName)
17181
17621
  return candidate;
@@ -17197,7 +17637,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17197
17637
  }, findInstallRoot = (cwd) => {
17198
17638
  let directory = resolve32(cwd);
17199
17639
  for (;; ) {
17200
- if (existsSync37(join43(directory, "bun.lock")) || existsSync37(join43(directory, "bun.lockb"))) {
17640
+ if (existsSync37(join44(directory, "bun.lock")) || existsSync37(join44(directory, "bun.lockb"))) {
17201
17641
  return directory;
17202
17642
  }
17203
17643
  const parent = dirname25(directory);
@@ -17208,14 +17648,14 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17208
17648
  }, findProjectManifest = (cwd, installRoot) => {
17209
17649
  let directory = resolve32(cwd);
17210
17650
  for (;; ) {
17211
- const candidate = join43(directory, "package.json");
17651
+ const candidate = join44(directory, "package.json");
17212
17652
  if (existsSync37(candidate))
17213
17653
  return candidate;
17214
17654
  if (directory === installRoot)
17215
- return join43(installRoot, "package.json");
17655
+ return join44(installRoot, "package.json");
17216
17656
  const parent = dirname25(directory);
17217
17657
  if (parent === directory)
17218
- return join43(installRoot, "package.json");
17658
+ return join44(installRoot, "package.json");
17219
17659
  directory = parent;
17220
17660
  }
17221
17661
  }, appendConsumer = (consumers, consumerPaths, path, manifest) => {
@@ -17253,7 +17693,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17253
17693
  appendConsumer(consumers, consumerPaths, inspection.consumer.path, inspection.consumer.manifest);
17254
17694
  }, inspectTypeGraph = (cwd) => {
17255
17695
  const installRoot = findInstallRoot(cwd);
17256
- const rootManifestPath = join43(installRoot, "package.json");
17696
+ const rootManifestPath = join44(installRoot, "package.json");
17257
17697
  const rootManifest = readManifest(rootManifestPath) ?? {};
17258
17698
  const consumers = [
17259
17699
  { manifest: rootManifest, path: rootManifestPath }
@@ -17289,7 +17729,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17289
17729
  const duplicates = duplicateTypeGraphPackages(report);
17290
17730
  if (duplicates.length === 0)
17291
17731
  return [];
17292
- const manifestPath = join43(report.installRoot, "package.json");
17732
+ const manifestPath = join44(report.installRoot, "package.json");
17293
17733
  const manifest = readManifest(manifestPath);
17294
17734
  if (!manifest)
17295
17735
  return [];
@@ -17311,7 +17751,7 @@ var DEPENDENCY_FIELDS, TYPE_GRAPH_PACKAGES, readManifest = (path) => {
17311
17751
  }
17312
17752
  return changes;
17313
17753
  }, removeDuplicateTypeGraphPackages = (report) => {
17314
- const manifest = readManifest(join43(report.installRoot, "package.json")) ?? {};
17754
+ const manifest = readManifest(join44(report.installRoot, "package.json")) ?? {};
17315
17755
  const rootName = manifestName(manifest, "<workspace>");
17316
17756
  const installPrefix = `${realpathSync2(report.installRoot)}${sep6}`;
17317
17757
  const nodeModulesSegment = `${sep6}node_modules${sep6}`;
@@ -17353,7 +17793,7 @@ __export(exports_doctor, {
17353
17793
  import { existsSync as existsSync38, mkdirSync as mkdirSync16, readFileSync as readFileSync37, writeFileSync as writeFileSync20 } from "fs";
17354
17794
  import { createRequire as createRequire2 } from "module";
17355
17795
  import { arch as arch4, platform as platform5 } from "os";
17356
- import { join as join44 } from "path";
17796
+ import { join as join45 } from "path";
17357
17797
  var FRAMEWORK_FIELDS2, projectRequire, check = (status2, label, detail) => ({
17358
17798
  detail,
17359
17799
  label,
@@ -17388,7 +17828,7 @@ var FRAMEWORK_FIELDS2, projectRequire, check = (status2, label, detail) => ({
17388
17828
  return [];
17389
17829
  const label = `${field.replace("Directory", "")} pages`;
17390
17830
  return [
17391
- existsSync38(join44(process.cwd(), dir)) ? check("ok", label, dir) : check("fail", label, `${dir} (missing)`)
17831
+ existsSync38(join45(process.cwd(), dir)) ? check("ok", label, dir) : check("fail", label, `${dir} (missing)`)
17392
17832
  ];
17393
17833
  }), envCheck = async () => {
17394
17834
  const vars = await collectEnvVars();
@@ -17450,9 +17890,9 @@ ${colors.dim}${checks.length} checks \xB7 ${colors.reset}${summary}${colors.dim}
17450
17890
  const fixes = [];
17451
17891
  for (const field of FRAMEWORK_FIELDS2) {
17452
17892
  const dir = readString2(config, field);
17453
- if (dir === undefined || existsSync38(join44(cwd, dir)))
17893
+ if (dir === undefined || existsSync38(join45(cwd, dir)))
17454
17894
  continue;
17455
- mkdirSync16(join44(cwd, dir, "pages"), { recursive: true });
17895
+ mkdirSync16(join45(cwd, dir, "pages"), { recursive: true });
17456
17896
  fixes.push(`created ${dir}/pages`);
17457
17897
  }
17458
17898
  return fixes;
@@ -17460,7 +17900,7 @@ ${colors.dim}${checks.length} checks \xB7 ${colors.reset}${summary}${colors.dim}
17460
17900
  const missing = (await collectEnvVars()).filter((entry) => !entry.set);
17461
17901
  if (missing.length === 0)
17462
17902
  return null;
17463
- const envExample = join44(cwd, ".env.example");
17903
+ const envExample = join45(cwd, ".env.example");
17464
17904
  const existing = existsSync38(envExample) ? readFileSync37(envExample, "utf-8") : "";
17465
17905
  const existingKeys = new Set(existing.split(`
17466
17906
  `).map((line) => line.split("=")[0]?.trim()));
@@ -17531,7 +17971,7 @@ var init_doctor = __esm(() => {
17531
17971
  "htmlDirectory",
17532
17972
  "htmxDirectory"
17533
17973
  ];
17534
- projectRequire = createRequire2(join44(process.cwd(), "package.json"));
17974
+ projectRequire = createRequire2(join45(process.cwd(), "package.json"));
17535
17975
  STATUS_MARK = {
17536
17976
  fail: `${colors.red}\u2717${colors.reset}`,
17537
17977
  ok: `${colors.green}\u2713${colors.reset}`,
@@ -17991,7 +18431,7 @@ __export(exports_islands, {
17991
18431
  runIslands: () => runIslands
17992
18432
  });
17993
18433
  import { existsSync as existsSync40, readFileSync as readFileSync39, statSync as statSync5 } from "fs";
17994
- import { join as join45, relative as relative23, resolve as resolve34 } from "path";
18434
+ import { join as join46, relative as relative23, resolve as resolve34 } from "path";
17995
18435
  var FRAMEWORK_DIR_KEY, FRAMEWORK_COLOR, printDim6 = (message) => process.stdout.write(`${colors.dim}${message}${colors.reset}
17996
18436
  `), hostFrameworkOf = (pagePath, cwd, config) => {
17997
18437
  const resolved = resolve34(cwd, pagePath);
@@ -18009,13 +18449,13 @@ var FRAMEWORK_DIR_KEY, FRAMEWORK_COLOR, printDim6 = (message) => process.stdout.
18009
18449
  return 0;
18010
18450
  }
18011
18451
  }, readManifestSizes2 = (manifestDir) => {
18012
- const manifestPath = join45(manifestDir, "manifest.json");
18452
+ const manifestPath = join46(manifestDir, "manifest.json");
18013
18453
  if (!existsSync40(manifestPath))
18014
18454
  return null;
18015
18455
  const manifest = JSON.parse(readFileSync39(manifestPath, "utf-8"));
18016
18456
  const sizes = new Map;
18017
18457
  for (const [key, value] of Object.entries(manifest)) {
18018
- sizes.set(key, fileSize3(join45(manifestDir, value.replace(/^\//, ""))));
18458
+ sizes.set(key, fileSize3(join46(manifestDir, value.replace(/^\//, ""))));
18019
18459
  }
18020
18460
  return sizes;
18021
18461
  }, collectIslands = async (cwd, config, sizes) => {
@@ -18151,7 +18591,7 @@ var init_islands2 = __esm(() => {
18151
18591
 
18152
18592
  // src/build/externalAssetPlugin.ts
18153
18593
  import { copyFileSync as copyFileSync2, existsSync as existsSync41, mkdirSync as mkdirSync17, statSync as statSync6 } from "fs";
18154
- import { basename as basename12, dirname as dirname27, join as join46, resolve as resolve35 } from "path";
18594
+ import { basename as basename12, dirname as dirname27, join as join47, resolve as resolve35 } from "path";
18155
18595
  var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
18156
18596
  name: "absolute-external-asset",
18157
18597
  setup(bld) {
@@ -18176,7 +18616,7 @@ var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
18176
18616
  continue;
18177
18617
  if (!statSync6(assetPath).isFile())
18178
18618
  continue;
18179
- const targetPath = join46(outDir, basename12(assetPath));
18619
+ const targetPath = join47(outDir, basename12(assetPath));
18180
18620
  if (existsSync41(targetPath))
18181
18621
  continue;
18182
18622
  mkdirSync17(dirname27(targetPath), { recursive: true });
@@ -18211,7 +18651,7 @@ import {
18211
18651
  basename as basename13,
18212
18652
  dirname as dirname28,
18213
18653
  isAbsolute as isAbsolute6,
18214
- join as join47,
18654
+ join as join48,
18215
18655
  relative as relative24,
18216
18656
  resolve as resolve36
18217
18657
  } from "path";
@@ -18227,7 +18667,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18227
18667
  const entry = pending.pop();
18228
18668
  if (!entry)
18229
18669
  continue;
18230
- const fullPath = join47(entry.parentPath, entry.name);
18670
+ const fullPath = join48(entry.parentPath, entry.name);
18231
18671
  if (entry.isDirectory())
18232
18672
  pending = pending.concat(readdirSync7(fullPath, { withFileTypes: true }));
18233
18673
  else
@@ -18273,7 +18713,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18273
18713
  const entry = pending.pop();
18274
18714
  if (!entry)
18275
18715
  continue;
18276
- const fullPath = join47(entry.parentPath, entry.name);
18716
+ const fullPath = join48(entry.parentPath, entry.name);
18277
18717
  if (entry.isDirectory()) {
18278
18718
  if (SERVER_RUNTIME_SCAN_SKIP_DIRS.has(entry.name))
18279
18719
  continue;
@@ -18412,7 +18852,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18412
18852
  return nativeAssetEnv;
18413
18853
  }, tryReadNodePackageJson = (packageDir) => {
18414
18854
  try {
18415
- return JSON.parse(readFileSync40(join47(packageDir, "package.json"), "utf-8"));
18855
+ return JSON.parse(readFileSync40(join48(packageDir, "package.json"), "utf-8"));
18416
18856
  } catch {
18417
18857
  return null;
18418
18858
  }
@@ -18424,7 +18864,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18424
18864
  if (!pkg)
18425
18865
  return;
18426
18866
  seen.add(specifier);
18427
- const destDir = join47(outdir, "node_modules", ...specifier.split("/"));
18867
+ const destDir = join48(outdir, "node_modules", ...specifier.split("/"));
18428
18868
  rmSync7(destDir, { force: true, recursive: true });
18429
18869
  cpSync(srcDir, destDir, {
18430
18870
  force: true,
@@ -18465,7 +18905,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18465
18905
  copyAngularRuntimePackages(buildConfig, outdir);
18466
18906
  copyChunkReferencedPackages(outdir, seen);
18467
18907
  }, collectRuntimePackageSpecifiers = (distDir) => {
18468
- const nodeModulesDir = join47(distDir, "node_modules");
18908
+ const nodeModulesDir = join48(distDir, "node_modules");
18469
18909
  if (!existsSync42(nodeModulesDir))
18470
18910
  return [];
18471
18911
  const specifiers = [];
@@ -18473,7 +18913,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18473
18913
  if (!entry.isDirectory())
18474
18914
  continue;
18475
18915
  if (entry.name.startsWith("@")) {
18476
- const scopeDir = join47(nodeModulesDir, entry.name);
18916
+ const scopeDir = join48(nodeModulesDir, entry.name);
18477
18917
  for (const scopedEntry of readdirSync7(scopeDir, {
18478
18918
  withFileTypes: true
18479
18919
  })) {
@@ -18504,18 +18944,18 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18504
18944
  const packageSpecifier = packageSpecifiers.find((root) => specifier === root || specifier.startsWith(`${root}/`));
18505
18945
  if (!packageSpecifier)
18506
18946
  return null;
18507
- const packageDir = join47(distDir, "node_modules", ...packageSpecifier.split("/"));
18947
+ const packageDir = join48(distDir, "node_modules", ...packageSpecifier.split("/"));
18508
18948
  const subpath = specifier.slice(packageSpecifier.length);
18509
- const subPackageDir = subpath ? join47(packageDir, ...subpath.slice(1).split("/")) : null;
18510
- const resolvedPackageDir = subPackageDir && existsSync42(join47(subPackageDir, "package.json")) ? subPackageDir : packageDir;
18511
- const packageJsonPath = join47(resolvedPackageDir, "package.json");
18949
+ const subPackageDir = subpath ? join48(packageDir, ...subpath.slice(1).split("/")) : null;
18950
+ const resolvedPackageDir = subPackageDir && existsSync42(join48(subPackageDir, "package.json")) ? subPackageDir : packageDir;
18951
+ const packageJsonPath = join48(resolvedPackageDir, "package.json");
18512
18952
  if (!existsSync42(packageJsonPath))
18513
18953
  return null;
18514
18954
  const pkg = JSON.parse(readFileSync40(packageJsonPath, "utf-8"));
18515
18955
  const exportKey = resolvedPackageDir !== subPackageDir && subpath ? `.${subpath}` : ".";
18516
18956
  const rootExport = pkg.exports?.[exportKey];
18517
18957
  const entry = pickExportEntry(rootExport) ?? (resolvedPackageDir === subPackageDir || !subpath ? pkg.module ?? pkg.main ?? "index.js" : `.${subpath}`);
18518
- return join47(resolvedPackageDir, entry);
18958
+ return join48(resolvedPackageDir, entry);
18519
18959
  }, RUNTIME_JS_EXTENSIONS, MODULE_SPECIFIER_RE, isRuntimeJsFile = (filePath) => RUNTIME_JS_EXTENSIONS.some((extension) => filePath.endsWith(extension)), isNodeModulesPath = (filePath) => filePath.split(/[\\/]/).includes("node_modules"), isFile = (filePath) => {
18520
18960
  try {
18521
18961
  return statSync7(filePath).isFile();
@@ -18528,13 +18968,13 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18528
18968
  const candidates = [
18529
18969
  candidate,
18530
18970
  ...RUNTIME_JS_EXTENSIONS.map((extension) => `${candidate}${extension}`),
18531
- ...RUNTIME_JS_EXTENSIONS.map((extension) => join47(candidate, `index${extension}`))
18971
+ ...RUNTIME_JS_EXTENSIONS.map((extension) => join48(candidate, `index${extension}`))
18532
18972
  ];
18533
18973
  return candidates.find((filePath) => isRuntimeJsFile(filePath) && isFile(filePath)) ?? null;
18534
18974
  }, findContainingRuntimePackageDir = (filePath) => {
18535
18975
  let dir = dirname28(filePath);
18536
18976
  while (dir !== dirname28(dir)) {
18537
- if (isNodeModulesPath(dir) && existsSync42(join47(dir, "package.json"))) {
18977
+ if (isNodeModulesPath(dir) && existsSync42(join48(dir, "package.json"))) {
18538
18978
  return dir;
18539
18979
  }
18540
18980
  dir = dirname28(dir);
@@ -18550,7 +18990,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
18550
18990
  const entry = pickExportEntry(pkg?.imports?.[specifier]);
18551
18991
  if (!entry)
18552
18992
  return null;
18553
- return join47(packageDir, entry);
18993
+ return join48(packageDir, entry);
18554
18994
  }, collectRuntimeRewriteRoots = (distDir) => collectFiles2(distDir).filter((filePath) => isRuntimeJsFile(filePath) && !isNodeModulesPath(filePath)), toTopLevelPackage = (specifier) => specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/"), FRAMEWORK_PACKAGE_NAME = "@absolutejs/absolute", copyChunkReferencedPackages = (distDir, seen) => {
18555
18995
  const distRoot = resolve36(distDir);
18556
18996
  for (const filePath of collectRuntimeRewriteRoots(distDir)) {
@@ -19117,7 +19557,7 @@ console.log(\`
19117
19557
  const resolvedOutdir = resolve36(outdir ?? "dist");
19118
19558
  await withBuildDirectoryLock(resolvedOutdir, () => compileUnlocked(serverEntry, resolvedOutdir, outfile, configPath2));
19119
19559
  }, compileUnlocked = async (serverEntry, resolvedOutdir, outfile, configPath2) => {
19120
- rmSync7(join47(resolvedOutdir, "node_modules"), {
19560
+ rmSync7(join48(resolvedOutdir, "node_modules"), {
19121
19561
  force: true,
19122
19562
  recursive: true
19123
19563
  });
@@ -19165,10 +19605,10 @@ console.log(\`
19165
19605
  ].filter((dir) => Boolean(dir));
19166
19606
  const islandRegistrySpec = buildConfig.islands?.registry;
19167
19607
  const islandRegistryPlugin = islandRegistrySpec ? createIslandRegistryDefinitionPlugin(await loadIslandRegistryBuildInfo(resolve36(islandRegistrySpec))) : undefined;
19168
- const serverBundleEntryDirectory = join47(resolvedOutdir, ".absolutejs-server-entry");
19608
+ const serverBundleEntryDirectory = join48(resolvedOutdir, ".absolutejs-server-entry");
19169
19609
  mkdirSync18(serverBundleEntryDirectory, { recursive: true });
19170
- const typeboxSetupEntry = join47(serverBundleEntryDirectory, "_typebox_setup.ts");
19171
- const serverBundleEntry = join47(serverBundleEntryDirectory, basename13(serverEntry));
19610
+ const typeboxSetupEntry = join48(serverBundleEntryDirectory, "_typebox_setup.ts");
19611
+ const serverBundleEntry = join48(serverBundleEntryDirectory, basename13(serverEntry));
19172
19612
  writeFileSync21(typeboxSetupEntry, `import { setupTypebox } from 'elysia';
19173
19613
  import * as compile from 'typebox/compile';
19174
19614
  import * as schema from 'typebox/schema';
@@ -19244,7 +19684,7 @@ export default server;
19244
19684
  copyServerRuntimeAssetReferences(resolvedOutdir);
19245
19685
  const prerenderStart = performance.now();
19246
19686
  process.stdout.write(cliTag4("\x1B[36m", "Pre-rendering pages"));
19247
- rmSync7(join47(resolvedOutdir, "_prerendered"), {
19687
+ rmSync7(join48(resolvedOutdir, "_prerendered"), {
19248
19688
  force: true,
19249
19689
  recursive: true
19250
19690
  });
@@ -19274,7 +19714,7 @@ export default server;
19274
19714
  const compileStart = performance.now();
19275
19715
  process.stdout.write(cliTag4("\x1B[36m", "Compiling standalone executable"));
19276
19716
  const entrypointCode = generateEntrypoint(resolvedOutdir, serverEntry, prerenderMap, absoluteVersion, buildConfig);
19277
- const entrypointPath = join47(resolvedOutdir, "_compile_entrypoint.ts");
19717
+ const entrypointPath = join48(resolvedOutdir, "_compile_entrypoint.ts");
19278
19718
  await Bun.write(entrypointPath, entrypointCode);
19279
19719
  mkdirSync18(dirname28(resolvedOutfile), { recursive: true });
19280
19720
  const result = await Bun.build({
@@ -19360,7 +19800,7 @@ var init_compile = __esm(() => {
19360
19800
 
19361
19801
  // src/mobile/nativeDeepLinks.ts
19362
19802
  import { readFile as readFile13, rename as rename9, writeFile as writeFile10 } from "fs/promises";
19363
- import { join as join48 } from "path";
19803
+ import { join as join49 } from "path";
19364
19804
  var START_MARKER = "<!-- absolutejs:deep-links:start -->", END_MARKER = "<!-- absolutejs:deep-links:end -->", IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements", NOT_FOUND = -1, escapeXml = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), writeChangedFile = async (path, source) => {
19365
19805
  const current = await readFile13(path, "utf8");
19366
19806
  if (current === source)
@@ -19409,7 +19849,7 @@ ${hosts}
19409
19849
  ${END_MARKER}
19410
19850
  `;
19411
19851
  }, configureAndroid = async (config) => {
19412
- const path = join48(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
19852
+ const path = join49(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
19413
19853
  const source = await readFile13(path, "utf8");
19414
19854
  const mainActivity = source.indexOf('android:name=".MainActivity"');
19415
19855
  if (mainActivity === NOT_FOUND) {
@@ -19433,7 +19873,7 @@ ${hosts}
19433
19873
  </array>
19434
19874
  ${END_MARKER}
19435
19875
  `, configureIosInfo = async (config) => {
19436
- const path = join48(config.nativeProjectDirectory, "ios/App/App/Info.plist");
19876
+ const path = join49(config.nativeProjectDirectory, "ios/App/App/Info.plist");
19437
19877
  const source = await readFile13(path, "utf8");
19438
19878
  const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
19439
19879
  ${END_MARKER}
@@ -19455,7 +19895,7 @@ ${domains}
19455
19895
  </plist>
19456
19896
  `;
19457
19897
  }, configureIosEntitlements = async (config) => {
19458
- const path = join48(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
19898
+ const path = join49(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
19459
19899
  let current = "";
19460
19900
  try {
19461
19901
  current = await readFile13(path, "utf8");
@@ -19472,7 +19912,7 @@ ${domains}
19472
19912
  await rename9(temporary, path);
19473
19913
  return true;
19474
19914
  }, configureIosProject = async (config) => {
19475
- const path = join48(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
19915
+ const path = join49(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
19476
19916
  const source = await readFile13(path, "utf8");
19477
19917
  const declarations = [
19478
19918
  ...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
@@ -19512,7 +19952,7 @@ var init_nativeDeepLinks = () => {};
19512
19952
 
19513
19953
  // src/mobile/nativeDeviceCapabilities.ts
19514
19954
  import { readFile as readFile14, rename as rename10, writeFile as writeFile11 } from "fs/promises";
19515
- import { join as join49 } from "path";
19955
+ import { join as join50 } from "path";
19516
19956
  var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->", END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->", NOT_FOUND2 = -1, IOS_PRIVACY_FILE_REFERENCE = "A85D0C000000000000000001", IOS_PRIVACY_BUILD_FILE = "A85D0C000000000000000002", PUSH_START_MARKER = "absolutejs:push-notifications:start", PUSH_END_MARKER = "absolutejs:push-notifications:end", escapeXml2 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), writeChangedFile2 = async (path, source) => {
19517
19957
  const current = await readFile14(path, "utf8");
19518
19958
  if (current === source)
@@ -19639,7 +20079,7 @@ ${entries}
19639
20079
  }, configureIosPrivacyProject = async (config, requirements) => {
19640
20080
  if (requirements.iosPrivacyAccessedApis.length === 0)
19641
20081
  return false;
19642
- const projectPath = join49(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
20082
+ const projectPath = join50(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
19643
20083
  const project = await readFile14(projectPath, "utf8");
19644
20084
  return writeChangedFile2(projectPath, addIosPrivacyProjectReference(project));
19645
20085
  }, addIosPrivacyProjectReference = (source) => {
@@ -19690,7 +20130,7 @@ ${next.slice(index)}`;
19690
20130
  }
19691
20131
  return next;
19692
20132
  }, configureIos2 = async (config, plan) => {
19693
- const path = join49(config.nativeProjectDirectory, "ios/App/App/Info.plist");
20133
+ const path = join50(config.nativeProjectDirectory, "ios/App/App/Info.plist");
19694
20134
  const source = await readFile14(path, "utf8");
19695
20135
  const requirements = absoluteDeviceNativeRequirements(plan);
19696
20136
  const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
@@ -19711,7 +20151,7 @@ ${content}
19711
20151
  ${END_MARKER2}
19712
20152
  ` : "";
19713
20153
  const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
19714
- const privacyPath = join49(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
20154
+ const privacyPath = join50(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
19715
20155
  const privacyCurrent = await optionalSource(privacyPath);
19716
20156
  const privacySource = privacyManifestSource(privacyCurrent, requirements);
19717
20157
  const [privacyChanged, projectChanged, pushChanged] = await Promise.all([
@@ -19721,7 +20161,7 @@ ${content}
19721
20161
  ]);
19722
20162
  return infoChanged || privacyChanged || projectChanged || pushChanged;
19723
20163
  }, configureIosPushNotifications = async (config, enabled) => {
19724
- const entitlementsPath = join49(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
20164
+ const entitlementsPath = join50(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
19725
20165
  const entitlements = await optionalSource(entitlementsPath);
19726
20166
  if (entitlements === null && !enabled)
19727
20167
  return false;
@@ -19733,7 +20173,7 @@ ${content}
19733
20173
  <!-- ${PUSH_END_MARKER} -->
19734
20174
  ` : "";
19735
20175
  const nextEntitlements = replacePushRegion(entitlements, entitlementRegion, entitlements.lastIndexOf("</dict>"));
19736
- const delegatePath = join49(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20176
+ const delegatePath = join50(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
19737
20177
  const delegate = await optionalSource(delegatePath);
19738
20178
  if (delegate === null && !enabled)
19739
20179
  return false;
@@ -19780,7 +20220,7 @@ ${content}
19780
20220
  throw new TypeError("Could not find a safe native project location for push notifications.");
19781
20221
  return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
19782
20222
  }, configureAndroid2 = async (config, plan) => {
19783
- const path = join49(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
20223
+ const path = join50(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
19784
20224
  const source = await readFile14(path, "utf8");
19785
20225
  const permissions = absoluteDeviceNativeRequirements(plan).androidPermissions;
19786
20226
  const content = permissions.map((permission) => ` <uses-permission android:name="${escapeXml2(permission)}" />`).join(`
@@ -19816,7 +20256,7 @@ ${content}
19816
20256
  throw new TypeError(`Android google-services.json does not contain package ${config.appId}.`);
19817
20257
  const [manifestChanged, firebaseChanged] = await Promise.all([
19818
20258
  writeChangedFile2(path, nextManifest),
19819
- writeOptionalChangedFile(join49(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
20259
+ writeOptionalChangedFile(join50(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
19820
20260
  ]);
19821
20261
  return manifestChanged || firebaseChanged;
19822
20262
  }, applyAbsoluteNativeDeviceCapabilities = async (projectRoot, config, platforms = config.platforms, plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot)) => {
@@ -19841,7 +20281,7 @@ var init_nativeDeviceCapabilities = __esm(() => {
19841
20281
 
19842
20282
  // src/mobile/nativeBackgroundSync.ts
19843
20283
  import { readFile as readFile15, rename as rename11, writeFile as writeFile12 } from "fs/promises";
19844
- import { join as join50 } from "path";
20284
+ import { join as join51 } from "path";
19845
20285
  var writeChanged = async (path, source) => {
19846
20286
  const current = await readFile15(path, "utf8");
19847
20287
  if (current === source)
@@ -19924,10 +20364,10 @@ ${makeRegion(values)} </array>
19924
20364
  if (!platforms.includes("ios") || !projectUsesAbsoluteAuth(projectRoot) || !projectUsesAbsoluteSync(projectRoot))
19925
20365
  return { changed: false };
19926
20366
  const identifier = `${config.appId}.absolutejs.background-sync`;
19927
- const infoPath = join50(config.nativeProjectDirectory, "ios/App/App/Info.plist");
20367
+ const infoPath = join51(config.nativeProjectDirectory, "ios/App/App/Info.plist");
19928
20368
  const info2 = await readFile15(infoPath, "utf8");
19929
20369
  const nextInfo = ensurePlistArrayValues(ensurePlistArrayValues(info2, "BGTaskSchedulerPermittedIdentifiers", [identifier], "background-sync-identifiers"), "UIBackgroundModes", ["fetch", "processing"], "background-sync-modes");
19930
- const delegatePath = join50(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20370
+ const delegatePath = join51(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
19931
20371
  let delegate = await readFile15(delegatePath, "utf8");
19932
20372
  if (!delegate.includes("import AbsoluteSyncCapacitor")) {
19933
20373
  const importIndex = delegate.lastIndexOf("import Capacitor");
@@ -19967,7 +20407,7 @@ import {
19967
20407
  rm as rm9,
19968
20408
  writeFile as writeFile13
19969
20409
  } from "fs/promises";
19970
- import { dirname as dirname29, join as join51 } from "path";
20410
+ import { dirname as dirname29, join as join52 } from "path";
19971
20411
  var START = "// absolutejs:mobile-updates:start", END = "// absolutejs:mobile-updates:end", PLUGIN_START = "// absolutejs:mobile-update-plugin:start", PLUGIN_END = "// absolutejs:mobile-update-plugin:end", ANDROID_PLUGIN = "AbsoluteMobileUpdateWatchdogPlugin.java", optionalSource2 = async (path) => {
19972
20412
  try {
19973
20413
  return await readFile16(path, "utf8");
@@ -20123,8 +20563,8 @@ public final class AbsoluteMobileUpdateWatchdogPlugin: CAPPlugin, CAPBridgedPlug
20123
20563
  }
20124
20564
  ${PLUGIN_END}
20125
20565
  `, configureIos3 = async (config, enabled) => {
20126
- const path = join51(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20127
- const capacitorConfigPath = join51(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
20566
+ const path = join52(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20567
+ const capacitorConfigPath = join52(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
20128
20568
  let source = await readFile16(path, "utf8");
20129
20569
  const launch = source.indexOf("didFinishLaunchingWithOptions");
20130
20570
  const brace = launch < 0 ? -1 : source.indexOf("{", launch);
@@ -20312,11 +20752,11 @@ public final class AbsoluteMobileUpdateWatchdogPlugin extends Plugin {
20312
20752
  throw error;
20313
20753
  }
20314
20754
  const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
20315
- return candidate ? join51(root, candidate) : undefined;
20755
+ return candidate ? join52(root, candidate) : undefined;
20316
20756
  }, androidActivity = async (config) => {
20317
20757
  const roots = [
20318
- join51(config.nativeProjectDirectory, "android/app/src/main/java"),
20319
- join51(config.nativeProjectDirectory, "android/app/src/main/kotlin")
20758
+ join52(config.nativeProjectDirectory, "android/app/src/main/java"),
20759
+ join52(config.nativeProjectDirectory, "android/app/src/main/kotlin")
20320
20760
  ];
20321
20761
  const candidates = await Promise.all(roots.map(androidActivityInRoot));
20322
20762
  const candidate = candidates.find((value) => value !== undefined);
@@ -20417,7 +20857,7 @@ public final class AbsoluteMobileUpdateWatchdogPlugin extends Plugin {
20417
20857
  if (!packageName)
20418
20858
  throw new TypeError("Android MainActivity package declaration was not found.");
20419
20859
  const packagePath = packageName.replaceAll(".", "/");
20420
- const pluginPath = join51(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
20860
+ const pluginPath = join52(config.nativeProjectDirectory, "android/app/src/main/java", packagePath, ANDROID_PLUGIN);
20421
20861
  const changed = await Promise.all([
20422
20862
  writeChanged2(activityPath, injectAndroidActivity(activity, enabled)),
20423
20863
  enabled ? writeChanged2(pluginPath, androidPluginSource(packageName, config.updates?.bootTimeoutMs ?? 20000)) : removeOwnedFile(pluginPath)
@@ -20436,15 +20876,522 @@ public final class AbsoluteMobileUpdateWatchdogPlugin extends Plugin {
20436
20876
  };
20437
20877
  var init_nativeUpdates = () => {};
20438
20878
 
20439
- // src/mobile/associationFiles.ts
20879
+ // src/mobile/nativeObservability.ts
20440
20880
  import {
20441
- access as access9,
20442
20881
  mkdir as mkdir12,
20443
20882
  readFile as readFile17,
20883
+ readdir as readdir6,
20444
20884
  rename as rename13,
20445
20885
  rm as rm10,
20446
20886
  writeFile as writeFile14
20447
20887
  } from "fs/promises";
20888
+ import { dirname as dirname30, join as join53 } from "path";
20889
+ var START2 = "// absolutejs:native-observability:start", END2 = "// absolutejs:native-observability:end", PLUGIN_START2 = "// absolutejs:native-observability-plugin:start", PLUGIN_END2 = "// absolutejs:native-observability-plugin:end", ANDROID_PLUGIN2 = "AbsoluteMobileObservabilityPlugin.java", optionalSource3 = async (path) => {
20890
+ try {
20891
+ return await readFile17(path, "utf8");
20892
+ } catch (error) {
20893
+ if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
20894
+ return null;
20895
+ throw error;
20896
+ }
20897
+ }, writeChanged3 = async (path, source) => {
20898
+ const current = await optionalSource3(path);
20899
+ if (current === source)
20900
+ return false;
20901
+ await mkdir12(dirname30(path), { recursive: true });
20902
+ if (current === null) {
20903
+ await writeFile14(path, source, { flag: "wx" });
20904
+ return true;
20905
+ }
20906
+ const temporary = `${path}.${crypto.randomUUID()}.tmp`;
20907
+ await writeFile14(temporary, source, { flag: "wx" });
20908
+ await rename13(temporary, path);
20909
+ return true;
20910
+ }, removeOwnedFile2 = async (path) => {
20911
+ if (await optionalSource3(path) === null)
20912
+ return false;
20913
+ await rm10(path);
20914
+ return true;
20915
+ }, isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), replaceMarkedRegion2 = (source, start2, end, region, insertion, error) => {
20916
+ const existingStart = source.indexOf(start2);
20917
+ const existingEnd = source.indexOf(end);
20918
+ if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
20919
+ throw new TypeError("AbsoluteJS native observability markers are malformed.");
20920
+ if (existingStart >= 0) {
20921
+ const from = source.lastIndexOf(`
20922
+ `, existingStart) + 1;
20923
+ const newline = source.indexOf(`
20924
+ `, existingEnd + end.length);
20925
+ return `${source.slice(0, from)}${region}${source.slice(newline < 0 ? source.length : newline + 1)}`;
20926
+ }
20927
+ if (!region)
20928
+ return source;
20929
+ if (insertion < 0)
20930
+ throw new TypeError(error);
20931
+ return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
20932
+ }, iosPluginRegion2, configureIos4 = async (config, enabled) => {
20933
+ const path = join53(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
20934
+ const capacitorConfigPath = join53(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
20935
+ let source = await readFile17(path, "utf8");
20936
+ if (enabled && !source.includes("import MetricKit")) {
20937
+ const capacitorImport = source.lastIndexOf("import Capacitor");
20938
+ if (capacitorImport < 0)
20939
+ throw new TypeError("Capacitor AppDelegate import was not found.");
20940
+ const newline = source.indexOf(`
20941
+ `, capacitorImport);
20942
+ source = `${source.slice(0, newline + 1)}import MetricKit
20943
+ ${source.slice(newline + 1)}`;
20944
+ }
20945
+ if (!enabled)
20946
+ source = source.replace(`import MetricKit
20947
+ `, "");
20948
+ source = replaceMarkedRegion2(source, PLUGIN_START2, PLUGIN_END2, enabled ? iosPluginRegion2 : "", source.length, "Could not find a safe iOS location for native observability.");
20949
+ const capacitorConfigSource2 = await readFile17(capacitorConfigPath, "utf8");
20950
+ let capacitorConfig;
20951
+ try {
20952
+ const value = JSON.parse(capacitorConfigSource2);
20953
+ if (!isRecord15(value))
20954
+ throw new TypeError;
20955
+ capacitorConfig = value;
20956
+ } catch {
20957
+ throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting native observability.");
20958
+ }
20959
+ const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
20960
+ if (!Array.isArray(packageClassList))
20961
+ throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting native observability.");
20962
+ const classes = packageClassList.filter((value) => typeof value === "string");
20963
+ const nextClasses = enabled ? [...new Set([...classes, "AbsoluteMobileObservabilityPlugin"])] : classes.filter((value) => value !== "AbsoluteMobileObservabilityPlugin");
20964
+ const nextCapacitorConfig = `${JSON.stringify({ ...capacitorConfig, packageClassList: nextClasses }, null, "\t")}
20965
+ `;
20966
+ const changed = await Promise.all([
20967
+ writeChanged3(path, source),
20968
+ writeChanged3(capacitorConfigPath, nextCapacitorConfig)
20969
+ ]);
20970
+ return changed.some(Boolean);
20971
+ }, androidPluginSource2 = (packageName) => `package ${packageName};
20972
+
20973
+ import android.app.ActivityManager;
20974
+ import android.app.ApplicationExitInfo;
20975
+ import android.os.Build;
20976
+ import android.util.Base64;
20977
+ import com.getcapacitor.JSArray;
20978
+ import com.getcapacitor.JSObject;
20979
+ import com.getcapacitor.Plugin;
20980
+ import com.getcapacitor.PluginCall;
20981
+ import com.getcapacitor.PluginMethod;
20982
+ import com.getcapacitor.annotation.CapacitorPlugin;
20983
+ import java.io.ByteArrayOutputStream;
20984
+ import java.io.File;
20985
+ import java.io.FileInputStream;
20986
+ import java.io.FileOutputStream;
20987
+ import java.io.InputStream;
20988
+ import java.nio.charset.StandardCharsets;
20989
+ import java.util.ArrayList;
20990
+ import java.util.HashSet;
20991
+ import java.util.List;
20992
+ import java.util.Set;
20993
+ import org.json.JSONArray;
20994
+ import org.json.JSONObject;
20995
+
20996
+ @CapacitorPlugin(name = "AbsoluteMobileObservability")
20997
+ public final class AbsoluteMobileObservabilityPlugin extends Plugin {
20998
+ private static final int MAX_REPORTS = 8;
20999
+ private static final int MAX_TRACE_BYTES = 32 * 1024;
21000
+ private static final String ACKNOWLEDGED_KEY = "acknowledged";
21001
+ private static final String PREFERENCES = "AbsoluteMobileObservability";
21002
+
21003
+ private File root() {
21004
+ File value = new File(getContext().getFilesDir(), "absolute-mobile-observability");
21005
+ value.mkdirs();
21006
+ return value;
21007
+ }
21008
+
21009
+ private Set<String> acknowledged() {
21010
+ Set<String> result = new HashSet<>();
21011
+ try {
21012
+ JSONArray values = new JSONArray(getContext().getSharedPreferences(PREFERENCES, 0).getString(ACKNOWLEDGED_KEY, "[]"));
21013
+ for (int index = 0; index < values.length(); index++) result.add(values.optString(index));
21014
+ } catch (Exception ignored) {}
21015
+ return result;
21016
+ }
21017
+
21018
+ private void writeAcknowledged(Set<String> values) {
21019
+ List<String> sorted = new ArrayList<>(values);
21020
+ sorted.sort(String::compareTo);
21021
+ if (sorted.size() > 64) sorted = sorted.subList(sorted.size() - 64, sorted.size());
21022
+ getContext().getSharedPreferences(PREFERENCES, 0).edit().putString(ACKNOWLEDGED_KEY, new JSONArray(sorted).toString()).apply();
21023
+ }
21024
+
21025
+ private static String kind(int reason) {
21026
+ if (reason == ApplicationExitInfo.REASON_ANR) return "anr";
21027
+ if (reason == ApplicationExitInfo.REASON_CRASH) return "crash";
21028
+ if (reason == ApplicationExitInfo.REASON_CRASH_NATIVE) return "native-crash";
21029
+ if (reason == ApplicationExitInfo.REASON_LOW_MEMORY) return "low-memory";
21030
+ if (reason == ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE) return "resource-exhaustion";
21031
+ if (reason == ApplicationExitInfo.REASON_INITIALIZATION_FAILURE) return "initialization-failure";
21032
+ if (reason == ApplicationExitInfo.REASON_SIGNALED) return "signal";
21033
+ return null;
21034
+ }
21035
+
21036
+ private static byte[] trace(ApplicationExitInfo info) {
21037
+ try (InputStream input = info.getTraceInputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream()) {
21038
+ if (input == null) return null;
21039
+ byte[] buffer = new byte[4096];
21040
+ int total = 0;
21041
+ int read;
21042
+ while ((read = input.read(buffer, 0, Math.min(buffer.length, MAX_TRACE_BYTES + 1 - total))) > 0) {
21043
+ output.write(buffer, 0, read);
21044
+ total += read;
21045
+ if (total > MAX_TRACE_BYTES) return null;
21046
+ }
21047
+ return output.toByteArray();
21048
+ } catch (Exception ignored) {
21049
+ return null;
21050
+ }
21051
+ }
21052
+
21053
+ private static JSONObject report(ApplicationExitInfo info) {
21054
+ String kind = kind(info.getReason());
21055
+ if (kind == null) return null;
21056
+ String id = "android:" + info.getTimestamp() + ":" + info.getReason() + ":" + info.getStatus();
21057
+ JSONObject details = new JSONObject();
21058
+ try {
21059
+ details.put("importance", info.getImportance());
21060
+ details.put("pssKb", info.getPss());
21061
+ details.put("reason", info.getReason());
21062
+ details.put("rssKb", info.getRss());
21063
+ details.put("status", info.getStatus());
21064
+ String description = info.getDescription();
21065
+ if (description != null) details.put("description", description);
21066
+ byte[] trace = trace(info);
21067
+ if (trace != null && trace.length > 0) {
21068
+ if (info.getReason() == ApplicationExitInfo.REASON_CRASH_NATIVE) {
21069
+ details.put("trace", Base64.encodeToString(trace, Base64.NO_WRAP));
21070
+ details.put("traceEncoding", "android-tombstone-protobuf-base64");
21071
+ } else {
21072
+ details.put("trace", new String(trace, StandardCharsets.UTF_8));
21073
+ details.put("traceEncoding", "utf-8");
21074
+ }
21075
+ }
21076
+ return new JSONObject()
21077
+ .put("details", details)
21078
+ .put("id", id)
21079
+ .put("kind", kind)
21080
+ .put("occurredAt", info.getTimestamp())
21081
+ .put("platform", "android");
21082
+ } catch (Exception ignored) {
21083
+ return null;
21084
+ }
21085
+ }
21086
+
21087
+ private Set<String> queuedIds() {
21088
+ Set<String> ids = new HashSet<>();
21089
+ for (File file : files()) {
21090
+ try (InputStream input = new FileInputStream(file)) {
21091
+ JSONObject report = new JSONObject(new String(read(input), StandardCharsets.UTF_8));
21092
+ ids.add(report.optString("id"));
21093
+ } catch (Exception ignored) {}
21094
+ }
21095
+ return ids;
21096
+ }
21097
+
21098
+ private File[] files() {
21099
+ File[] values = root().listFiles((directory, name) -> name.endsWith(".json"));
21100
+ if (values == null) return new File[0];
21101
+ java.util.Arrays.sort(values, java.util.Comparator.comparing(File::getName));
21102
+ return values;
21103
+ }
21104
+
21105
+ private static byte[] read(InputStream input) throws java.io.IOException {
21106
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
21107
+ byte[] buffer = new byte[4096];
21108
+ int count;
21109
+ while ((count = input.read(buffer)) >= 0) output.write(buffer, 0, count);
21110
+ return output.toByteArray();
21111
+ }
21112
+
21113
+ private void collect() {
21114
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return;
21115
+ ActivityManager manager = getContext().getSystemService(ActivityManager.class);
21116
+ if (manager == null) return;
21117
+ Set<String> ignored = acknowledged();
21118
+ ignored.addAll(queuedIds());
21119
+ List<ApplicationExitInfo> exits = manager.getHistoricalProcessExitReasons(null, 0, 16);
21120
+ for (ApplicationExitInfo exit : exits) {
21121
+ JSONObject report = report(exit);
21122
+ if (report == null || ignored.contains(report.optString("id"))) continue;
21123
+ String name = String.format(java.util.Locale.ROOT, "%013d-%s.json", exit.getTimestamp(), java.util.UUID.randomUUID());
21124
+ File target = new File(root(), name);
21125
+ try (FileOutputStream output = new FileOutputStream(target)) {
21126
+ output.write(report.toString().getBytes(StandardCharsets.UTF_8));
21127
+ ignored.add(report.optString("id"));
21128
+ } catch (Exception ignoredWrite) {}
21129
+ }
21130
+ File[] files = files();
21131
+ for (int index = 0; index < files.length - MAX_REPORTS; index++) files[index].delete();
21132
+ }
21133
+
21134
+ @PluginMethod
21135
+ public void pending(PluginCall call) {
21136
+ collect();
21137
+ JSArray reports = new JSArray();
21138
+ for (File file : files()) {
21139
+ try (InputStream input = new FileInputStream(file)) {
21140
+ reports.put(new JSONObject(new String(read(input), StandardCharsets.UTF_8)));
21141
+ } catch (Exception ignored) {}
21142
+ }
21143
+ call.resolve(new JSObject().put("reports", reports));
21144
+ }
21145
+
21146
+ @PluginMethod
21147
+ public void acknowledge(PluginCall call) {
21148
+ JSArray ids = call.getArray("ids");
21149
+ if (ids == null) {
21150
+ call.reject("Native observability acknowledgement requires report ids.");
21151
+ return;
21152
+ }
21153
+ Set<String> accepted = new HashSet<>();
21154
+ for (int index = 0; index < ids.length(); index++) accepted.add(ids.optString(index));
21155
+ Set<String> acknowledged = acknowledged();
21156
+ for (File file : files()) {
21157
+ try (InputStream input = new FileInputStream(file)) {
21158
+ JSONObject report = new JSONObject(new String(read(input), StandardCharsets.UTF_8));
21159
+ String id = report.optString("id");
21160
+ if (accepted.contains(id)) {
21161
+ acknowledged.add(id);
21162
+ file.delete();
21163
+ }
21164
+ } catch (Exception ignored) {}
21165
+ }
21166
+ writeAcknowledged(acknowledged);
21167
+ call.resolve();
21168
+ }
21169
+ }
21170
+ `, androidActivityInRoot2 = async (root) => {
21171
+ let entries;
21172
+ try {
21173
+ entries = await readdir6(root, { recursive: true });
21174
+ } catch (error) {
21175
+ if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
21176
+ return;
21177
+ throw error;
21178
+ }
21179
+ const candidate = entries.find((entry) => entry.endsWith("/MainActivity.java") || entry.endsWith("/MainActivity.kt") || entry === "MainActivity.java" || entry === "MainActivity.kt");
21180
+ return candidate ? join53(root, candidate) : undefined;
21181
+ }, androidActivity2 = async (config) => {
21182
+ const roots = [
21183
+ join53(config.nativeProjectDirectory, "android/app/src/main/java"),
21184
+ join53(config.nativeProjectDirectory, "android/app/src/main/kotlin")
21185
+ ];
21186
+ const candidates = await Promise.all(roots.map(androidActivityInRoot2));
21187
+ const candidate = candidates.find((value) => value !== undefined);
21188
+ if (candidate)
21189
+ return candidate;
21190
+ throw new TypeError("Android MainActivity.java or MainActivity.kt was not found.");
21191
+ }, androidRegion2 = (kotlin, generatedMethod) => {
21192
+ if (generatedMethod)
21193
+ return kotlin ? ` ${START2}
21194
+ override fun onCreate(savedInstanceState: android.os.Bundle?) {
21195
+ registerPlugin(AbsoluteMobileObservabilityPlugin::class.java)
21196
+ super.onCreate(savedInstanceState)
21197
+ }
21198
+ ${END2}
21199
+ ` : ` ${START2}
21200
+ @Override
21201
+ public void onCreate(android.os.Bundle savedInstanceState) {
21202
+ registerPlugin(AbsoluteMobileObservabilityPlugin.class);
21203
+ super.onCreate(savedInstanceState);
21204
+ }
21205
+ ${END2}
21206
+ `;
21207
+ return kotlin ? ` ${START2}
21208
+ registerPlugin(AbsoluteMobileObservabilityPlugin::class.java)
21209
+ ${END2}
21210
+ ` : ` ${START2}
21211
+ registerPlugin(AbsoluteMobileObservabilityPlugin.class);
21212
+ ${END2}
21213
+ `;
21214
+ }, injectAndroidActivity2 = (source, enabled) => {
21215
+ const kotlin = /\bfun\s+onCreate\s*\(/u.test(source) || source.includes("BridgeActivity()");
21216
+ const existingStart = source.indexOf(START2);
21217
+ const existingEnd = source.indexOf(END2);
21218
+ if (existingStart < 0 !== existingEnd < 0)
21219
+ throw new TypeError("AbsoluteJS native observability markers are malformed.");
21220
+ if (existingStart >= 0) {
21221
+ const from = source.lastIndexOf(`
21222
+ `, existingStart) + 1;
21223
+ const newline = source.indexOf(`
21224
+ `, existingEnd + END2.length);
21225
+ const through = newline < 0 ? source.length : newline + 1;
21226
+ const generatedMethod = source.slice(from, through).includes("onCreate(");
21227
+ if (!enabled)
21228
+ return `${source.slice(0, from)}${source.slice(through)}`;
21229
+ return `${source.slice(0, from)}${androidRegion2(kotlin, generatedMethod)}${source.slice(through)}`;
21230
+ }
21231
+ if (!enabled)
21232
+ return source;
21233
+ const onCreate = kotlin ? source.search(/\boverride\s+fun\s+onCreate\s*\([^)]*\)\s*\{/u) : source.search(/\b(?:public|protected)\s+void\s+onCreate\s*\([^)]*\)\s*\{/u);
21234
+ if (onCreate >= 0) {
21235
+ const brace = source.indexOf("{", onCreate);
21236
+ const insert = source.indexOf(`
21237
+ `, brace) + 1;
21238
+ return `${source.slice(0, insert)}${androidRegion2(kotlin, false)}${source.slice(insert)}`;
21239
+ }
21240
+ const close = source.lastIndexOf("}");
21241
+ if (close < 0)
21242
+ throw new TypeError("Could not find the Android MainActivity class body.");
21243
+ const separator = source[close - 1] === `
21244
+ ` ? "" : `
21245
+ `;
21246
+ return `${source.slice(0, close)}${separator}${androidRegion2(kotlin, true)}${source.slice(close)}`;
21247
+ }, configureAndroid4 = async (config, enabled) => {
21248
+ const activityPath = await androidActivity2(config);
21249
+ const activity = await readFile17(activityPath, "utf8");
21250
+ const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
21251
+ if (!packageName)
21252
+ throw new TypeError("Android MainActivity package declaration was not found.");
21253
+ const pluginPath = join53(config.nativeProjectDirectory, "android/app/src/main/java", packageName.replaceAll(".", "/"), ANDROID_PLUGIN2);
21254
+ const changed = await Promise.all([
21255
+ writeChanged3(activityPath, injectAndroidActivity2(activity, enabled)),
21256
+ enabled ? writeChanged3(pluginPath, androidPluginSource2(packageName)) : removeOwnedFile2(pluginPath)
21257
+ ]);
21258
+ return changed.some(Boolean);
21259
+ }, applyAbsoluteNativeObservability = async (config, platforms = config.platforms) => {
21260
+ if (config.engine !== "capacitor")
21261
+ return { changed: false };
21262
+ const enabled = config.observability !== undefined;
21263
+ const changed = [];
21264
+ if (platforms.includes("ios"))
21265
+ changed.push(await configureIos4(config, enabled));
21266
+ if (platforms.includes("android"))
21267
+ changed.push(await configureAndroid4(config, enabled));
21268
+ return { changed: changed.some(Boolean) };
21269
+ };
21270
+ var init_nativeObservability = __esm(() => {
21271
+ iosPluginRegion2 = `${PLUGIN_START2}
21272
+ @objc(AbsoluteMobileObservabilityPlugin)
21273
+ public final class AbsoluteMobileObservabilityPlugin: CAPPlugin, CAPBridgedPlugin, MXMetricManagerSubscriber {
21274
+ public let identifier = "AbsoluteMobileObservabilityPlugin"
21275
+ public let jsName = "AbsoluteMobileObservability"
21276
+ public let pluginMethods: [CAPPluginMethod] = [
21277
+ CAPPluginMethod(name: "acknowledge", returnType: CAPPluginReturnPromise),
21278
+ CAPPluginMethod(name: "pending", returnType: CAPPluginReturnPromise)
21279
+ ]
21280
+
21281
+ private static let maximumReports = 8
21282
+ private static let maximumReportBytes = 64 * 1024
21283
+ private static let queue = DispatchQueue(label: "dev.absolute.mobile-observability")
21284
+
21285
+ public override func load() {
21286
+ MXMetricManager.shared.add(self)
21287
+ }
21288
+
21289
+ deinit {
21290
+ MXMetricManager.shared.remove(self)
21291
+ }
21292
+
21293
+ private static func root() -> URL? {
21294
+ guard let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return nil }
21295
+ let root = support.appendingPathComponent("AbsoluteMobileObservability", isDirectory: true)
21296
+ try? FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
21297
+ return root
21298
+ }
21299
+
21300
+ private static func files() -> [URL] {
21301
+ guard let root = root() else { return [] }
21302
+ return ((try? FileManager.default.contentsOfDirectory(at: root, includingPropertiesForKeys: nil)) ?? [])
21303
+ .filter { $0.pathExtension == "json" }
21304
+ .sorted { $0.lastPathComponent < $1.lastPathComponent }
21305
+ }
21306
+
21307
+ private static func trim() {
21308
+ let overflow = max(0, files().count - maximumReports)
21309
+ for file in files().prefix(overflow) { try? FileManager.default.removeItem(at: file) }
21310
+ }
21311
+
21312
+ private static func enqueue(kind: String, occurredAt: Double, details: [AnyHashable: Any]) {
21313
+ guard let root = root() else { return }
21314
+ let id = UUID().uuidString.lowercased()
21315
+ var value: [String: Any] = [
21316
+ "details": Dictionary(uniqueKeysWithValues: details.map { (String(describing: $0.key), $0.value) }),
21317
+ "id": id,
21318
+ "kind": kind,
21319
+ "occurredAt": occurredAt,
21320
+ "platform": "ios"
21321
+ ]
21322
+ guard JSONSerialization.isValidJSONObject(value),
21323
+ let original = try? JSONSerialization.data(withJSONObject: value) else { return }
21324
+ if original.count > maximumReportBytes {
21325
+ value["details"] = [
21326
+ "diagnosticPrefix": String(decoding: original.prefix(24 * 1024), as: UTF8.self),
21327
+ "originalBytes": original.count,
21328
+ "truncated": true
21329
+ ]
21330
+ }
21331
+ guard let data = try? JSONSerialization.data(withJSONObject: value),
21332
+ data.count <= maximumReportBytes else { return }
21333
+ let timestamp = String(format: "%013.0f", occurredAt)
21334
+ try? data.write(to: root.appendingPathComponent("\\(timestamp)-\\(id).json"), options: .atomic)
21335
+ trim()
21336
+ }
21337
+
21338
+ public func didReceive(_ payloads: [MXDiagnosticPayload]) {
21339
+ Self.queue.async {
21340
+ for payload in payloads {
21341
+ let at = payload.timeStampEnd.timeIntervalSince1970 * 1000
21342
+ for value in payload.crashDiagnostics ?? [] { Self.enqueue(kind: "crash", occurredAt: at, details: value.dictionaryRepresentation()) }
21343
+ for value in payload.hangDiagnostics ?? [] { Self.enqueue(kind: "hang", occurredAt: at, details: value.dictionaryRepresentation()) }
21344
+ for value in payload.cpuExceptionDiagnostics ?? [] { Self.enqueue(kind: "cpu-exception", occurredAt: at, details: value.dictionaryRepresentation()) }
21345
+ for value in payload.diskWriteExceptionDiagnostics ?? [] { Self.enqueue(kind: "disk-write-exception", occurredAt: at, details: value.dictionaryRepresentation()) }
21346
+ if #available(iOS 16.0, *) {
21347
+ for value in payload.appLaunchDiagnostics ?? [] { Self.enqueue(kind: "app-launch", occurredAt: at, details: value.dictionaryRepresentation()) }
21348
+ }
21349
+ }
21350
+ }
21351
+ }
21352
+
21353
+ @objc public func pending(_ call: CAPPluginCall) {
21354
+ Self.queue.async {
21355
+ let reports: [[String: Any]] = Self.files().compactMap { file in
21356
+ guard let data = try? Data(contentsOf: file),
21357
+ let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil }
21358
+ return value
21359
+ }
21360
+ call.resolve(["reports": reports])
21361
+ }
21362
+ }
21363
+
21364
+ @objc public func acknowledge(_ call: CAPPluginCall) {
21365
+ guard let ids = call.getArray("ids", String.self) else {
21366
+ call.reject("Native observability acknowledgement requires report ids.")
21367
+ return
21368
+ }
21369
+ let accepted = Set(ids)
21370
+ Self.queue.async {
21371
+ for file in Self.files() {
21372
+ guard let data = try? Data(contentsOf: file),
21373
+ let value = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
21374
+ let id = value["id"] as? String,
21375
+ accepted.contains(id) else { continue }
21376
+ try? FileManager.default.removeItem(at: file)
21377
+ }
21378
+ call.resolve()
21379
+ }
21380
+ }
21381
+ }
21382
+ ${PLUGIN_END2}
21383
+ `;
21384
+ });
21385
+
21386
+ // src/mobile/associationFiles.ts
21387
+ import {
21388
+ access as access9,
21389
+ mkdir as mkdir13,
21390
+ readFile as readFile18,
21391
+ rename as rename14,
21392
+ rm as rm11,
21393
+ writeFile as writeFile15
21394
+ } from "fs/promises";
20448
21395
  import { resolve as resolve37 } from "path";
20449
21396
  import { Elysia } from "elysia";
20450
21397
  var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERIFY_TIMEOUT_MS = 1e4, ANDROID_ASSOCIATION_PATH = "/.well-known/assetlinks.json", APPLE_ASSOCIATION_PATH = "/.well-known/apple-app-site-association", missingIdentity = (field, platform6) => new TypeError(`${field} is required to publish ${platform6} deep-link association files.`), createAppleDocument = (config, requireAll) => {
@@ -20498,7 +21445,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20498
21445
  }, writeAtomic = async (path, source) => {
20499
21446
  let current;
20500
21447
  try {
20501
- current = await readFile17(path, "utf8");
21448
+ current = await readFile18(path, "utf8");
20502
21449
  } catch (error) {
20503
21450
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
20504
21451
  throw error;
@@ -20507,8 +21454,8 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20507
21454
  if (current === source)
20508
21455
  return false;
20509
21456
  const temporary = `${path}.${crypto.randomUUID()}.tmp`;
20510
- await writeFile14(temporary, source, { flag: "wx" });
20511
- await rename13(temporary, path);
21457
+ await writeFile15(temporary, source, { flag: "wx" });
21458
+ await rename14(temporary, path);
20512
21459
  return true;
20513
21460
  }, exists3 = async (path) => {
20514
21461
  try {
@@ -20521,7 +21468,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20521
21468
  const path = resolve37(root, OWNERSHIP_FILE);
20522
21469
  let ownership;
20523
21470
  try {
20524
- ownership = JSON.parse(await readFile17(path, "utf8"));
21471
+ ownership = JSON.parse(await readFile18(path, "utf8"));
20525
21472
  } catch {
20526
21473
  throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
20527
21474
  }
@@ -20534,19 +21481,19 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20534
21481
  await assertOwnedOutput(root);
20535
21482
  const backup = `${root}.${crypto.randomUUID()}.previous`;
20536
21483
  if (hasCurrent)
20537
- await rename13(root, backup);
21484
+ await rename14(root, backup);
20538
21485
  try {
20539
- await rename13(temporary, root);
21486
+ await rename14(temporary, root);
20540
21487
  } catch (error) {
20541
21488
  if (hasCurrent)
20542
- await rename13(backup, root);
21489
+ await rename14(backup, root);
20543
21490
  throw error;
20544
21491
  }
20545
21492
  if (hasCurrent)
20546
- await rm10(backup, { force: true, recursive: true });
21493
+ await rm11(backup, { force: true, recursive: true });
20547
21494
  }, materializeHost = async (root, host2, files) => {
20548
21495
  const directory = resolve37(root, host2, ".well-known");
20549
- await mkdir12(directory, { recursive: true });
21496
+ await mkdir13(directory, { recursive: true });
20550
21497
  return Promise.all(files.map(async ([name, document2]) => {
20551
21498
  const path = resolve37(directory, name);
20552
21499
  await writeAtomic(path, `${JSON.stringify(document2, null, 2)}
@@ -20582,7 +21529,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20582
21529
  if (documents.apple) {
20583
21530
  files.push(["apple-app-site-association", documents.apple]);
20584
21531
  }
20585
- await mkdir12(temporary, { recursive: true });
21532
+ await mkdir13(temporary, { recursive: true });
20586
21533
  try {
20587
21534
  const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host2) => materializeHost(temporary, host2, files)))).flat();
20588
21535
  await writeAtomic(resolve37(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
@@ -20591,7 +21538,7 @@ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json", HTTP_OK = 200, VERI
20591
21538
  const written = temporaryPaths.map((path) => resolve37(root, path.slice(temporary.length + 1)));
20592
21539
  return { root, written };
20593
21540
  } catch (error) {
20594
- await rm10(temporary, { force: true, recursive: true });
21541
+ await rm11(temporary, { force: true, recursive: true });
20595
21542
  throw error;
20596
21543
  }
20597
21544
  }, verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.fetch) => {
@@ -20629,8 +21576,8 @@ var init_associationFiles = __esm(() => {
20629
21576
  });
20630
21577
 
20631
21578
  // src/mobile/androidWebView.ts
20632
- import { mkdir as mkdir13, writeFile as writeFile15 } from "fs/promises";
20633
- import { dirname as dirname30, resolve as resolve38 } from "path";
21579
+ import { mkdir as mkdir14, writeFile as writeFile16 } from "fs/promises";
21580
+ import { dirname as dirname31, resolve as resolve38 } from "path";
20634
21581
 
20635
21582
  class CdpConnection {
20636
21583
  #closed;
@@ -20904,8 +21851,8 @@ var CDP_COMMAND_TIMEOUT_MS = 1e4, WEBVIEW_ATTACH_TIMEOUT_MS = 30000, WEBVIEW_POL
20904
21851
  throw new Error("Android WebView screenshot returned no image data.");
20905
21852
  }
20906
21853
  const absolutePath = resolve38(path);
20907
- await mkdir13(dirname30(absolutePath), { recursive: true });
20908
- await writeFile15(absolutePath, Buffer.from(data, "base64"));
21854
+ await mkdir14(dirname31(absolutePath), { recursive: true });
21855
+ await writeFile16(absolutePath, Buffer.from(data, "base64"));
20909
21856
  return absolutePath;
20910
21857
  },
20911
21858
  tap: async (coordinateX, coordinateY) => {
@@ -21037,8 +21984,8 @@ var DEFAULT_ROUTE_TIMEOUT_MS = 30000, DEFAULT_HMR_TIMEOUT_MS = 30000, routeExpre
21037
21984
 
21038
21985
  // src/mobile/mobileBundleInspection.ts
21039
21986
  import { createHash as createHash15 } from "crypto";
21040
- import { access as access10, readFile as readFile18, stat as stat2 } from "fs/promises";
21041
- import { join as join52, relative as relative25, resolve as resolve39 } from "path";
21987
+ import { access as access10, readFile as readFile19, stat as stat2 } from "fs/promises";
21988
+ import { join as join54, relative as relative25, resolve as resolve39 } from "path";
21042
21989
  var MOBILE_FRAMEWORKS, SHA256_PATTERN, isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), portablePath = (projectRoot, path) => {
21043
21990
  const value = relative25(resolve39(projectRoot), resolve39(path)).replaceAll("\\", "/");
21044
21991
  return value || ".";
@@ -21050,7 +21997,7 @@ var MOBILE_FRAMEWORKS, SHA256_PATTERN, isObject2 = (value) => typeof value === "
21050
21997
  return false;
21051
21998
  }
21052
21999
  }, readObject = async (path) => {
21053
- const value = JSON.parse(await readFile18(path, "utf8"));
22000
+ const value = JSON.parse(await readFile19(path, "utf8"));
21054
22001
  if (!isObject2(value))
21055
22002
  throw new TypeError("JSON root must be an object.");
21056
22003
  return value;
@@ -21075,13 +22022,13 @@ var MOBILE_FRAMEWORKS, SHA256_PATTERN, isObject2 = (value) => typeof value === "
21075
22022
  if (expectedHash !== undefined) {
21076
22023
  if (!SHA256_PATTERN.test(expectedHash))
21077
22024
  throw new TypeError(`${field} has an invalid SHA-256 digest.`);
21078
- const actual = createHash15("sha256").update(await readFile18(path)).digest("hex");
22025
+ const actual = createHash15("sha256").update(await readFile19(path)).digest("hex");
21079
22026
  if (actual !== expectedHash)
21080
22027
  throw new TypeError(`${field} failed its SHA-256 integrity check.`);
21081
22028
  }
21082
22029
  return portable;
21083
22030
  }, inspectAbsoluteMobileBundle = async (config, projectRoot) => {
21084
- const manifestPath = join52(config.bundleDirectory, "absolute-mobile-manifest.json");
22031
+ const manifestPath = join54(config.bundleDirectory, "absolute-mobile-manifest.json");
21085
22032
  const manifest = portablePath(projectRoot, manifestPath);
21086
22033
  if (!await pathExists5(manifestPath))
21087
22034
  return { manifest, status: "missing" };
@@ -21188,8 +22135,8 @@ var init_mobileBundleInspection = __esm(() => {
21188
22135
  });
21189
22136
 
21190
22137
  // src/mobile/releaseDoctor.ts
21191
- import { access as access11, readFile as readFile19, readdir as readdir6 } from "fs/promises";
21192
- import { dirname as dirname31, extname as extname8, join as join53, relative as relative26 } from "path";
22138
+ import { access as access11, readFile as readFile20, readdir as readdir7 } from "fs/promises";
22139
+ import { dirname as dirname32, extname as extname8, join as join55, relative as relative26 } from "path";
21193
22140
  var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, EXACT_VERSION_PATTERN, NOT_FOUND3 = -1, LOCK_FILES, MANUAL_REVIEW, pathExists6 = async (path) => {
21194
22141
  try {
21195
22142
  await access11(path);
@@ -21202,13 +22149,13 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21202
22149
  return findHmrAsset(path);
21203
22150
  if (!isFile2 || !RELEASE_ASSET_EXTENSIONS.has(extname8(path)))
21204
22151
  return;
21205
- const source = await readFile19(path, "utf8");
22152
+ const source = await readFile20(path, "utf8");
21206
22153
  return HMR_ASSET_PATTERN.test(source) ? path : undefined;
21207
22154
  }, findHmrAsset = async (root) => {
21208
22155
  if (!await pathExists6(root))
21209
22156
  return;
21210
- const entries = await readdir6(root, { withFileTypes: true });
21211
- const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join53(root, entry.name), entry.isDirectory(), entry.isFile())));
22157
+ const entries = await readdir7(root, { withFileTypes: true });
22158
+ const matches = await Promise.all(entries.map((entry) => inspectReleaseAsset(join55(root, entry.name), entry.isDirectory(), entry.isFile())));
21212
22159
  return matches.find((match) => match !== undefined);
21213
22160
  }, pass = (id, detail, path) => ({ detail, id, path, status: "pass" }), fail5 = (id, detail, path, remediation) => ({
21214
22161
  detail,
@@ -21222,8 +22169,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21222
22169
  path,
21223
22170
  remediation,
21224
22171
  status: "warn"
21225
- }), isRecord15 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJsonObject = async (path) => {
21226
- const value = JSON.parse(await readFile19(path, "utf8"));
22172
+ }), isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJsonObject = async (path) => {
22173
+ const value = JSON.parse(await readFile20(path, "utf8"));
21227
22174
  if (typeof value !== "object" || value === null || Array.isArray(value))
21228
22175
  throw new TypeError("JSON root must be an object.");
21229
22176
  return Object.fromEntries(Object.entries(value));
@@ -21239,7 +22186,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21239
22186
  }
21240
22187
  return declarations;
21241
22188
  }, versionCore = (version2) => version2.split("-")[0]?.split(".").slice(0, 2).join("."), capacitorVersionCheck = async (config, projectRoot) => {
21242
- const manifestPath = join53(projectRoot, "package.json");
22189
+ const manifestPath = join55(projectRoot, "package.json");
21243
22190
  try {
21244
22191
  const manifest = await readJsonObject(manifestPath);
21245
22192
  const declarations = packageDeclarations(manifest);
@@ -21252,7 +22199,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21252
22199
  const declared = declarations.get(name);
21253
22200
  if (!declared || !EXACT_VERSION_PATTERN.test(declared))
21254
22201
  throw new TypeError(`${name} must be a direct exact dependency.`);
21255
- const installed = await readJsonObject(join53(projectRoot, "node_modules", name, "package.json"));
22202
+ const installed = await readJsonObject(join55(projectRoot, "node_modules", name, "package.json"));
21256
22203
  if (installed.version !== declared)
21257
22204
  throw new TypeError(`${name} does not match its installed version.`);
21258
22205
  return declared;
@@ -21273,7 +22220,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21273
22220
  const actual = installed.split(".").map(Number);
21274
22221
  return actual[0] === expected[0] && actual[1] === expected[1] && (actual[2] ?? NOT_FOUND3) >= (expected[2] ?? 0);
21275
22222
  }, expoVersionCheck = async (config) => {
21276
- const manifestPath = join53(config.nativeProjectDirectory, "package.json");
22223
+ const manifestPath = join55(config.nativeProjectDirectory, "package.json");
21277
22224
  try {
21278
22225
  const manifest = await readJsonObject(manifestPath);
21279
22226
  const declarations = packageDeclarations(manifest);
@@ -21283,13 +22230,13 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21283
22230
  throw new TypeError(`Generated Expo project is missing ${missingRequired}.`);
21284
22231
  const installedVersions = await Promise.all([...declarations].map(async ([name, declared]) => ({
21285
22232
  declared,
21286
- installed: await readJsonObject(join53(config.nativeProjectDirectory, "node_modules", name, "package.json")),
22233
+ installed: await readJsonObject(join55(config.nativeProjectDirectory, "node_modules", name, "package.json")),
21287
22234
  name
21288
22235
  })));
21289
22236
  const mismatch = installedVersions.find(({ declared, installed }) => typeof installed.version !== "string" || !satisfiesGeneratedVersion(declared, installed.version));
21290
22237
  if (mismatch)
21291
22238
  throw new TypeError(`Generated Expo dependency ${mismatch.name}@${mismatch.declared} does not match its installed version.`);
21292
- if (!await pathExists6(join53(config.nativeProjectDirectory, "bun.lock")))
22239
+ if (!await pathExists6(join55(config.nativeProjectDirectory, "bun.lock")))
21293
22240
  throw new TypeError("Generated Expo dependency lockfile is missing.");
21294
22241
  return pass("mobile.expo-versions", `Generated Expo SDK dependencies are pinned, installed, and locked (${declarations.get("expo")}).`, manifestPath);
21295
22242
  } catch (error) {
@@ -21297,10 +22244,10 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21297
22244
  }
21298
22245
  }, dependencyLockCheck = async (projectRoot) => {
21299
22246
  const present = (await Promise.all(LOCK_FILES.map(async (name) => ({
21300
- exists: await pathExists6(join53(projectRoot, name)),
22247
+ exists: await pathExists6(join55(projectRoot, name)),
21301
22248
  name
21302
22249
  })))).find(({ exists: exists4 }) => exists4);
21303
- return present ? pass("mobile.dependency-lock", `Dependency graph is locked by ${present.name}.`, join53(projectRoot, present.name)) : fail5("mobile.dependency-lock", "No supported dependency lockfile is present.", projectRoot, "Install dependencies with the project package manager and commit its lockfile before release.");
22250
+ return present ? pass("mobile.dependency-lock", `Dependency graph is locked by ${present.name}.`, join55(projectRoot, present.name)) : fail5("mobile.dependency-lock", "No supported dependency lockfile is present.", projectRoot, "Install dependencies with the project package manager and commit its lockfile before release.");
21304
22251
  }, productionOriginCheck = (config, projectRoot) => {
21305
22252
  const origin = new URL(config.productionOrigin);
21306
22253
  return origin.protocol === "https:" ? pass("mobile.production-origin", "Production transport uses an HTTPS origin.") : fail5("mobile.production-origin", "A loopback development origin cannot be used for a signed release.", projectRoot, "Configure mobile.server.productionOrigin with the deployed HTTPS origin.");
@@ -21335,7 +22282,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21335
22282
  if (!await pathExists6(nativeConfigPath)) {
21336
22283
  return fail5("android.capacitor-config", "The generated Android Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync android` before release validation.");
21337
22284
  }
21338
- const unsafe = isUnsafeCapacitorConfig(await readFile19(nativeConfigPath, "utf8"));
22285
+ const unsafe = isUnsafeCapacitorConfig(await readFile20(nativeConfigPath, "utf8"));
21339
22286
  return unsafe ? fail5("android.capacitor-config", "Android Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync android`; do not ship development transport overrides.") : pass("android.capacitor-config", "Android Capacitor config contains no development transport overrides.", nativeConfigPath);
21340
22287
  }, capacitorIdentityCheck = async (config, platform6, nativeConfigPath) => {
21341
22288
  try {
@@ -21350,12 +22297,12 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21350
22297
  if (!await pathExists6(manifestPath)) {
21351
22298
  return fail5("android.cleartext", "The Android manifest is missing.", manifestPath, "Run `absolute mobile sync android` before release validation.");
21352
22299
  }
21353
- const source = await readFile19(manifestPath, "utf8");
22300
+ const source = await readFile20(manifestPath, "utf8");
21354
22301
  const cleartext = /android:usesCleartextTraffic=["']true["']/u.test(source);
21355
22302
  const networkConfigName = source.match(/android:networkSecurityConfig=["']@xml\/([a-z0-9_]+)["']/u)?.[1];
21356
- const networkConfigPath = networkConfigName ? join53(dirname31(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
22303
+ const networkConfigPath = networkConfigName ? join55(dirname32(manifestPath), "res", "xml", `${networkConfigName}.xml`) : undefined;
21357
22304
  const developmentTrustReference = /android:networkSecurityConfig=["']@xml\/absolutejs_dev_network_security["']/u.test(source);
21358
- const developmentTrustContents = networkConfigPath ? await readFile19(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
22305
+ const developmentTrustContents = networkConfigPath ? await readFile20(networkConfigPath, "utf8").then((value) => value.includes("@raw/absolutejs_dev_ca")).catch(() => false) : false;
21359
22306
  const developmentTrust = developmentTrustReference || developmentTrustContents;
21360
22307
  return cleartext || developmentTrust ? fail5("android.cleartext", developmentTrust ? "Android still references the AbsoluteJS development certificate authority." : "Android explicitly permits cleartext traffic.", manifestPath, "Run `absolute mobile sync android`; do not ship development transport or trust overrides.") : pass("android.cleartext", "Android does not explicitly permit cleartext traffic.", manifestPath);
21361
22308
  }, hmrAssetsReleaseCheck = async (publicRoot) => {
@@ -21364,12 +22311,12 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21364
22311
  }, embeddedBundleReleaseCheck = async (config, projectRoot, platform6, publicRoot) => {
21365
22312
  const inspection = await inspectAbsoluteMobileBundle({ ...config, bundleDirectory: publicRoot }, projectRoot);
21366
22313
  if (inspection.status === "valid")
21367
- return pass(`${platform6}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join53(publicRoot, "absolute-mobile-manifest.json"));
21368
- return fail5(`${platform6}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join53(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
22314
+ return pass(`${platform6}.bundle-integrity`, `Packaged mobile manifest, runtime, routes, and ${inspection.pageCount ?? 0} page asset(s) passed structural and SHA-256 validation.`, join55(publicRoot, "absolute-mobile-manifest.json"));
22315
+ return fail5(`${platform6}.bundle-integrity`, inspection.status === "missing" ? "The packaged mobile manifest is missing." : `The packaged mobile bundle is invalid: ${inspection.issue ?? "unknown validation error"}`, join55(publicRoot, "absolute-mobile-manifest.json"), "Rebuild the production mobile bundle and run Capacitor sync for this platform.");
21369
22316
  }, contentSecurityPolicyCheck = async (config, platform6, publicRoot) => {
21370
- const path = join53(publicRoot, "index.html");
22317
+ const path = join55(publicRoot, "index.html");
21371
22318
  try {
21372
- const source = await readFile19(path, "utf8");
22319
+ const source = await readFile20(path, "utf8");
21373
22320
  const requirements = [
21374
22321
  "Content-Security-Policy",
21375
22322
  "default-src 'self'",
@@ -21388,26 +22335,26 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21388
22335
  }
21389
22336
  }, validateExpoCodeSigningProjection = async (config, expo) => {
21390
22337
  const configuredSigning = config.updates?.expoCodeSigning;
21391
- const generatedUpdates = isRecord15(expo.updates) ? expo.updates : undefined;
22338
+ const generatedUpdates = isRecord16(expo.updates) ? expo.updates : undefined;
21392
22339
  if (config.updates && !configuredSigning)
21393
22340
  throw new TypeError("Expo production updates require an embedded code-signing certificate.");
21394
22341
  if (!configuredSigning && generatedUpdates && (("codeSigningCertificate" in generatedUpdates) || ("codeSigningMetadata" in generatedUpdates)))
21395
22342
  throw new TypeError("Generated Expo config contains unconfigured code-signing material.");
21396
22343
  if (!configuredSigning)
21397
22344
  return;
21398
- if (!generatedUpdates || generatedUpdates.codeSigningCertificate !== "./certs/absolute-mobile-update.pem" || !isRecord15(generatedUpdates.codeSigningMetadata) || generatedUpdates.codeSigningMetadata.keyid !== configuredSigning.keyId || generatedUpdates.codeSigningMetadata.alg !== configuredSigning.algorithm)
22345
+ if (!generatedUpdates || generatedUpdates.codeSigningCertificate !== "./certs/absolute-mobile-update.pem" || !isRecord16(generatedUpdates.codeSigningMetadata) || generatedUpdates.codeSigningMetadata.keyid !== configuredSigning.keyId || generatedUpdates.codeSigningMetadata.alg !== configuredSigning.algorithm)
21399
22346
  throw new TypeError("Generated Expo code-signing metadata does not match mobile config.");
21400
- const generatedCertificate = await readFile19(join53(config.nativeProjectDirectory, "certs", "absolute-mobile-update.pem"), "utf8");
22347
+ const generatedCertificate = await readFile20(join55(config.nativeProjectDirectory, "certs", "absolute-mobile-update.pem"), "utf8");
21401
22348
  if (generatedCertificate !== configuredSigning.certificatePem)
21402
22349
  throw new TypeError("Generated Expo code-signing certificate does not match mobile config.");
21403
22350
  }, expoApplicationConfigCheck = async (config, projectRoot) => {
21404
- const path = join53(config.nativeProjectDirectory, "app.json");
22351
+ const path = join55(config.nativeProjectDirectory, "app.json");
21405
22352
  try {
21406
22353
  const root = await readJsonObject(path);
21407
- if (!isRecord15(root.expo))
22354
+ if (!isRecord16(root.expo))
21408
22355
  throw new TypeError("Generated Expo application config is invalid.");
21409
22356
  const { expo } = root;
21410
- if (expo.name !== config.appName || config.platforms.includes("android") && (!isRecord15(expo.android) || expo.android.package !== config.appId) || config.platforms.includes("ios") && (!isRecord15(expo.ios) || expo.ios.bundleIdentifier !== config.appId)) {
22357
+ if (expo.name !== config.appName || config.platforms.includes("android") && (!isRecord16(expo.android) || expo.android.package !== config.appId) || config.platforms.includes("ios") && (!isRecord16(expo.ios) || expo.ios.bundleIdentifier !== config.appId)) {
21411
22358
  throw new TypeError("Generated Expo application identity does not match mobile config.");
21412
22359
  }
21413
22360
  const expectedRuntime = resolveAbsoluteMobileUpdateRuntime(config, projectRoot).fingerprint;
@@ -21423,12 +22370,12 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21423
22370
  return fail5("expo.app-config", error instanceof Error ? error.message : "Generated Expo application config could not be validated.", path, "Run `absolute mobile build <platform>`; do not edit the generated Expo project.");
21424
22371
  }
21425
22372
  }, expoEmbeddedAssetsCheck = async (config) => {
21426
- const generated = join53(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
21427
- const assetsRoot = join53(config.nativeProjectDirectory, "assets", "absolute");
22373
+ const generated = join55(config.nativeProjectDirectory, "src", "generated", "webAssets.ts");
22374
+ const assetsRoot = join55(config.nativeProjectDirectory, "assets", "absolute");
21428
22375
  try {
21429
22376
  const [source, manifest] = await Promise.all([
21430
- readFile19(generated, "utf8"),
21431
- readJsonObject(join53(config.bundleDirectory, "absolute-mobile-manifest.json"))
22377
+ readFile20(generated, "utf8"),
22378
+ readJsonObject(join55(config.bundleDirectory, "absolute-mobile-manifest.json"))
21432
22379
  ]);
21433
22380
  if (source.includes("embedded AbsoluteJS bundle is unavailable") || typeof manifest.appBuild !== "string" || !source.includes(JSON.stringify(manifest.appBuild)) || !source.includes(JSON.stringify(config.productionOrigin))) {
21434
22381
  throw new TypeError("Generated Expo assets do not contain the prepared production release identity.");
@@ -21448,8 +22395,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21448
22395
  if (!embedded)
21449
22396
  return false;
21450
22397
  const [left, right] = await Promise.all([
21451
- readFile19(join53(config.bundleDirectory, path)),
21452
- readFile19(join53(assetsRoot, embedded))
22398
+ readFile20(join55(config.bundleDirectory, path)),
22399
+ readFile20(join55(assetsRoot, embedded))
21453
22400
  ]);
21454
22401
  return left.equals(right);
21455
22402
  }));
@@ -21463,18 +22410,18 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21463
22410
  if (!await pathExists6(root))
21464
22411
  return [];
21465
22412
  const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
21466
- return files.filter((file) => extensions.has(extname8(file))).map((file) => join53(root, file));
22413
+ return files.filter((file) => extensions.has(extname8(file))).map((file) => join55(root, file));
21467
22414
  }, containsPattern = async (paths, pattern) => {
21468
- const sources = await Promise.all(paths.map((path) => readFile19(path, "utf8")));
22415
+ const sources = await Promise.all(paths.map((path) => readFile20(path, "utf8")));
21469
22416
  const index = sources.findIndex((source) => pattern.test(source));
21470
22417
  return index === NOT_FOUND3 ? undefined : paths[index];
21471
22418
  }, androidNativeSecurityCheck = async (androidRoot) => {
21472
- const manifestPath = join53(androidRoot, "app/src/main/AndroidManifest.xml");
22419
+ const manifestPath = join55(androidRoot, "app/src/main/AndroidManifest.xml");
21473
22420
  try {
21474
- const manifest = await readFile19(manifestPath, "utf8");
22421
+ const manifest = await readFile20(manifestPath, "utf8");
21475
22422
  if (/android:debuggable=["']true["']/u.test(manifest))
21476
22423
  throw new TypeError("Android release manifest explicitly enables application debugging.");
21477
- const sources = await sourceFiles(join53(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
22424
+ const sources = await sourceFiles(join55(androidRoot, "app/src/main"), new Set([".java", ".kt"]));
21478
22425
  const debugSource = await containsPattern(sources, /setWebContentsDebuggingEnabled\s*\(\s*true\s*\)/u);
21479
22426
  if (debugSource)
21480
22427
  return fail5("android.native-debugging", "Android application source unconditionally enables WebView debugging.", debugSource, "Remove the unconditional WebView debugging call; use the platform debug-build behavior during development.");
@@ -21483,14 +22430,14 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21483
22430
  return fail5("android.native-debugging", error instanceof Error ? error.message : "Android native debugging configuration could not be validated.", manifestPath, "Remove explicit release debugging settings and rerun mobile sync.");
21484
22431
  }
21485
22432
  }, androidExportedComponentsCheck = async (manifestPath) => {
21486
- const source = await readFile19(manifestPath, "utf8").catch(() => "");
22433
+ const source = await readFile20(manifestPath, "utf8").catch(() => "");
21487
22434
  const exported = [
21488
22435
  ...source.matchAll(/<(?:activity|activity-alias|provider|receiver|service)\b[^>]*>/giu)
21489
22436
  ].map(([tag]) => tag).filter((tag) => /android:exported=["']true["']/iu.test(tag)).map((tag) => tag.match(/android:name=["']([^"']+)["']/iu)?.[1]).filter((name) => Boolean(name) && name !== ".MainActivity");
21490
22437
  return exported.length === 0 ? pass("android.exported-components", "No non-launcher Android component is explicitly exported.", manifestPath) : warn("android.exported-components", `${exported.length} non-launcher Android component(s) are exported and require manual authorization review.`, manifestPath, "Confirm each exported component is intentional, permission-protected where appropriate, and documented in the mobile threat model review.");
21491
22438
  }, androidDeepLinkProjectionCheck = async (config, manifestPath) => {
21492
22439
  try {
21493
- const source = await readFile19(manifestPath, "utf8");
22440
+ const source = await readFile20(manifestPath, "utf8");
21494
22441
  const hasWebHost = (host2) => [...source.matchAll(/<data\b[^>]*>/giu)].some(([tag]) => tag.includes('android:scheme="https"') && tag.includes(`android:host="${host2}"`));
21495
22442
  if (!source.includes('android:autoVerify="true"') || !source.includes("android.intent.category.BROWSABLE") || config.deepLinkHosts.some((host2) => !hasWebHost(host2)) || config.deepLinkScheme && !source.includes(`android:scheme="${config.deepLinkScheme}"`))
21496
22443
  throw new TypeError("Android App Link or custom-scheme projection does not match mobile config.");
@@ -21503,15 +22450,15 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21503
22450
  return;
21504
22451
  const timeout = String(config.updates.bootTimeoutMs);
21505
22452
  if (platform6 === "ios") {
21506
- const path = join53(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
21507
- const configPath2 = join53(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
22453
+ const path = join55(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
22454
+ const configPath2 = join55(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
21508
22455
  const [source, nativeConfig] = await Promise.all([
21509
- readFile19(path, "utf8").catch(() => ""),
21510
- readFile19(configPath2, "utf8").catch(() => "")
22456
+ readFile20(path, "utf8").catch(() => ""),
22457
+ readFile20(configPath2, "utf8").catch(() => "")
21511
22458
  ]);
21512
22459
  return source.includes("AbsoluteMobileUpdateWatchdogPlugin") && source.includes("recoverInterruptedBoot()") && source.includes(`.milliseconds(${timeout})`) && source.includes("quarantinedReleases") && nativeConfig.includes("AbsoluteMobileUpdateWatchdogPlugin") ? pass("ios.update-watchdog", `The native update boot watchdog is projected with a ${timeout}ms deadline.`, path) : fail5("ios.update-watchdog", "The iOS update boot watchdog does not match mobile config.", path, "Run `absolute mobile sync ios` before building the release.");
21513
22460
  }
21514
- const sourceRoot = join53(config.nativeProjectDirectory, "android/app/src/main");
22461
+ const sourceRoot = join55(config.nativeProjectDirectory, "android/app/src/main");
21515
22462
  const sources = await sourceFiles(sourceRoot, new Set([".java", ".kt"]));
21516
22463
  const [activity, plugin] = await Promise.all([
21517
22464
  containsPattern(sources, /recoverInterruptedBoot\s*\(\s*this\s*\)/u),
@@ -21530,14 +22477,14 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21530
22477
  return fail5("ios.native-debugging", "iOS application source unconditionally enables WebView inspection.", debugSource, "Remove unconditional WebView inspection from release source.");
21531
22478
  return pass("ios.native-debugging", "iOS source does not enable release debugger attachment or WebView inspection.", entitlementPaths[0] ?? iosRoot);
21532
22479
  }, iosDeepLinkProjectionCheck = async (config, iosRoot) => {
21533
- const infoPath = join53(iosRoot, "App/App/Info.plist");
21534
- const entitlementsPath = join53(iosRoot, "App/AbsoluteJS.entitlements");
21535
- const projectPath = join53(iosRoot, "App/App.xcodeproj/project.pbxproj");
22480
+ const infoPath = join55(iosRoot, "App/App/Info.plist");
22481
+ const entitlementsPath = join55(iosRoot, "App/AbsoluteJS.entitlements");
22482
+ const projectPath = join55(iosRoot, "App/App.xcodeproj/project.pbxproj");
21536
22483
  try {
21537
22484
  const [info2, entitlements, project] = await Promise.all([
21538
- readFile19(infoPath, "utf8"),
21539
- readFile19(entitlementsPath, "utf8"),
21540
- readFile19(projectPath, "utf8")
22485
+ readFile20(infoPath, "utf8"),
22486
+ readFile20(entitlementsPath, "utf8"),
22487
+ readFile20(projectPath, "utf8")
21541
22488
  ]);
21542
22489
  if (config.deepLinkScheme && (!info2.includes("<key>CFBundleURLTypes</key>") || !info2.includes(`<string>${config.deepLinkScheme}</string>`)))
21543
22490
  throw new TypeError("iOS custom URL scheme does not match mobile config.");
@@ -21552,7 +22499,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21552
22499
  }, syncSchemaReleaseCheck = (projectRoot) => {
21553
22500
  if (!projectUsesAbsoluteSync(projectRoot))
21554
22501
  return;
21555
- const manifestPath = join53(projectRoot, "package.json");
22502
+ const manifestPath = join55(projectRoot, "package.json");
21556
22503
  try {
21557
22504
  const schema = discoverAbsoluteSyncSchema(projectRoot);
21558
22505
  const versions = schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ");
@@ -21574,8 +22521,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21574
22521
  }, IOS_USAGE_KEYS, androidDevicePermissionCheck = async (config, permissions) => {
21575
22522
  if (!config.platforms.includes("android") || permissions.length === 0)
21576
22523
  return;
21577
- const path = join53(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
21578
- const source = await readFile19(path, "utf8");
22524
+ const path = join55(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
22525
+ const source = await readFile20(path, "utf8");
21579
22526
  const missing = permissions.filter((permission) => !source.includes(`android:name="${permission}"`) && !source.includes(`android:name='${permission}'`));
21580
22527
  if (missing.length === 0)
21581
22528
  return;
@@ -21583,9 +22530,9 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21583
22530
  }, iosDevicePermissionCheck = async (config, purposes) => {
21584
22531
  if (!config.platforms.includes("ios") || purposes.length === 0)
21585
22532
  return;
21586
- const iosRoot = join53(config.nativeProjectDirectory, "ios");
21587
- const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join53(iosRoot, "App/App/Info.plist");
21588
- const source = await readFile19(path, "utf8");
22533
+ const iosRoot = join55(config.nativeProjectDirectory, "ios");
22534
+ const path = config.engine === "expo" ? await uniqueExpoIosFile(iosRoot, "**/Info.plist", "Info.plist") : join55(iosRoot, "App/App/Info.plist");
22535
+ const source = await readFile20(path, "utf8");
21589
22536
  const missing = purposes.filter((purpose) => !source.includes(`<key>${IOS_USAGE_KEYS[purpose]}</key>`));
21590
22537
  if (missing.length === 0)
21591
22538
  return;
@@ -21594,7 +22541,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21594
22541
  if (requirements.iosPrivacyAccessedApis.length === 0)
21595
22542
  return;
21596
22543
  const privacyPath = await uniqueExpoIosFile(iosRoot, "**/PrivacyInfo.xcprivacy", "privacy manifest");
21597
- const privacy = await readFile19(privacyPath, "utf8");
22544
+ const privacy = await readFile20(privacyPath, "utf8");
21598
22545
  const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
21599
22546
  if (!missing && project.includes(privacyPath.split(/[\\/]/u).at(-1) ?? ""))
21600
22547
  return;
@@ -21603,14 +22550,14 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21603
22550
  if (!requirements.iosPushNotifications)
21604
22551
  return;
21605
22552
  const entitlementsPath = await uniqueExpoIosFile(iosRoot, "**/*.entitlements", "entitlements");
21606
- const entitlements = await readFile19(entitlementsPath, "utf8");
22553
+ const entitlements = await readFile20(entitlementsPath, "utf8");
21607
22554
  if (entitlements.includes("<key>aps-environment</key>"))
21608
22555
  return;
21609
22556
  return fail5("mobile.device-capabilities", "Expo iOS push entitlement does not match detected capabilities.", entitlementsPath, "Run `absolute mobile build ios` to regenerate native push integration.");
21610
22557
  }, expoIosCapabilityProjectionCheck = async (config, requirements) => {
21611
- const iosRoot = join53(config.nativeProjectDirectory, "ios");
22558
+ const iosRoot = join55(config.nativeProjectDirectory, "ios");
21612
22559
  const projectPath = await uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project");
21613
- const project = await readFile19(projectPath, "utf8");
22560
+ const project = await readFile20(projectPath, "utf8");
21614
22561
  const privacy = await expoIosPrivacyCapabilityCheck(iosRoot, project, requirements);
21615
22562
  if (privacy)
21616
22563
  return privacy;
@@ -21620,41 +22567,41 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21620
22567
  return;
21621
22568
  if (config.engine === "expo")
21622
22569
  return expoIosCapabilityProjectionCheck(config, requirements);
21623
- const appRoot = join53(config.nativeProjectDirectory, "ios/App/App");
21624
- const infoPath = join53(appRoot, "Info.plist");
21625
- const info2 = await readFile19(infoPath, "utf8").catch(() => "");
22570
+ const appRoot = join55(config.nativeProjectDirectory, "ios/App/App");
22571
+ const infoPath = join55(appRoot, "Info.plist");
22572
+ const info2 = await readFile20(infoPath, "utf8").catch(() => "");
21626
22573
  if (requirements.iosSystemBars && !/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<true\s*\/>/u.test(info2))
21627
22574
  return fail5("mobile.device-capabilities", "iOS system-bar capability is missing its required view-controller setting.", infoPath, "Run `absolute mobile sync ios` to regenerate native capability settings.");
21628
22575
  if (requirements.iosPrivacyAccessedApis.length > 0) {
21629
- const privacyPath = join53(appRoot, "PrivacyInfo.xcprivacy");
21630
- const projectPath = join53(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
22576
+ const privacyPath = join55(appRoot, "PrivacyInfo.xcprivacy");
22577
+ const projectPath = join55(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
21631
22578
  const [privacy, project] = await Promise.all([
21632
- readFile19(privacyPath, "utf8").catch(() => ""),
21633
- readFile19(projectPath, "utf8").catch(() => "")
22579
+ readFile20(privacyPath, "utf8").catch(() => ""),
22580
+ readFile20(projectPath, "utf8").catch(() => "")
21634
22581
  ]);
21635
22582
  const missing = requirements.iosPrivacyAccessedApis.some(({ api, reasons }) => !privacy.includes(`<string>${api}</string>`) || reasons.some((reason) => !privacy.includes(`<string>${reason}</string>`)));
21636
22583
  if (missing || !project.includes("PrivacyInfo.xcprivacy in Resources"))
21637
22584
  return fail5("mobile.device-capabilities", "iOS privacy manifest or target membership does not match detected native capabilities.", privacyPath, "Run `absolute mobile sync ios` to regenerate and target PrivacyInfo.xcprivacy.");
21638
22585
  }
21639
22586
  if (requirements.iosPushNotifications) {
21640
- const entitlementsPath = join53(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
21641
- const delegatePath = join53(appRoot, "AppDelegate.swift");
22587
+ const entitlementsPath = join55(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
22588
+ const delegatePath = join55(appRoot, "AppDelegate.swift");
21642
22589
  const [entitlements, delegate] = await Promise.all([
21643
- readFile19(entitlementsPath, "utf8").catch(() => ""),
21644
- readFile19(delegatePath, "utf8").catch(() => "")
22590
+ readFile20(entitlementsPath, "utf8").catch(() => ""),
22591
+ readFile20(delegatePath, "utf8").catch(() => "")
21645
22592
  ]);
21646
22593
  if (!entitlements.includes("<key>aps-environment</key>") || !delegate.includes("capacitorDidRegisterForRemoteNotifications") || !delegate.includes("capacitorDidFailToRegisterForRemoteNotifications"))
21647
22594
  return fail5("mobile.device-capabilities", "iOS push entitlement or AppDelegate forwarding does not match detected capabilities.", entitlementsPath, "Run `absolute mobile sync ios` to regenerate native push integration.");
21648
22595
  }
21649
22596
  return;
21650
22597
  }, deviceCapabilityReleaseCheck = async (config, projectRoot) => {
21651
- const manifestPath = join53(projectRoot, "package.json");
22598
+ const manifestPath = join55(projectRoot, "package.json");
21652
22599
  try {
21653
22600
  const plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot, config.engine);
21654
22601
  const assertPackages = async () => {
21655
22602
  if (config.engine === "capacitor")
21656
22603
  return assertAbsoluteDeviceCapabilityPackages(projectRoot, plan);
21657
- const generated = await readJsonObject(join53(config.nativeProjectDirectory, "package.json"));
22604
+ const generated = await readJsonObject(join55(config.nativeProjectDirectory, "package.json"));
21658
22605
  const declarations = packageDeclarations(generated);
21659
22606
  const missing = plan.requiredPackages.filter((spec) => {
21660
22607
  const separator = spec.lastIndexOf("@");
@@ -21680,11 +22627,11 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21680
22627
  return fail5("mobile.device-capabilities", error instanceof Error ? error.message : "Native device capability provisioning is invalid.", manifestPath, "Run `absolute mobile sync` and approve the exact capability plugins before releasing.");
21681
22628
  }
21682
22629
  }, inspectAndroidRelease = async (config, projectRoot) => {
21683
- const androidRoot = join53(config.nativeProjectDirectory, "android");
21684
- const nativeConfigPath = join53(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
21685
- const manifestPath = join53(androidRoot, "app", "src", "main", "AndroidManifest.xml");
21686
- const publicRoot = join53(androidRoot, "app", "src", "main", "assets", "public");
21687
- const journalPath = join53(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
22630
+ const androidRoot = join55(config.nativeProjectDirectory, "android");
22631
+ const nativeConfigPath = join55(androidRoot, "app", "src", "main", "assets", "capacitor.config.json");
22632
+ const manifestPath = join55(androidRoot, "app", "src", "main", "AndroidManifest.xml");
22633
+ const publicRoot = join55(androidRoot, "app", "src", "main", "assets", "public");
22634
+ const journalPath = join55(projectRoot, ".absolutejs", "mobile", "dev-session", "journal.json");
21688
22635
  const checks = await Promise.all([
21689
22636
  journalReleaseCheck(journalPath, "android"),
21690
22637
  capacitorConfigReleaseCheck(nativeConfigPath),
@@ -21703,9 +22650,9 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21703
22650
  path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
21704
22651
  }));
21705
22652
  }, inspectExpoAndroidRelease = async (config, projectRoot) => {
21706
- const androidRoot = join53(config.nativeProjectDirectory, "android");
21707
- const manifestPath = join53(androidRoot, "app", "src", "main", "AndroidManifest.xml");
21708
- const journalPath = join53(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
22653
+ const androidRoot = join55(config.nativeProjectDirectory, "android");
22654
+ const manifestPath = join55(androidRoot, "app", "src", "main", "AndroidManifest.xml");
22655
+ const journalPath = join55(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
21709
22656
  const checks = await Promise.all([
21710
22657
  journalReleaseCheck(journalPath, "android"),
21711
22658
  expoApplicationConfigCheck(config, projectRoot),
@@ -21729,7 +22676,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21729
22676
  const [path] = paths;
21730
22677
  if (!path)
21731
22678
  throw new TypeError(`Generated Expo iOS project is missing its ${label}.`);
21732
- return join53(iosRoot, path);
22679
+ return join55(iosRoot, path);
21733
22680
  }, expoIosNativeProjectionCheck = async (config, iosRoot) => {
21734
22681
  try {
21735
22682
  const [infoPath, entitlementsPath, projectPath] = await Promise.all([
@@ -21738,9 +22685,9 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21738
22685
  uniqueExpoIosFile(iosRoot, "**/*.xcodeproj/project.pbxproj", "Xcode project")
21739
22686
  ]);
21740
22687
  const [info2, entitlements, project] = await Promise.all([
21741
- readFile19(infoPath, "utf8"),
21742
- readFile19(entitlementsPath, "utf8"),
21743
- readFile19(projectPath, "utf8")
22688
+ readFile20(infoPath, "utf8"),
22689
+ readFile20(entitlementsPath, "utf8"),
22690
+ readFile20(projectPath, "utf8")
21744
22691
  ]);
21745
22692
  if (/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2))
21746
22693
  throw new TypeError("Expo iOS App Transport Security permits arbitrary network loads.");
@@ -21755,8 +22702,8 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21755
22702
  return fail5("ios.expo-native-projection", error instanceof Error ? error.message : "Expo iOS native projection could not be validated.", iosRoot, "Run `absolute mobile build ios` to regenerate the production Expo CNG project.");
21756
22703
  }
21757
22704
  }, inspectExpoIosRelease = async (config, projectRoot) => {
21758
- const iosRoot = join53(config.nativeProjectDirectory, "ios");
21759
- const journalPath = join53(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
22705
+ const iosRoot = join55(config.nativeProjectDirectory, "ios");
22706
+ const journalPath = join55(projectRoot, ".absolutejs", "mobile", "expo-dev-session", "journal.json");
21760
22707
  const checks = [
21761
22708
  await journalReleaseCheck(journalPath, "ios")
21762
22709
  ];
@@ -21775,11 +22722,11 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21775
22722
  path: check2.path ? relative26(projectRoot, check2.path).replaceAll("\\", "/") || "." : undefined
21776
22723
  }));
21777
22724
  }, inspectIosRelease = async (config, projectRoot) => {
21778
- const iosAppRoot = join53(config.nativeProjectDirectory, "ios", "App", "App");
21779
- const nativeConfigPath = join53(iosAppRoot, "capacitor.config.json");
21780
- const infoPath = join53(iosAppRoot, "Info.plist");
21781
- const publicRoot = join53(iosAppRoot, "public");
21782
- const journalPath = join53(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
22725
+ const iosAppRoot = join55(config.nativeProjectDirectory, "ios", "App", "App");
22726
+ const nativeConfigPath = join55(iosAppRoot, "capacitor.config.json");
22727
+ const infoPath = join55(iosAppRoot, "Info.plist");
22728
+ const publicRoot = join55(iosAppRoot, "public");
22729
+ const journalPath = join55(projectRoot, ".absolutejs", "mobile", "ios-dev-session", "journal.json");
21783
22730
  const checks = [
21784
22731
  await journalReleaseCheck(journalPath, "ios")
21785
22732
  ];
@@ -21791,7 +22738,7 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21791
22738
  }
21792
22739
  if (!await pathExists6(nativeConfigPath)) {
21793
22740
  checks.push(fail5("ios.capacitor-config", "The generated iOS Capacitor config is missing.", nativeConfigPath, "Run `absolute mobile sync ios` before release validation."));
21794
- } else if (isUnsafeCapacitorConfig(await readFile19(nativeConfigPath, "utf8"))) {
22741
+ } else if (isUnsafeCapacitorConfig(await readFile20(nativeConfigPath, "utf8"))) {
21795
22742
  checks.push(fail5("ios.capacitor-config", "iOS Capacitor config contains a development server URL, cleartext transport, navigation allowlist, or invalid JSON.", nativeConfigPath, "Run `absolute mobile sync ios`; do not ship development transport overrides."));
21796
22743
  } else {
21797
22744
  checks.push(pass("ios.capacitor-config", "iOS Capacitor config contains no development transport overrides.", nativeConfigPath));
@@ -21800,12 +22747,12 @@ var ABSOLUTE_MOBILE_COMPLIANCE_REPORT_FORMAT = 1, HMR_ASSET_PATTERN, RELEASE_ASS
21800
22747
  if (!await pathExists6(infoPath)) {
21801
22748
  checks.push(fail5("ios.transport-security", "The iOS Info.plist is missing.", infoPath, "Run `absolute mobile sync ios` before release validation."));
21802
22749
  } else {
21803
- const info2 = await readFile19(infoPath, "utf8");
22750
+ const info2 = await readFile20(infoPath, "utf8");
21804
22751
  checks.push(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(info2) ? fail5("ios.transport-security", "iOS App Transport Security permits arbitrary network loads.", infoPath, "Remove NSAllowsArbitraryLoads from the release Info.plist.") : pass("ios.transport-security", "iOS App Transport Security does not permit arbitrary loads.", infoPath));
21805
22752
  }
21806
22753
  const hmrAsset = await findHmrAsset(publicRoot);
21807
22754
  checks.push(hmrAsset ? fail5("ios.hmr-assets", "A packaged iOS asset contains the development HMR client.", hmrAsset, "Rebuild the production mobile bundle and run Capacitor sync again.") : pass("ios.hmr-assets", "Packaged iOS assets contain no development HMR markers.", publicRoot));
21808
- checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join53(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join53(config.nativeProjectDirectory, "ios")));
22755
+ checks.push(await embeddedBundleReleaseCheck(config, projectRoot, "ios", publicRoot), await contentSecurityPolicyCheck(config, "ios", publicRoot), await iosNativeSecurityCheck(join55(config.nativeProjectDirectory, "ios")), await iosDeepLinkProjectionCheck(config, join55(config.nativeProjectDirectory, "ios")));
21809
22756
  if (updateWatchdog)
21810
22757
  checks.push(updateWatchdog);
21811
22758
  return checks.map((check2) => ({
@@ -21900,18 +22847,18 @@ import { createHash as createHash16 } from "crypto";
21900
22847
  import {
21901
22848
  access as access12,
21902
22849
  copyFile as copyFile5,
21903
- mkdir as mkdir14,
22850
+ mkdir as mkdir15,
21904
22851
  mkdtemp as mkdtemp7,
21905
- readFile as readFile20,
22852
+ readFile as readFile21,
21906
22853
  realpath as realpath2,
21907
- rename as rename14,
21908
- rm as rm11,
22854
+ rename as rename15,
22855
+ rm as rm12,
21909
22856
  stat as stat3,
21910
- writeFile as writeFile16
22857
+ writeFile as writeFile17
21911
22858
  } from "fs/promises";
21912
- import { dirname as dirname32, isAbsolute as isAbsolute7, join as join54, relative as relative27, resolve as resolve40, sep as sep7 } from "path";
21913
- var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
21914
- if (!isRecord16(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
22859
+ import { dirname as dirname33, isAbsolute as isAbsolute7, join as join56, relative as relative27, resolve as resolve40, sep as sep7 } from "path";
22860
+ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), requireManifest2 = (value) => {
22861
+ if (!isRecord17(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
21915
22862
  throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
21916
22863
  }
21917
22864
  return {
@@ -21973,14 +22920,14 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value ==
21973
22920
  ]);
21974
22921
  if (result.exitCode !== 0)
21975
22922
  throw new TypeError("jarsigner could not sign the Android App Bundle with the configured CI identity.");
21976
- }, sha256File2 = async (path) => createHash16("sha256").update(await readFile20(path)).digest("hex"), fingerprintExpoAndroidProject = async (nativeDirectory) => {
22923
+ }, sha256File2 = async (path) => createHash16("sha256").update(await readFile21(path)).digest("hex"), fingerprintExpoAndroidProject = async (nativeDirectory) => {
21977
22924
  const root = await realpath2(nativeDirectory);
21978
22925
  const files = await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: root, onlyFiles: true }));
21979
22926
  const records = await Promise.all(files.filter((path) => {
21980
22927
  const parts = path.replaceAll("\\", "/").split("/");
21981
22928
  return !parts.includes(".gradle") && !parts.includes("build");
21982
22929
  }).sort().map(async (path) => {
21983
- const contents = await readFile20(join54(root, path));
22930
+ const contents = await readFile21(join56(root, path));
21984
22931
  return `${path.replaceAll("\\", "/")}\x00${createHash16("sha256").update(contents).digest("hex")}\x00`;
21985
22932
  }));
21986
22933
  return createHash16("sha256").update(records.join("")).digest("hex");
@@ -21993,11 +22940,11 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value ==
21993
22940
  }
21994
22941
  return output;
21995
22942
  }, installRelease2 = async (artifactPath, metadata, outputRoot) => {
21996
- const releaseRoot = join54(outputRoot, metadata.releaseId);
22943
+ const releaseRoot = join56(outputRoot, metadata.releaseId);
21997
22944
  const artifactName = "app-release.aab";
21998
- const destination = join54(releaseRoot, artifactName);
22945
+ const destination = join56(releaseRoot, artifactName);
21999
22946
  if (await pathExists7(releaseRoot)) {
22000
- const existing = requireManifestIdentity(JSON.parse(await readFile20(join54(releaseRoot, "release.json"), "utf8")), metadata);
22947
+ const existing = requireManifestIdentity(JSON.parse(await readFile21(join56(releaseRoot, "release.json"), "utf8")), metadata);
22001
22948
  const [installedBytes, installedSha256] = await Promise.all([
22002
22949
  stat3(destination).then(({ size }) => size),
22003
22950
  sha256File2(destination)
@@ -22007,25 +22954,25 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value ==
22007
22954
  }
22008
22955
  return { artifactPath: destination, metadata: existing, releaseRoot };
22009
22956
  }
22010
- await mkdir14(dirname32(releaseRoot), { recursive: true });
22011
- const staging = await mkdtemp7(join54(dirname32(releaseRoot), ".android-stage-"));
22957
+ await mkdir15(dirname33(releaseRoot), { recursive: true });
22958
+ const staging = await mkdtemp7(join56(dirname33(releaseRoot), ".android-stage-"));
22012
22959
  try {
22013
- await copyFile5(artifactPath, join54(staging, artifactName));
22960
+ await copyFile5(artifactPath, join56(staging, artifactName));
22014
22961
  const complete = {
22015
22962
  ...metadata,
22016
22963
  artifact: artifactName
22017
22964
  };
22018
- await writeFile16(join54(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
22965
+ await writeFile17(join56(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
22019
22966
  `, { flag: "wx" });
22020
- await rename14(staging, releaseRoot);
22967
+ await rename15(staging, releaseRoot);
22021
22968
  return { artifactPath: destination, metadata: complete, releaseRoot };
22022
22969
  } finally {
22023
- await rm11(staging, { force: true, recursive: true }).catch(() => {
22970
+ await rm12(staging, { force: true, recursive: true }).catch(() => {
22024
22971
  return;
22025
22972
  });
22026
22973
  }
22027
22974
  }, requireManifestIdentity = (value, expected) => {
22028
- if (!isRecord16(value)) {
22975
+ if (!isRecord17(value)) {
22029
22976
  throw new TypeError("Existing Android release metadata is invalid.");
22030
22977
  }
22031
22978
  const { artifact } = value;
@@ -22046,8 +22993,8 @@ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1, isRecord16 = (value) => typeof value ==
22046
22993
  throw new TypeError("Expo Android production builds from WSL are not available yet. Run the generated CI workflow on Linux or build from native Windows while the WSL projection is completed.");
22047
22994
  }
22048
22995
  const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host2);
22049
- const nativeDirectory = join54(options.config.nativeProjectDirectory, "android");
22050
- const manifest = requireManifest2(JSON.parse(await readFile20(join54(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
22996
+ const nativeDirectory = join56(options.config.nativeProjectDirectory, "android");
22997
+ const manifest = requireManifest2(JSON.parse(await readFile21(join56(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
22051
22998
  if (manifest.appId !== options.config.appId) {
22052
22999
  throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
22053
23000
  }
@@ -22161,7 +23108,7 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
22161
23108
  };
22162
23109
 
22163
23110
  // src/mobile/iosConformance.ts
22164
- import { readFile as readFile21, stat as stat4 } from "fs/promises";
23111
+ import { readFile as readFile22, stat as stat4 } from "fs/promises";
22165
23112
  var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
22166
23113
  const match = HMR_LINE.exec(line);
22167
23114
  if (!match)
@@ -22196,7 +23143,7 @@ var HMR_LINE, parseAbsoluteIosHmrLog = (line) => {
22196
23143
  if (Date.now() > deadline)
22197
23144
  throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
22198
23145
  options.signal?.throwIfAborted();
22199
- const contents = await readFile21(options.logPath).catch(() => Buffer.alloc(0));
23146
+ const contents = await readFile22(options.logPath).catch(() => Buffer.alloc(0));
22200
23147
  if (contents.byteLength < offset) {
22201
23148
  offset = 0;
22202
23149
  buffered = "";
@@ -22220,8 +23167,8 @@ var init_iosConformance = __esm(() => {
22220
23167
  });
22221
23168
 
22222
23169
  // src/mobile/nativeTestReport.ts
22223
- import { mkdir as mkdir15, readFile as readFile22, writeFile as writeFile17 } from "fs/promises";
22224
- import { join as join55 } from "path";
23170
+ import { mkdir as mkdir16, readFile as readFile23, writeFile as writeFile18 } from "fs/promises";
23171
+ import { join as join57 } from "path";
22225
23172
  var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sanitizeNativeReportText = (value) => value.replace(nativeCredentialPattern, "[REDACTED]").replace(bearerPattern, "Bearer [REDACTED]").replace(secretPattern, "$1$2[REDACTED]").replace(coordinatePattern, "$1$2[REDACTED]").replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/giu, "$1?[REDACTED]"), markdownCell = (value) => sanitizeNativeReportText(value).replaceAll("|", "\\|").replaceAll(`
22226
23173
  `, "<br>"), createAbsoluteNativeAutomatedChecks = (run) => {
22227
23174
  const target = `${run.targetKind} ${run.targetId}`;
@@ -22323,7 +23270,7 @@ var secretPattern, bearerPattern, coordinatePattern, nativeCredentialPattern, sa
22323
23270
  reportVersion: 1,
22324
23271
  run: options.run
22325
23272
  }), readPackageVersionForNativeReport = async (packageJsonPath) => {
22326
- const manifest = JSON.parse(await readFile22(packageJsonPath, "utf8"));
23273
+ const manifest = JSON.parse(await readFile23(packageJsonPath, "utf8"));
22327
23274
  if (typeof manifest !== "object" || manifest === null)
22328
23275
  return "unknown";
22329
23276
  const version2 = Reflect.get(manifest, "version");
@@ -22359,13 +23306,13 @@ Replace each \`NOT_RUN\` with \`PASS\`, \`FAIL\`, or \`SKIPPED\` after completin
22359
23306
  ${table(report.manualChecks)}
22360
23307
  `;
22361
23308
  }, writeAbsoluteNativeTestReport = async (directory, report) => {
22362
- await mkdir15(directory, { recursive: true });
22363
- const jsonPath = join55(directory, "report.json");
22364
- const markdownPath = join55(directory, "report.md");
23309
+ await mkdir16(directory, { recursive: true });
23310
+ const jsonPath = join57(directory, "report.json");
23311
+ const markdownPath = join57(directory, "report.md");
22365
23312
  await Promise.all([
22366
- writeFile17(jsonPath, `${JSON.stringify(report, null, 2)}
23313
+ writeFile18(jsonPath, `${JSON.stringify(report, null, 2)}
22367
23314
  `),
22368
- writeFile17(markdownPath, renderAbsoluteNativeTestReport(report))
23315
+ writeFile18(markdownPath, renderAbsoluteNativeTestReport(report))
22369
23316
  ]);
22370
23317
  return { directory, jsonPath, markdownPath };
22371
23318
  };
@@ -22649,7 +23596,7 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
22649
23596
  throw new TypeError("Google Play publisher returned an invalid Android versionCode.");
22650
23597
  }
22651
23598
  return versionCode;
22652
- }, isRecord17 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord17(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
23599
+ }, isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isPublisher = (value) => isRecord18(value) && typeof value.publish === "function", publisherModulePath = (projectRoot, requested) => {
22653
23600
  const root = resolve41(projectRoot);
22654
23601
  const path = resolve41(root, requested);
22655
23602
  const projectRelative = relative28(root, path);
@@ -22663,7 +23610,7 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
22663
23610
  throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
22664
23611
  });
22665
23612
  const loaded = await import(pathToFileURL2(modulePath).href);
22666
- const publisher = isRecord17(loaded) ? loaded.default ?? loaded.registry : undefined;
23613
+ const publisher = isRecord18(loaded) ? loaded.default ?? loaded.registry : undefined;
22667
23614
  if (!isPublisher(publisher)) {
22668
23615
  throw new TypeError("Native release registry module must default-export a registry with publish(options).");
22669
23616
  }
@@ -22720,8 +23667,8 @@ var prepareAbsoluteIosRelease = async (publisher, options) => {
22720
23667
  var init_releasePublisher = () => {};
22721
23668
 
22722
23669
  // src/mobile/mobileInspect.ts
22723
- import { access as access14, readFile as readFile23 } from "fs/promises";
22724
- import { join as join56, relative as relative29, resolve as resolve42 } from "path";
23670
+ import { access as access14, readFile as readFile24 } from "fs/promises";
23671
+ import { join as join58, relative as relative29, resolve as resolve42 } from "path";
22725
23672
  var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), portablePath2 = (projectRoot, path) => {
22726
23673
  const value = relative29(resolve42(projectRoot), resolve42(path)).replaceAll("\\", "/");
22727
23674
  return value || ".";
@@ -22733,7 +23680,7 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, isObject3 = (va
22733
23680
  return false;
22734
23681
  }
22735
23682
  }, readObject2 = async (path) => {
22736
- const value = JSON.parse(await readFile23(path, "utf8"));
23683
+ const value = JSON.parse(await readFile24(path, "utf8"));
22737
23684
  if (!isObject3(value))
22738
23685
  throw new TypeError("JSON root must be an object.");
22739
23686
  return value;
@@ -22743,13 +23690,13 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, isObject3 = (va
22743
23690
  for (const [name, declared] of Object.entries(value).filter((entry) => typeof entry[1] === "string"))
22744
23691
  declarations.set(name, declared);
22745
23692
  }, packageInspections = async (projectRoot, additionalNames) => {
22746
- const project = await readObject2(join56(projectRoot, "package.json"));
23693
+ const project = await readObject2(join58(projectRoot, "package.json"));
22747
23694
  const declarations = new Map;
22748
23695
  for (const field of ["dependencies", "devDependencies"])
22749
23696
  addPackageDeclarations(declarations, project[field]);
22750
23697
  const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
22751
23698
  return Promise.all(names.map(async (name) => {
22752
- const installedManifest = await readObject2(join56(projectRoot, "node_modules", name, "package.json")).catch(() => {
23699
+ const installedManifest = await readObject2(join58(projectRoot, "node_modules", name, "package.json")).catch(() => {
22753
23700
  return;
22754
23701
  });
22755
23702
  const installed = installedManifest?.version;
@@ -22807,7 +23754,7 @@ var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1, MOBILE_PACKAGE_NAMES, isObject3 = (va
22807
23754
  },
22808
23755
  format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
22809
23756
  nativeProjects: await Promise.all(config.platforms.map(async (platform6) => {
22810
- const path = join56(config.nativeProjectDirectory, platform6);
23757
+ const path = join58(config.nativeProjectDirectory, platform6);
22811
23758
  return {
22812
23759
  initialized: await pathExists8(path),
22813
23760
  path: portablePath2(projectRoot, path),
@@ -22878,8 +23825,8 @@ var init_mobileInspect = __esm(() => {
22878
23825
 
22879
23826
  // src/mobile/ciWorkflow.ts
22880
23827
  import { existsSync as existsSync43 } from "fs";
22881
- import { access as access15, mkdir as mkdir16, readFile as readFile24, writeFile as writeFile18 } from "fs/promises";
22882
- import { dirname as dirname33, extname as extname9, relative as relative30, resolve as resolve43, sep as sep9 } from "path";
23828
+ import { access as access15, mkdir as mkdir17, readFile as readFile25, writeFile as writeFile19 } from "fs/promises";
23829
+ import { dirname as dirname34, extname as extname9, relative as relative30, resolve as resolve43, sep as sep9 } from "path";
22883
23830
  var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1, SECRET_NAME_PATTERN, CI_ENV_INDENTATION = 6, RESERVED_SECRET_NAMES, exists4 = async (path) => {
22884
23831
  try {
22885
23832
  await access15(path);
@@ -23292,12 +24239,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
23292
24239
  }, writeAbsoluteMobileGithubWorkflow = async (options) => {
23293
24240
  const path = workflowOutputPath(options.projectRoot, options.outputPath);
23294
24241
  const generated = createAbsoluteMobileGithubWorkflow(options);
23295
- const previous = await exists4(path) ? await readFile24(path, "utf8") : undefined;
24242
+ const previous = await exists4(path) ? await readFile25(path, "utf8") : undefined;
23296
24243
  if (previous !== undefined && previous !== generated.workflow && !options.force)
23297
24244
  throw new TypeError(`${relative30(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
23298
24245
  if (previous !== generated.workflow) {
23299
- await mkdir16(dirname33(path), { recursive: true });
23300
- await writeFile18(path, generated.workflow);
24246
+ await mkdir17(dirname34(path), { recursive: true });
24247
+ await writeFile19(path, generated.workflow);
23301
24248
  }
23302
24249
  return {
23303
24250
  changed: previous !== generated.workflow,
@@ -23358,20 +24305,20 @@ var init_ciWorkflow = __esm(() => {
23358
24305
  import { createHash as createHash17, sign as sign2, verify as verify2 } from "crypto";
23359
24306
  import {
23360
24307
  cp as cp4,
23361
- mkdir as mkdir17,
24308
+ mkdir as mkdir18,
23362
24309
  mkdtemp as mkdtemp8,
23363
- readdir as readdir7,
23364
- readFile as readFile25,
23365
- rename as rename15,
23366
- rm as rm12,
24310
+ readdir as readdir8,
24311
+ readFile as readFile26,
24312
+ rename as rename16,
24313
+ rm as rm13,
23367
24314
  stat as stat5,
23368
- writeFile as writeFile19
24315
+ writeFile as writeFile20
23369
24316
  } from "fs/promises";
23370
- import { dirname as dirname34, join as join57, relative as relative31, resolve as resolve44 } from "path";
24317
+ import { dirname as dirname35, join as join59, relative as relative31, resolve as resolve44 } from "path";
23371
24318
  var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2562 = (value) => createHash17("sha256").update(value).digest("hex"), listFiles = async (root, directory = root) => {
23372
- const entries = await readdir7(directory, { withFileTypes: true });
24319
+ const entries = await readdir8(directory, { withFileTypes: true });
23373
24320
  const paths = await Promise.all(entries.map(async (entry) => {
23374
- const path = join57(directory, entry.name);
24321
+ const path = join59(directory, entry.name);
23375
24322
  if (entry.isDirectory())
23376
24323
  return listFiles(root, path);
23377
24324
  if (!entry.isFile())
@@ -23380,10 +24327,10 @@ var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2
23380
24327
  }));
23381
24328
  return paths.flat().sort((left, right) => left.localeCompare(right));
23382
24329
  }, inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
23383
- const source = join57(root, path);
24330
+ const source = join59(root, path);
23384
24331
  const [metadata, contents] = await Promise.all([
23385
24332
  stat5(source),
23386
- readFile25(source)
24333
+ readFile26(source)
23387
24334
  ]);
23388
24335
  return { bytes: metadata.size, path, sha256: sha2562(contents) };
23389
24336
  })), releaseIdFor = (value) => `amu_${sha2562(canonicalizeAbsoluteMobileUpdate(value))}`, buildAbsoluteMobileUpdate = async (options) => {
@@ -23416,29 +24363,29 @@ var UPDATE_MANIFEST_FILE = "update.json", UPDATE_FILES_DIRECTORY = "files", sha2
23416
24363
  value: signature.toString("base64")
23417
24364
  }
23418
24365
  });
23419
- await mkdir17(outputRoot, { recursive: true });
23420
- const outputDirectory = join57(outputRoot, manifest.releaseId);
23421
- const staging = await mkdtemp8(join57(outputRoot, ".stage-"));
24366
+ await mkdir18(outputRoot, { recursive: true });
24367
+ const outputDirectory = join59(outputRoot, manifest.releaseId);
24368
+ const staging = await mkdtemp8(join59(outputRoot, ".stage-"));
23422
24369
  try {
23423
- await cp4(bundleDirectory, join57(staging, UPDATE_FILES_DIRECTORY), {
24370
+ await cp4(bundleDirectory, join59(staging, UPDATE_FILES_DIRECTORY), {
23424
24371
  force: true,
23425
24372
  recursive: true
23426
24373
  });
23427
- await writeFile19(join57(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
24374
+ await writeFile20(join59(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
23428
24375
  `);
23429
- await rename15(staging, outputDirectory);
24376
+ await rename16(staging, outputDirectory);
23430
24377
  } catch (error) {
23431
- await rm12(staging, { force: true, recursive: true });
24378
+ await rm13(staging, { force: true, recursive: true });
23432
24379
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
23433
24380
  throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
23434
24381
  throw error;
23435
24382
  }
23436
24383
  return {
23437
24384
  manifest,
23438
- manifestPath: join57(outputDirectory, UPDATE_MANIFEST_FILE),
24385
+ manifestPath: join59(outputDirectory, UPDATE_MANIFEST_FILE),
23439
24386
  outputDirectory
23440
24387
  };
23441
- }, readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile25(join57(resolve44(directory), UPDATE_MANIFEST_FILE), "utf8"))), verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
24388
+ }, readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile26(join59(resolve44(directory), UPDATE_MANIFEST_FILE), "utf8"))), verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
23442
24389
  const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
23443
24390
  const valid = verify2("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
23444
24391
  if (!valid)
@@ -23450,8 +24397,8 @@ var init_updateSigning = __esm(() => {
23450
24397
  });
23451
24398
 
23452
24399
  // src/mobile/expoUpdate.ts
23453
- import { access as access16, mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "fs/promises";
23454
- import { dirname as dirname35, extname as extname10, join as join58 } from "path";
24400
+ import { access as access16, mkdir as mkdir19, readFile as readFile27, writeFile as writeFile21 } from "fs/promises";
24401
+ import { dirname as dirname36, extname as extname10, join as join60 } from "path";
23455
24402
  var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json", ABSOLUTE_EXPO_UPDATE_FORMAT = 1, object4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), safePath = (value, field) => {
23456
24403
  if (typeof value !== "string" || value.length === 0)
23457
24404
  throw new TypeError(`Expo update ${field} is invalid.`);
@@ -23509,7 +24456,7 @@ var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json", ABSOLUTE_EXP
23509
24456
  runtimeVersion: value.runtimeVersion
23510
24457
  };
23511
24458
  }, finalizeAbsoluteExpoUpdateExport = async (options) => {
23512
- const metadata = JSON.parse(await readFile26(join58(options.exportDirectory, "metadata.json"), "utf8"));
24459
+ const metadata = JSON.parse(await readFile27(join60(options.exportDirectory, "metadata.json"), "utf8"));
23513
24460
  if (!object4(metadata) || !object4(metadata.fileMetadata))
23514
24461
  throw new TypeError("Expo export metadata.json is invalid.");
23515
24462
  const descriptor = parseAbsoluteExpoUpdateDescriptor({
@@ -23520,12 +24467,12 @@ var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json", ABSOLUTE_EXP
23520
24467
  runtimeVersion: options.runtimeVersion
23521
24468
  });
23522
24469
  const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
23523
- await Promise.all(referenced.map(({ path }) => access16(join58(options.exportDirectory, path)).catch(() => {
24470
+ await Promise.all(referenced.map(({ path }) => access16(join60(options.exportDirectory, path)).catch(() => {
23524
24471
  throw new TypeError(`Expo export metadata references missing asset ${path}.`);
23525
24472
  })));
23526
- const destination = join58(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
23527
- await mkdir18(dirname35(destination), { recursive: true });
23528
- await writeFile20(destination, `${JSON.stringify(descriptor, null, "\t")}
24473
+ const destination = join60(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
24474
+ await mkdir19(dirname36(destination), { recursive: true });
24475
+ await writeFile21(destination, `${JSON.stringify(descriptor, null, "\t")}
23529
24476
  `);
23530
24477
  return { descriptor, path: destination };
23531
24478
  };
@@ -40199,8 +41146,8 @@ var require_main = __commonJS(function(exports) {
40199
41146
  });
40200
41147
 
40201
41148
  // src/mobile/expoCodeSigning.ts
40202
- import { access as access17, mkdir as mkdir19, writeFile as writeFile21 } from "fs/promises";
40203
- import { dirname as dirname36, isAbsolute as isAbsolute9, relative as relative32, resolve as resolve45, sep as sep10 } from "path";
41149
+ import { access as access17, mkdir as mkdir20, writeFile as writeFile22 } from "fs/promises";
41150
+ import { dirname as dirname37, isAbsolute as isAbsolute9, relative as relative32, resolve as resolve45, sep as sep10 } from "path";
40204
41151
  var import_code_signing_certificates, exists5 = async (path) => access17(path).then(() => true).catch(() => false), inside = (root, path) => {
40205
41152
  const location = relative32(root, path);
40206
41153
  return location === "" || location !== ".." && !location.startsWith(`..${sep10}`) && !isAbsolute9(location);
@@ -40208,7 +41155,7 @@ var import_code_signing_certificates, exists5 = async (path) => access17(path).t
40208
41155
  const root = resolve45(options.projectRoot);
40209
41156
  const certificatePath = resolve45(root, options.certificatePath);
40210
41157
  const privateKeyPath = resolve45(root, options.privateKeyPath);
40211
- const publicKeyPath = resolve45(root, options.publicKeyPath ?? resolve45(dirname36(privateKeyPath), "public-key.pem"));
41158
+ const publicKeyPath = resolve45(root, options.publicKeyPath ?? resolve45(dirname37(privateKeyPath), "public-key.pem"));
40212
41159
  if (!inside(root, certificatePath))
40213
41160
  throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
40214
41161
  if (inside(root, privateKeyPath) || inside(root, publicKeyPath))
@@ -40238,11 +41185,11 @@ var import_code_signing_certificates, exists5 = async (path) => access17(path).t
40238
41185
  import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
40239
41186
  const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
40240
41187
  const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
40241
- await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir19(dirname36(path), { recursive: true })));
41188
+ await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir20(dirname37(path), { recursive: true })));
40242
41189
  await Promise.all([
40243
- writeFile21(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
40244
- writeFile21(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
40245
- writeFile21(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
41190
+ writeFile22(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
41191
+ writeFile22(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
41192
+ writeFile22(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
40246
41193
  ]);
40247
41194
  return {
40248
41195
  certificatePath,
@@ -40321,18 +41268,18 @@ __export(exports_mobile, {
40321
41268
  });
40322
41269
  import {
40323
41270
  access as access19,
40324
- mkdir as mkdir20,
41271
+ mkdir as mkdir21,
40325
41272
  mkdtemp as mkdtemp9,
40326
- readFile as readFile27,
40327
- rm as rm13,
40328
- writeFile as writeFile22
41273
+ readFile as readFile28,
41274
+ rm as rm14,
41275
+ writeFile as writeFile23
40329
41276
  } from "fs/promises";
40330
41277
  import { createPublicKey as createPublicKey3 } from "crypto";
40331
- import { join as join59, relative as relative34, resolve as resolve47 } from "path";
41278
+ import { join as join61, relative as relative34, resolve as resolve47 } from "path";
40332
41279
  import { createInterface } from "readline/promises";
40333
- var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), expoPublicConfig = (config) => {
41280
+ var NOT_FOUND4 = -1, isRecord19 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), expoPublicConfig = (config) => {
40334
41281
  const result = { ...config };
40335
- if (!isRecord18(result.updates))
41282
+ if (!isRecord19(result.updates))
40336
41283
  return result;
40337
41284
  const {
40338
41285
  codeSigningCertificate: _codeSigningCertificate,
@@ -40342,21 +41289,21 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40342
41289
  result.updates = publicUpdates;
40343
41290
  return result;
40344
41291
  }, CAPACITOR_PACKAGES, CAPACITOR_PACKAGE_SPECS, CAPACITOR_SYNC_PACKAGE_SPECS, packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@")), directProjectPackages = async (projectRoot) => {
40345
- const manifest = JSON.parse(await readFile27(join59(projectRoot, "package.json"), "utf8"));
40346
- if (!isRecord18(manifest))
41292
+ const manifest = JSON.parse(await readFile28(join61(projectRoot, "package.json"), "utf8"));
41293
+ if (!isRecord19(manifest))
40347
41294
  throw new TypeError("Application package.json must contain an object.");
40348
41295
  const names = new Set;
40349
41296
  for (const field of ["dependencies", "devDependencies"]) {
40350
41297
  const dependencies = Reflect.get(manifest, field);
40351
- if (isRecord18(dependencies))
41298
+ if (isRecord19(dependencies))
40352
41299
  for (const name of Object.keys(dependencies))
40353
41300
  names.add(name);
40354
41301
  }
40355
41302
  return names;
40356
41303
  }, resolvedPackageVersion = async (projectRoot, packageName) => {
40357
41304
  try {
40358
- const manifest = JSON.parse(await readFile27(join59(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
40359
- return isRecord18(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
41305
+ const manifest = JSON.parse(await readFile28(join61(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
41306
+ return isRecord19(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
40360
41307
  } catch {
40361
41308
  return;
40362
41309
  }
@@ -40397,7 +41344,7 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40397
41344
  }
40398
41345
  return value;
40399
41346
  }, capacitorExecutable = async (projectRoot) => {
40400
- const executable = join59(projectRoot, "node_modules", ".bin", "cap");
41347
+ const executable = join61(projectRoot, "node_modules", ".bin", "cap");
40401
41348
  try {
40402
41349
  await access19(executable);
40403
41350
  return executable;
@@ -40417,7 +41364,7 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40417
41364
  throw new TypeError(`Capacitor exited with status ${exitCode}.`);
40418
41365
  }
40419
41366
  }, runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform7) => pending.then(() => runCapacitor(projectRoot, [command, platform7])), Promise.resolve()), expoExecutable = async (project) => {
40420
- const executable = join59(project, "node_modules", ".bin", "expo");
41367
+ const executable = join61(project, "node_modules", ".bin", "expo");
40421
41368
  try {
40422
41369
  await access19(executable);
40423
41370
  return executable;
@@ -40448,14 +41395,14 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40448
41395
  throw new TypeError(`Expo exited with status ${exitCode}.`);
40449
41396
  }, ensureExpoPackages = async (project, args) => {
40450
41397
  try {
40451
- const manifest = JSON.parse(await readFile27(join59(project, "package.json"), "utf8"));
41398
+ const manifest = JSON.parse(await readFile28(join61(project, "package.json"), "utf8"));
40452
41399
  const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
40453
41400
  if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
40454
41401
  throw new TypeError("Generated Expo dependencies are invalid.");
40455
41402
  await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
40456
41403
  if (typeof expected !== "string")
40457
41404
  throw new TypeError("Generated Expo dependency version is invalid.");
40458
- const installed = JSON.parse(await readFile27(join59(project, "node_modules", name, "package.json"), "utf8"));
41405
+ const installed = JSON.parse(await readFile28(join61(project, "node_modules", name, "package.json"), "utf8"));
40459
41406
  const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
40460
41407
  if (typeof actual !== "string")
40461
41408
  throw new TypeError("Installed Expo dependency version is invalid.");
@@ -40604,6 +41551,7 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40604
41551
  await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile);
40605
41552
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile);
40606
41553
  await applyAbsoluteNativeUpdates(mobile);
41554
+ await applyAbsoluteNativeObservability(mobile);
40607
41555
  }, sync = async (args) => {
40608
41556
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
40609
41557
  if (mobile.engine === "expo") {
@@ -40637,6 +41585,7 @@ var NOT_FOUND4 = -1, isRecord18 = (value) => typeof value === "object" && value
40637
41585
  await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile, platforms);
40638
41586
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, platforms);
40639
41587
  await applyAbsoluteNativeUpdates(mobile, platforms);
41588
+ await applyAbsoluteNativeObservability(mobile, platforms);
40640
41589
  }, associations = async (args) => {
40641
41590
  const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
40642
41591
  const outputDirectory = resolve47(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
@@ -40793,9 +41742,9 @@ Mobile release security and compliance checks failed.`);
40793
41742
  temporaryDirectory: undefined
40794
41743
  };
40795
41744
  await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
40796
- const temporaryParent = join59(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
40797
- await mkdir20(temporaryParent, { recursive: true });
40798
- const temporaryDirectory = await mkdtemp9(join59(temporaryParent, ".stage-"));
41745
+ const temporaryParent = join61(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
41746
+ await mkdir21(temporaryParent, { recursive: true });
41747
+ const temporaryDirectory = await mkdtemp9(join61(temporaryParent, ".stage-"));
40799
41748
  try {
40800
41749
  await runExpo(mobile.nativeProjectDirectory, [
40801
41750
  "export",
@@ -40807,9 +41756,9 @@ Mobile release security and compliance checks failed.`);
40807
41756
  temporaryDirectory,
40808
41757
  "--clear"
40809
41758
  ], { production: true });
40810
- const generatedApp = JSON.parse(await readFile27(join59(mobile.nativeProjectDirectory, "app.json"), "utf8"));
40811
- const expoConfig = isRecord18(generatedApp) ? generatedApp.expo : undefined;
40812
- if (!isRecord18(expoConfig))
41759
+ const generatedApp = JSON.parse(await readFile28(join61(mobile.nativeProjectDirectory, "app.json"), "utf8"));
41760
+ const expoConfig = isRecord19(generatedApp) ? generatedApp.expo : undefined;
41761
+ if (!isRecord19(expoConfig))
40813
41762
  throw new TypeError("Generated Expo app configuration is invalid.");
40814
41763
  const publicExpoConfig = expoPublicConfig(expoConfig);
40815
41764
  const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
@@ -40825,7 +41774,7 @@ Mobile release security and compliance checks failed.`);
40825
41774
  temporaryDirectory
40826
41775
  };
40827
41776
  } catch (error) {
40828
- await rm13(temporaryDirectory, { force: true, recursive: true });
41777
+ await rm14(temporaryDirectory, { force: true, recursive: true });
40829
41778
  throw error;
40830
41779
  }
40831
41780
  }, buildMobileUpdate = async (args) => {
@@ -40849,11 +41798,11 @@ Mobile release security and compliance checks failed.`);
40849
41798
  let expoExportDirectory;
40850
41799
  try {
40851
41800
  await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath2, { prepareOnly: true });
40852
- const embedded = JSON.parse(await readFile27(join59(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
40853
- const runtimeFingerprint = isRecord18(embedded) ? embedded.nativeRuntime : undefined;
41801
+ const embedded = JSON.parse(await readFile28(join61(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
41802
+ const runtimeFingerprint = isRecord19(embedded) ? embedded.nativeRuntime : undefined;
40854
41803
  if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
40855
41804
  throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
40856
- const privateKey = await readFile27(resolve47(projectRoot, signingKeyPath));
41805
+ const privateKey = await readFile28(resolve47(projectRoot, signingKeyPath));
40857
41806
  const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
40858
41807
  const derivedPublicKey = createPublicKey3(privateKey).export({
40859
41808
  format: "der",
@@ -40874,7 +41823,7 @@ Mobile release security and compliance checks failed.`);
40874
41823
  channel: mobile.updates.channel,
40875
41824
  classification,
40876
41825
  keyId,
40877
- outputDirectory: valueAfter(args, "--outdir") ?? join59(projectRoot, ".absolutejs", "mobile", "updates"),
41826
+ outputDirectory: valueAfter(args, "--outdir") ?? join61(projectRoot, ".absolutejs", "mobile", "updates"),
40878
41827
  privateKey,
40879
41828
  runtimeFingerprint
40880
41829
  });
@@ -40886,7 +41835,7 @@ Mobile release security and compliance checks failed.`);
40886
41835
  return result;
40887
41836
  } finally {
40888
41837
  if (expoExportDirectory)
40889
- await rm13(expoExportDirectory, { force: true, recursive: true });
41838
+ await rm14(expoExportDirectory, { force: true, recursive: true });
40890
41839
  sendTelemetryEvent("mobile:update-build", {
40891
41840
  classification: classification ?? "invalid",
40892
41841
  durationMs: Math.round(performance.now() - startedAt),
@@ -41147,6 +42096,7 @@ Mobile release security and compliance checks failed.`);
41147
42096
  await applyAbsoluteNativeDeviceCapabilities(projectRoot, mobile, ["ios"]);
41148
42097
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
41149
42098
  await applyAbsoluteNativeUpdates(mobile, ["ios"]);
42099
+ await applyAbsoluteNativeObservability(mobile, ["ios"]);
41150
42100
  }, prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot), prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
41151
42101
  await writeAbsoluteCapacitorConfig(mobile, { projectRoot });
41152
42102
  await runCapacitorForPlatforms(projectRoot, "sync", ["android"]);
@@ -41156,6 +42106,7 @@ Mobile release security and compliance checks failed.`);
41156
42106
  ]);
41157
42107
  await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["android"]);
41158
42108
  await applyAbsoluteNativeUpdates(mobile, ["android"]);
42109
+ await applyAbsoluteNativeObservability(mobile, ["android"]);
41159
42110
  }, prepareAndroidReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoAndroidReleaseProject(mobile, projectRoot, args) : prepareCapacitorAndroidReleaseProject(mobile, projectRoot), buildAndroid = async (args, prepareVersionCode) => {
41160
42111
  const configPath2 = valueAfter(args, "--config");
41161
42112
  const { mobile, projectRoot } = await loadMobile(configPath2);
@@ -41183,7 +42134,7 @@ Mobile release security and compliance checks failed.`);
41183
42134
  const durationMs = Math.round(performance.now() - startedAt);
41184
42135
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
41185
42136
  console.log(`Artifact: ${release.artifactPath}`);
41186
- console.log(`Metadata: ${join59(release.releaseRoot, "release.json")}`);
42137
+ console.log(`Metadata: ${join61(release.releaseRoot, "release.json")}`);
41187
42138
  return release;
41188
42139
  } finally {
41189
42140
  sendTelemetryEvent("mobile:android-release-build", {
@@ -41331,7 +42282,7 @@ Mobile release security and compliance checks failed.`);
41331
42282
  const durationMs = Math.round(performance.now() - startedAt);
41332
42283
  console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
41333
42284
  console.log(`Artifact: ${release.artifactPath}`);
41334
- console.log(`Metadata: ${join59(release.releaseRoot, "release.json")}`);
42285
+ console.log(`Metadata: ${join61(release.releaseRoot, "release.json")}`);
41335
42286
  return release;
41336
42287
  } finally {
41337
42288
  stopListeningForCancellation();
@@ -41440,7 +42391,7 @@ Mobile release security and compliance checks failed.`);
41440
42391
  checks.push({
41441
42392
  id: "sync.storage-schema",
41442
42393
  label: `Offline schema ${schema.components.map((component2) => `${component2.id}@${component2.version}`).join(", ")}`,
41443
- path: join59(projectRoot, "package.json"),
42394
+ path: join61(projectRoot, "package.json"),
41444
42395
  platform: "host",
41445
42396
  status: "pass"
41446
42397
  });
@@ -41448,7 +42399,7 @@ Mobile release security and compliance checks failed.`);
41448
42399
  checks.push({
41449
42400
  id: "sync.storage-schema",
41450
42401
  label: "Offline schema metadata is invalid",
41451
- path: join59(projectRoot, "package.json"),
42402
+ path: join61(projectRoot, "package.json"),
41452
42403
  platform: "host",
41453
42404
  remediation: error instanceof Error ? error.message : String(error),
41454
42405
  status: "fail"
@@ -41601,12 +42552,12 @@ Emulator setup verification:`);
41601
42552
  timeoutMs
41602
42553
  });
41603
42554
  }, writeAndroidFailureArtifacts = async (options) => {
41604
- await mkdir20(options.artifactRoot, { recursive: true });
41605
- const screenshot = options.session ? await options.session.screenshot(join59(options.artifactRoot, "android-failure.png")).catch(() => {
42555
+ await mkdir21(options.artifactRoot, { recursive: true });
42556
+ const screenshot = options.session ? await options.session.screenshot(join61(options.artifactRoot, "android-failure.png")).catch(() => {
41606
42557
  return;
41607
42558
  }) : undefined;
41608
- const diagnosticPath = join59(options.artifactRoot, "android-failure.json");
41609
- await writeFile22(diagnosticPath, `${JSON.stringify({
42559
+ const diagnosticPath = join61(options.artifactRoot, "android-failure.json");
42560
+ await writeFile23(diagnosticPath, `${JSON.stringify({
41610
42561
  diagnostics: options.session?.diagnostics ?? [],
41611
42562
  error: options.error instanceof Error ? options.error.message : String(options.error),
41612
42563
  platform: "android",
@@ -41670,7 +42621,7 @@ Emulator setup verification:`);
41670
42621
  console.log(JSON.stringify(report, null, 2));
41671
42622
  else
41672
42623
  printAndroidTestReport(report);
41673
- const screenshot = reportRoot ? await session.screenshot(join59(artifactRoot, "android-emulator.png")) : undefined;
42624
+ const screenshot = reportRoot ? await session.screenshot(join61(artifactRoot, "android-emulator.png")) : undefined;
41674
42625
  await writeRequestedAndroidReport({
41675
42626
  adb,
41676
42627
  args,
@@ -41762,7 +42713,7 @@ Emulator setup verification:`);
41762
42713
  return;
41763
42714
  });
41764
42715
  const status2 = response?.ok ? await response.json().catch(() => null) : null;
41765
- const targets = isRecord18(status2) && isRecord18(status2.connectedTargets) ? status2.connectedTargets : undefined;
42716
+ const targets = isRecord19(status2) && isRecord19(status2.connectedTargets) ? status2.connectedTargets : undefined;
41766
42717
  if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
41767
42718
  return;
41768
42719
  await Bun.sleep(100);
@@ -41859,8 +42810,8 @@ Emulator setup verification:`);
41859
42810
  throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
41860
42811
  return result;
41861
42812
  }, writeIosFailureArtifacts = async (options) => {
41862
- await mkdir20(options.artifactRoot, { recursive: true });
41863
- const screenshot = join59(options.artifactRoot, "ios-failure.png");
42813
+ await mkdir21(options.artifactRoot, { recursive: true });
42814
+ const screenshot = join61(options.artifactRoot, "ios-failure.png");
41864
42815
  const screenshotResult = captureCommand4([
41865
42816
  options.xcrun,
41866
42817
  "simctl",
@@ -41869,8 +42820,8 @@ Emulator setup verification:`);
41869
42820
  "screenshot",
41870
42821
  screenshot
41871
42822
  ]);
41872
- const diagnosticPath = join59(options.artifactRoot, "ios-failure.json");
41873
- await writeFile22(diagnosticPath, `${JSON.stringify({
42823
+ const diagnosticPath = join61(options.artifactRoot, "ios-failure.json");
42824
+ await writeFile23(diagnosticPath, `${JSON.stringify({
41874
42825
  appId: options.appId,
41875
42826
  error: options.error instanceof Error ? options.error.message : String(options.error),
41876
42827
  platform: "ios",
@@ -42115,8 +43066,8 @@ Emulator setup verification:`);
42115
43066
  mobile.appId
42116
43067
  ], "iOS app launch");
42117
43068
  await waitForIosHmrClient({ https, port, timeoutMs });
42118
- await mkdir20(artifactRoot, { recursive: true });
42119
- const screenshot = join59(artifactRoot, "ios-simulator.png");
43069
+ await mkdir21(artifactRoot, { recursive: true });
43070
+ const screenshot = join61(artifactRoot, "ios-simulator.png");
42120
43071
  requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
42121
43072
  const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
42122
43073
  const report = {
@@ -42292,6 +43243,7 @@ var init_mobile = __esm(() => {
42292
43243
  init_nativeDeviceCapabilities();
42293
43244
  init_nativeBackgroundSync();
42294
43245
  init_nativeUpdates();
43246
+ init_nativeObservability();
42295
43247
  init_emulatorDoctor();
42296
43248
  init_emulatorInstaller();
42297
43249
  init_associationFiles();
@@ -42359,9 +43311,9 @@ var exports_typecheck = {};
42359
43311
  __export(exports_typecheck, {
42360
43312
  typecheck: () => typecheck
42361
43313
  });
42362
- import { resolve as resolve48, join as join60 } from "path";
43314
+ import { resolve as resolve48, join as join62 } from "path";
42363
43315
  import { existsSync as existsSync44, readFileSync as readFileSync41 } from "fs";
42364
- import { mkdir as mkdir21, writeFile as writeFile23 } from "fs/promises";
43316
+ import { mkdir as mkdir22, writeFile as writeFile24 } from "fs/promises";
42365
43317
  var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve48(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
42366
43318
  if (!existsSync44(resolveConfigPath(configPath2))) {
42367
43319
  const defaultService = {};
@@ -42468,8 +43420,8 @@ Found ${errorCount} error${suffix}.`;
42468
43420
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
42469
43421
  process.exit(1);
42470
43422
  }
42471
- const vueTsconfigPath = join60(cacheDir, "tsconfig.vue-check.json");
42472
- await writeFile23(vueTsconfigPath, JSON.stringify({
43423
+ const vueTsconfigPath = join62(cacheDir, "tsconfig.vue-check.json");
43424
+ await writeFile24(vueTsconfigPath, JSON.stringify({
42473
43425
  compilerOptions: {
42474
43426
  rootDir: ".."
42475
43427
  },
@@ -42488,7 +43440,7 @@ Found ${errorCount} error${suffix}.`;
42488
43440
  ...base,
42489
43441
  "--incremental",
42490
43442
  "--tsBuildInfoFile",
42491
- join60(cacheDir, "vue-tsc.tsbuildinfo")
43443
+ join62(cacheDir, "vue-tsc.tsbuildinfo")
42492
43444
  ]);
42493
43445
  if (cached.exitCode === 0 || cached.output.length > 0)
42494
43446
  return cached;
@@ -42499,8 +43451,8 @@ Found ${errorCount} error${suffix}.`;
42499
43451
  console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
42500
43452
  process.exit(1);
42501
43453
  }
42502
- const angularTsconfigPath = join60(cacheDir, "tsconfig.angular-check.json");
42503
- await writeFile23(angularTsconfigPath, JSON.stringify({
43454
+ const angularTsconfigPath = join62(cacheDir, "tsconfig.angular-check.json");
43455
+ await writeFile24(angularTsconfigPath, JSON.stringify({
42504
43456
  angularCompilerOptions: {
42505
43457
  strictTemplates: true
42506
43458
  },
@@ -42519,8 +43471,8 @@ Found ${errorCount} error${suffix}.`;
42519
43471
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
42520
43472
  process.exit(1);
42521
43473
  }
42522
- const tscConfigPath = join60(cacheDir, "tsconfig.typecheck.json");
42523
- return writeFile23(tscConfigPath, JSON.stringify({
43474
+ const tscConfigPath = join62(cacheDir, "tsconfig.typecheck.json");
43475
+ return writeFile24(tscConfigPath, JSON.stringify({
42524
43476
  compilerOptions: {
42525
43477
  rootDir: ".."
42526
43478
  },
@@ -42534,7 +43486,7 @@ Found ${errorCount} error${suffix}.`;
42534
43486
  resolve48(tscConfigPath),
42535
43487
  "--incremental",
42536
43488
  "--tsBuildInfoFile",
42537
- join60(cacheDir, "tsc.tsbuildinfo"),
43489
+ join62(cacheDir, "tsc.tsbuildinfo"),
42538
43490
  "--pretty"
42539
43491
  ]));
42540
43492
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -42543,8 +43495,8 @@ Found ${errorCount} error${suffix}.`;
42543
43495
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
42544
43496
  process.exit(1);
42545
43497
  }
42546
- const svelteTsconfigPath = join60(cacheDir, "tsconfig.svelte-check.json");
42547
- await writeFile23(svelteTsconfigPath, JSON.stringify({
43498
+ const svelteTsconfigPath = join62(cacheDir, "tsconfig.svelte-check.json");
43499
+ await writeFile24(svelteTsconfigPath, JSON.stringify({
42548
43500
  extends: resolve48("tsconfig.json"),
42549
43501
  files: ABSOLUTE_TYPECHECK_FILES,
42550
43502
  include: [`../${svelteDir}/**/*`]
@@ -42573,7 +43525,7 @@ Found ${errorCount} error${suffix}.`;
42573
43525
  ...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
42574
43526
  ];
42575
43527
  const cacheDir = ".absolutejs";
42576
- await mkdir21(cacheDir, { recursive: true });
43528
+ await mkdir22(cacheDir, { recursive: true });
42577
43529
  const checks = [];
42578
43530
  checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
42579
43531
  for (const svelteDir of hasSvelte ? svelteDirs : []) {
@@ -42802,7 +43754,7 @@ import {
42802
43754
  writeFileSync as writeFileSync4
42803
43755
  } from "fs";
42804
43756
  import { tmpdir as tmpdir2 } from "os";
42805
- import { join as join17, resolve as resolvePath2 } from "path";
43757
+ import { join as join18, resolve as resolvePath2 } from "path";
42806
43758
 
42807
43759
  // src/dev/tunnel/client.ts
42808
43760
  var RECONNECT_DELAY_MS = 2000;
@@ -43312,7 +44264,7 @@ init_expoProject();
43312
44264
  init_iosPhysicalDeviceTransport();
43313
44265
  import { spawn } from "child_process";
43314
44266
  import { access as access2 } from "fs/promises";
43315
- import { join as join9 } from "path";
44267
+ import { join as join10 } from "path";
43316
44268
  var METRO_READY_TIMEOUT_MS = 60000;
43317
44269
  var PROCESS_CLOSE_TIMEOUT_MS = 2000;
43318
44270
  var commandEnvironment = (options) => ({
@@ -43325,7 +44277,7 @@ var commandEnvironment = (options) => ({
43325
44277
  ...options.metroHost ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.metroHost } : {}
43326
44278
  });
43327
44279
  var absoluteExpoExecutable = async (project) => {
43328
- const executable = join9(project, "node_modules", ".bin", "expo");
44280
+ const executable = join10(project, "node_modules", ".bin", "expo");
43329
44281
  try {
43330
44282
  await access2(executable);
43331
44283
  return executable;
@@ -44077,7 +45029,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44077
45029
  const generated = await writeAbsoluteExpoProject(normalized, {
44078
45030
  projectRoot: process.cwd()
44079
45031
  });
44080
- const dependenciesReady = existsSync6(join17(generated.path, "node_modules", "expo", "package.json")) && existsSync6(join17(generated.path, "node_modules", "expo-dev-client", "package.json"));
45032
+ const dependenciesReady = existsSync6(join18(generated.path, "node_modules", "expo", "package.json")) && existsSync6(join18(generated.path, "node_modules", "expo-dev-client", "package.json"));
44081
45033
  if (!dependenciesReady) {
44082
45034
  const install = await confirmPrompt("Expo development dependencies are missing. Install the pinned SDK 57 development client now?");
44083
45035
  if (!install) {
@@ -44156,7 +45108,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44156
45108
  }
44157
45109
  }
44158
45110
  if (ready) {
44159
- const nativeDirectory = join17(normalized.nativeProjectDirectory, "android");
45111
+ const nativeDirectory = join18(normalized.nativeProjectDirectory, "android");
44160
45112
  let createNativeProject = false;
44161
45113
  if (!existsSync6(nativeDirectory)) {
44162
45114
  createNativeProject = await confirmPrompt("Create the managed Capacitor Android project now?");
@@ -44176,7 +45128,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44176
45128
  if (!remote) {
44177
45129
  console.log(cliTag("\x1B[33m", "iOS target skipped. Pair a Mac with `absolute mobile pair mac <name> <user@host>`."));
44178
45130
  } else {
44179
- const nativeDirectory = join17(normalized.nativeProjectDirectory, "ios");
45131
+ const nativeDirectory = join18(normalized.nativeProjectDirectory, "ios");
44180
45132
  if (!existsSync6(nativeDirectory)) {
44181
45133
  console.log(cliTag("\x1B[33m", "The iOS project is missing. Run `absolute mobile init` before remote development."));
44182
45134
  } else {
@@ -44197,7 +45149,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44197
45149
  }
44198
45150
  }
44199
45151
  if (ready) {
44200
- const nativeDirectory = join17(normalized.nativeProjectDirectory, "ios");
45152
+ const nativeDirectory = join18(normalized.nativeProjectDirectory, "ios");
44201
45153
  let createNativeProject = false;
44202
45154
  if (!existsSync6(nativeDirectory)) {
44203
45155
  createNativeProject = await confirmPrompt("Create the managed Capacitor iOS project now?");
@@ -44855,7 +45807,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44855
45807
  }
44856
45808
  return merged;
44857
45809
  };
44858
- const heapPreloadPath = join17(tmpdir2(), `absolute-heap-${process.pid}.ts`);
45810
+ const heapPreloadPath = join18(tmpdir2(), `absolute-heap-${process.pid}.ts`);
44859
45811
  let heapSnapshotEnabled = false;
44860
45812
  try {
44861
45813
  writeFileSync4(heapPreloadPath, DEV_CHILD_PRELOAD);
@@ -44988,7 +45940,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
44988
45940
  if (now - last < 100)
44989
45941
  return;
44990
45942
  recentlyHandled.set(filename, now);
44991
- scheduleServerRestart(join17(serverEntryDir, filename));
45943
+ scheduleServerRestart(join18(serverEntryDir, filename));
44992
45944
  };
44993
45945
  const recoveryScan = async () => {
44994
45946
  let entries;
@@ -45007,7 +45959,7 @@ var dev = async (serverEntry, configPath2, options = {}) => {
45007
45959
  continue;
45008
45960
  let fileStat;
45009
45961
  try {
45010
- fileStat = statSync(join17(serverEntryDir, entry.name));
45962
+ fileStat = statSync(join18(serverEntryDir, entry.name));
45011
45963
  } catch {
45012
45964
  continue;
45013
45965
  }
@@ -45541,7 +46493,7 @@ var info = () => {
45541
46493
  // src/cli/cache.ts
45542
46494
  init_constants();
45543
46495
  import { mkdir as mkdir7 } from "fs/promises";
45544
- import { join as join18 } from "path";
46496
+ import { join as join19 } from "path";
45545
46497
  var {Glob } = globalThis.Bun;
45546
46498
  var CACHE_DIR = ".absolutejs";
45547
46499
  var MAX_FILES_PER_BATCH = 200;
@@ -45593,7 +46545,7 @@ var hashFiles = async (paths) => {
45593
46545
  };
45594
46546
  var loadCache = async (tool) => {
45595
46547
  try {
45596
- const path = join18(CACHE_DIR, `${tool}.cache.json`);
46548
+ const path = join19(CACHE_DIR, `${tool}.cache.json`);
45597
46549
  const data = await Bun.file(path).json();
45598
46550
  const result = data;
45599
46551
  return result;
@@ -45640,7 +46592,7 @@ var runTool = async (adapter, args) => {
45640
46592
  };
45641
46593
  var saveCache = async (tool, data) => {
45642
46594
  await mkdir7(CACHE_DIR, { recursive: true });
45643
- const path = join18(CACHE_DIR, `${tool}.cache.json`);
46595
+ const path = join19(CACHE_DIR, `${tool}.cache.json`);
45644
46596
  await Bun.write(path, JSON.stringify(data, null, "\t"));
45645
46597
  };
45646
46598