@mentra/engine 3.4.0-dev.1208 → 3.4.0-dev.1209
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/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +5 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AppRegistry.d.ts +2 -0
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +2 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +18 -1
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MentraJSCrashController.d.ts +2 -0
- package/build/services/MentraJSCrashController.d.ts.map +1 -1
- package/build/services/MentraJSCrashController.js +4 -0
- package/build/services/MentraJSCrashController.js.map +1 -1
- package/build/services/MentraJSRouter.d.ts.map +1 -1
- package/build/services/MentraJSRouter.js +14 -3
- package/build/services/MentraJSRouter.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts +6 -0
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +20 -3
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/fleet/FleetReporter.d.ts.map +1 -1
- package/build/services/fleet/FleetReporter.js +43 -0
- package/build/services/fleet/FleetReporter.js.map +1 -1
- package/build/services/fleet/fleetCollector.d.ts +1 -1
- package/build/services/fleet/fleetCollector.d.ts.map +1 -1
- package/build/services/fleet/fleetCollector.js +146 -5
- package/build/services/fleet/fleetCollector.js.map +1 -1
- package/build/services/fleet/fleetEvents.d.ts +39 -3
- package/build/services/fleet/fleetEvents.d.ts.map +1 -1
- package/build/services/fleet/fleetEvents.js +66 -22
- package/build/services/fleet/fleetEvents.js.map +1 -1
- package/build/services/fleet/fleetQueue.d.ts +26 -0
- package/build/services/fleet/fleetQueue.d.ts.map +1 -1
- package/build/services/fleet/fleetQueue.js +94 -8
- package/build/services/fleet/fleetQueue.js.map +1 -1
- package/build/services/fleet/telemetryLimits.d.ts +16 -0
- package/build/services/fleet/telemetryLimits.d.ts.map +1 -0
- package/build/services/fleet/telemetryLimits.js +69 -0
- package/build/services/fleet/telemetryLimits.js.map +1 -0
- package/build/services/installMiniapp.d.ts +13 -0
- package/build/services/installMiniapp.d.ts.map +1 -1
- package/build/services/installMiniapp.js +64 -1
- package/build/services/installMiniapp.js.map +1 -1
- package/build/services/miniappReleaseInstall.js +1 -1
- package/build/services/miniappReleaseInstall.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +16 -1
- package/build/stores/apps.js.map +1 -1
- package/package.json +8 -8
- package/src/facades/glasses.ts +5 -3
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/services/AppRegistry.ts +2 -0
- package/src/services/LocalMiniappRuntime.ts +20 -1
- package/src/services/MentraJSCrashController.ts +5 -0
- package/src/services/MentraJSRouter.ts +15 -3
- package/src/services/MiniappLauncher.ts +18 -2
- package/src/services/fleet/FleetReporter.ts +40 -0
- package/src/services/fleet/fleetCollector.ts +160 -6
- package/src/services/fleet/fleetEvents.ts +104 -20
- package/src/services/fleet/fleetQueue.ts +100 -7
- package/src/services/fleet/telemetryLimits.ts +78 -0
- package/src/services/installMiniapp.ts +78 -2
- package/src/services/miniappReleaseInstall.ts +1 -1
- package/src/stores/apps.ts +19 -2
|
@@ -77,6 +77,8 @@ export declare class MentraJSCrashController {
|
|
|
77
77
|
constructor(opts?: CrashControllerOptions);
|
|
78
78
|
/** Tracking starts when MentraJSRouter spawns a fresh JSContext. */
|
|
79
79
|
onSpawn(packageName: string): void;
|
|
80
|
+
/** The crashes of `packageName` inside the current crash window, the latest included (Fleet's `crashesInWindow`). */
|
|
81
|
+
crashCount(packageName: string): number;
|
|
80
82
|
/** Called when MentraJSRouter explicitly tears down (user-initiated). */
|
|
81
83
|
onKill(packageName: string): void;
|
|
82
84
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentraJSCrashController.d.ts","sourceRoot":"","sources":["../../src/services/MentraJSCrashController.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,MAAM,UAAU,GAClB;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAClC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,GACtD;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,GACjE;IAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAA;AAEjE,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;yEACqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAeD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAQ;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;gBAEpD,IAAI,GAAE,sBAA2B;IAQ7C,oEAAoE;IACpE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAiBlC,yEAAyE;IACzE,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;OAIG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY;IA4C1D,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAIhD,2EAA2E;IAC3E,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIzC,sEAAsE;IACtE,gBAAgB,IAAI,MAAM,EAAE;IAQ5B,oEAAoE;IACpE,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;CAMjC"}
|
|
1
|
+
{"version":3,"file":"MentraJSCrashController.d.ts","sourceRoot":"","sources":["../../src/services/MentraJSCrashController.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,MAAM,UAAU,GAClB;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAClC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,GACtD;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,GACjE;IAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAA;AAEjE,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;yEACqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAeD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAQ;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;gBAEpD,IAAI,GAAE,sBAA2B;IAQ7C,oEAAoE;IACpE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAiBlC,qHAAqH;IACrH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIvC,yEAAyE;IACzE,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;OAIG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY;IA4C1D,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAIhD,2EAA2E;IAC3E,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIzC,sEAAsE;IACtE,gBAAgB,IAAI,MAAM,EAAE;IAQ5B,oEAAoE;IACpE,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;CAMjC"}
|
|
@@ -63,6 +63,10 @@ export class MentraJSCrashController {
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
/** The crashes of `packageName` inside the current crash window, the latest included (Fleet's `crashesInWindow`). */
|
|
67
|
+
crashCount(packageName) {
|
|
68
|
+
return this.packages.get(packageName)?.crashesInWindow.length ?? 0;
|
|
69
|
+
}
|
|
66
70
|
/** Called when MentraJSRouter explicitly tears down (user-initiated). */
|
|
67
71
|
onKill(packageName) {
|
|
68
72
|
this.packages.delete(packageName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentraJSCrashController.js","sourceRoot":"","sources":["../../src/services/MentraJSCrashController.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MentraJSCrashController — crash recovery state machine for the\n * per-miniapp JSContexts owned by MentraJSRouter.\n *\n * RUNNING\n * │ crash detected (uncaught exception, native bridge throw,\n * │ ping miss, soft-watchdog kill, etc.)\n * ▼\n * CRASHED ──── respawn allowed? ──── no ──► CRASHLOOP_DISABLED\n * │ (persistent banner)\n * ▼ yes\n * BACKOFF(2s → 8s → 30s)\n * │ timer fires\n * ▼\n * RUNNING (and stays here on a clean 60s window;\n * otherwise crash count increments)\n *\n * Retry policy: zero retries by default — the first crash escalates\n * straight to CRASHLOOP_DISABLED, matching how Android / iOS treat crashed\n * apps. Callers that prefer auto-respawn opt in by passing\n * `{maxRetries: 3, backoffMs: [2_000, 8_000, 30_000]}`; the BACKOFF state\n * machine and exponential delays are implemented and unit-tested for that\n * case.\n *\n * The controller is purely a policy engine — it doesn't actually call\n * mentraJsSpawn / mentraJsKill. Callers (MentraJSRouter) read the\n * Outcome returned from onCrash() and drive the host accordingly. This\n * makes the controller unit-testable with synthetic timer.\n */\n\nexport type CrashState =\n | {kind: \"RUNNING\"; sinceMs: number}\n | {kind: \"CRASHED\"; reason: string; crashedAtMs: number}\n | {kind: \"BACKOFF\"; until: number; attempt: number; reason: string}\n | {kind: \"CRASHLOOP_DISABLED\"; sinceMs: number; reason: string}\n\nexport interface CrashOutcome {\n /** Caller must call `respawn(packageName)` after this delay (ms from now). */\n scheduleRespawnAfterMs?: number\n /** Caller should surface \"X restarted\" toast (2nd-within-5m crash). */\n showRestartToast?: boolean\n /** Caller should surface persistent \"crashloop disabled\" banner. */\n surfaceCrashloopBanner?: boolean\n /** Caller may push the crash event to dev portal / Sentry as warn-level. */\n sentryLevel?: \"info\" | \"warning\" | \"error\"\n}\n\nexport interface CrashControllerOptions {\n /** Override the wall clock for tests. Defaults to Date.now. */\n now?: () => number\n /** Backoff sequence in ms. Defaults to [2_000, 8_000, 30_000]. */\n backoffMs?: number[]\n /** Window in ms — N crashes inside this window escalates. Defaults to 5min. */\n crashWindowMs?: number\n /** Max retries before CRASHLOOP_DISABLED. Defaults to 0 — first crash\n * escalates immediately, matching Android / iOS app conventions. */\n maxRetries?: number\n /** Clean-uptime threshold that resets the retry counter. Defaults to 60s. */\n cleanUptimeResetMs?: number\n}\n\ninterface PackageState {\n state: CrashState\n /** Crash timestamps inside the current window (sorted ascending). */\n crashesInWindow: number[]\n /** Total respawns since the package was first registered (for telemetry). */\n totalRespawns: number\n}\n\nconst DEFAULT_BACKOFFS = [2_000, 8_000, 30_000]\nconst DEFAULT_WINDOW_MS = 5 * 60_000\nconst DEFAULT_MAX_RETRIES = 0\nconst DEFAULT_CLEAN_UPTIME_RESET_MS = 60_000\n\nexport class MentraJSCrashController {\n private readonly now: () => number\n private readonly backoffMs: number[]\n private readonly crashWindowMs: number\n private readonly maxRetries: number\n private readonly cleanUptimeResetMs: number\n private readonly packages: Map<string, PackageState> = new Map()\n\n constructor(opts: CrashControllerOptions = {}) {\n this.now = opts.now ?? (() => Date.now())\n this.backoffMs = opts.backoffMs ?? DEFAULT_BACKOFFS\n this.crashWindowMs = opts.crashWindowMs ?? DEFAULT_WINDOW_MS\n this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n this.cleanUptimeResetMs = opts.cleanUptimeResetMs ?? DEFAULT_CLEAN_UPTIME_RESET_MS\n }\n\n /** Tracking starts when MentraJSRouter spawns a fresh JSContext. */\n onSpawn(packageName: string): void {\n const at = this.now()\n const prev = this.packages.get(packageName)\n if (prev) {\n // Carry forward the crash history; a respawn doesn't reset retry\n // budget unless the previous RUNNING state survived >= cleanUptimeResetMs.\n prev.state = {kind: \"RUNNING\", sinceMs: at}\n prev.totalRespawns += 1\n } else {\n this.packages.set(packageName, {\n state: {kind: \"RUNNING\", sinceMs: at},\n crashesInWindow: [],\n totalRespawns: 0,\n })\n }\n }\n\n /** Called when MentraJSRouter explicitly tears down (user-initiated). */\n onKill(packageName: string): void {\n this.packages.delete(packageName)\n }\n\n /**\n * Record a crash and return the policy outcome. Caller is responsible for\n * scheduling the respawn (timer ownership belongs at the orchestration\n * layer that holds the host context, not here).\n */\n onCrash(packageName: string, reason: string): CrashOutcome {\n const at = this.now()\n const entry = this.packages.get(packageName) ?? {\n state: {kind: \"RUNNING\", sinceMs: at},\n crashesInWindow: [],\n totalRespawns: 0,\n }\n\n // If the previous RUNNING window was long enough, reset the retry\n // counter — a crash after a stable session shouldn't burn budget.\n if (entry.state.kind === \"RUNNING\") {\n const uptime = at - entry.state.sinceMs\n if (uptime >= this.cleanUptimeResetMs) {\n entry.crashesInWindow = []\n }\n }\n\n // Drop crashes that fell out of the window before counting the new one.\n const cutoff = at - this.crashWindowMs\n entry.crashesInWindow = entry.crashesInWindow.filter((t) => t >= cutoff)\n entry.crashesInWindow.push(at)\n this.packages.set(packageName, entry)\n\n const crashCount = entry.crashesInWindow.length\n\n if (crashCount > this.maxRetries) {\n entry.state = {kind: \"CRASHLOOP_DISABLED\", sinceMs: at, reason}\n return {\n surfaceCrashloopBanner: true,\n sentryLevel: \"error\",\n }\n }\n\n const attempt = crashCount - 1\n const delayMs = this.backoffMs[Math.min(attempt, this.backoffMs.length - 1)]!\n entry.state = {kind: \"BACKOFF\", until: at + delayMs, attempt: crashCount, reason}\n\n return {\n scheduleRespawnAfterMs: delayMs,\n showRestartToast: crashCount >= 2,\n sentryLevel: crashCount === 1 ? \"info\" : \"warning\",\n }\n }\n\n /** Inspect current state — useful for tests and diagnostics. */\n stateFor(packageName: string): CrashState | null {\n return this.packages.get(packageName)?.state ?? null\n }\n\n /** Total respawns since registration — surfaced to Sentry as a counter. */\n respawnCount(packageName: string): number {\n return this.packages.get(packageName)?.totalRespawns ?? 0\n }\n\n /** Currently-disabled packages. Host UI uses this to show banners. */\n disabledPackages(): string[] {\n const out: string[] = []\n for (const [name, entry] of this.packages) {\n if (entry.state.kind === \"CRASHLOOP_DISABLED\") out.push(name)\n }\n return out\n }\n\n /** Manually re-enable a disabled package (user \"Try again\" tap). */\n reset(packageName: string): void {\n const entry = this.packages.get(packageName)\n if (!entry) return\n entry.crashesInWindow = []\n entry.state = {kind: \"RUNNING\", sinceMs: this.now()}\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MentraJSCrashController.js","sourceRoot":"","sources":["../../src/services/MentraJSCrashController.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MentraJSCrashController — crash recovery state machine for the\n * per-miniapp JSContexts owned by MentraJSRouter.\n *\n * RUNNING\n * │ crash detected (uncaught exception, native bridge throw,\n * │ ping miss, soft-watchdog kill, etc.)\n * ▼\n * CRASHED ──── respawn allowed? ──── no ──► CRASHLOOP_DISABLED\n * │ (persistent banner)\n * ▼ yes\n * BACKOFF(2s → 8s → 30s)\n * │ timer fires\n * ▼\n * RUNNING (and stays here on a clean 60s window;\n * otherwise crash count increments)\n *\n * Retry policy: zero retries by default — the first crash escalates\n * straight to CRASHLOOP_DISABLED, matching how Android / iOS treat crashed\n * apps. Callers that prefer auto-respawn opt in by passing\n * `{maxRetries: 3, backoffMs: [2_000, 8_000, 30_000]}`; the BACKOFF state\n * machine and exponential delays are implemented and unit-tested for that\n * case.\n *\n * The controller is purely a policy engine — it doesn't actually call\n * mentraJsSpawn / mentraJsKill. Callers (MentraJSRouter) read the\n * Outcome returned from onCrash() and drive the host accordingly. This\n * makes the controller unit-testable with synthetic timer.\n */\n\nexport type CrashState =\n | {kind: \"RUNNING\"; sinceMs: number}\n | {kind: \"CRASHED\"; reason: string; crashedAtMs: number}\n | {kind: \"BACKOFF\"; until: number; attempt: number; reason: string}\n | {kind: \"CRASHLOOP_DISABLED\"; sinceMs: number; reason: string}\n\nexport interface CrashOutcome {\n /** Caller must call `respawn(packageName)` after this delay (ms from now). */\n scheduleRespawnAfterMs?: number\n /** Caller should surface \"X restarted\" toast (2nd-within-5m crash). */\n showRestartToast?: boolean\n /** Caller should surface persistent \"crashloop disabled\" banner. */\n surfaceCrashloopBanner?: boolean\n /** Caller may push the crash event to dev portal / Sentry as warn-level. */\n sentryLevel?: \"info\" | \"warning\" | \"error\"\n}\n\nexport interface CrashControllerOptions {\n /** Override the wall clock for tests. Defaults to Date.now. */\n now?: () => number\n /** Backoff sequence in ms. Defaults to [2_000, 8_000, 30_000]. */\n backoffMs?: number[]\n /** Window in ms — N crashes inside this window escalates. Defaults to 5min. */\n crashWindowMs?: number\n /** Max retries before CRASHLOOP_DISABLED. Defaults to 0 — first crash\n * escalates immediately, matching Android / iOS app conventions. */\n maxRetries?: number\n /** Clean-uptime threshold that resets the retry counter. Defaults to 60s. */\n cleanUptimeResetMs?: number\n}\n\ninterface PackageState {\n state: CrashState\n /** Crash timestamps inside the current window (sorted ascending). */\n crashesInWindow: number[]\n /** Total respawns since the package was first registered (for telemetry). */\n totalRespawns: number\n}\n\nconst DEFAULT_BACKOFFS = [2_000, 8_000, 30_000]\nconst DEFAULT_WINDOW_MS = 5 * 60_000\nconst DEFAULT_MAX_RETRIES = 0\nconst DEFAULT_CLEAN_UPTIME_RESET_MS = 60_000\n\nexport class MentraJSCrashController {\n private readonly now: () => number\n private readonly backoffMs: number[]\n private readonly crashWindowMs: number\n private readonly maxRetries: number\n private readonly cleanUptimeResetMs: number\n private readonly packages: Map<string, PackageState> = new Map()\n\n constructor(opts: CrashControllerOptions = {}) {\n this.now = opts.now ?? (() => Date.now())\n this.backoffMs = opts.backoffMs ?? DEFAULT_BACKOFFS\n this.crashWindowMs = opts.crashWindowMs ?? DEFAULT_WINDOW_MS\n this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n this.cleanUptimeResetMs = opts.cleanUptimeResetMs ?? DEFAULT_CLEAN_UPTIME_RESET_MS\n }\n\n /** Tracking starts when MentraJSRouter spawns a fresh JSContext. */\n onSpawn(packageName: string): void {\n const at = this.now()\n const prev = this.packages.get(packageName)\n if (prev) {\n // Carry forward the crash history; a respawn doesn't reset retry\n // budget unless the previous RUNNING state survived >= cleanUptimeResetMs.\n prev.state = {kind: \"RUNNING\", sinceMs: at}\n prev.totalRespawns += 1\n } else {\n this.packages.set(packageName, {\n state: {kind: \"RUNNING\", sinceMs: at},\n crashesInWindow: [],\n totalRespawns: 0,\n })\n }\n }\n\n /** The crashes of `packageName` inside the current crash window, the latest included (Fleet's `crashesInWindow`). */\n crashCount(packageName: string): number {\n return this.packages.get(packageName)?.crashesInWindow.length ?? 0\n }\n\n /** Called when MentraJSRouter explicitly tears down (user-initiated). */\n onKill(packageName: string): void {\n this.packages.delete(packageName)\n }\n\n /**\n * Record a crash and return the policy outcome. Caller is responsible for\n * scheduling the respawn (timer ownership belongs at the orchestration\n * layer that holds the host context, not here).\n */\n onCrash(packageName: string, reason: string): CrashOutcome {\n const at = this.now()\n const entry = this.packages.get(packageName) ?? {\n state: {kind: \"RUNNING\", sinceMs: at},\n crashesInWindow: [],\n totalRespawns: 0,\n }\n\n // If the previous RUNNING window was long enough, reset the retry\n // counter — a crash after a stable session shouldn't burn budget.\n if (entry.state.kind === \"RUNNING\") {\n const uptime = at - entry.state.sinceMs\n if (uptime >= this.cleanUptimeResetMs) {\n entry.crashesInWindow = []\n }\n }\n\n // Drop crashes that fell out of the window before counting the new one.\n const cutoff = at - this.crashWindowMs\n entry.crashesInWindow = entry.crashesInWindow.filter((t) => t >= cutoff)\n entry.crashesInWindow.push(at)\n this.packages.set(packageName, entry)\n\n const crashCount = entry.crashesInWindow.length\n\n if (crashCount > this.maxRetries) {\n entry.state = {kind: \"CRASHLOOP_DISABLED\", sinceMs: at, reason}\n return {\n surfaceCrashloopBanner: true,\n sentryLevel: \"error\",\n }\n }\n\n const attempt = crashCount - 1\n const delayMs = this.backoffMs[Math.min(attempt, this.backoffMs.length - 1)]!\n entry.state = {kind: \"BACKOFF\", until: at + delayMs, attempt: crashCount, reason}\n\n return {\n scheduleRespawnAfterMs: delayMs,\n showRestartToast: crashCount >= 2,\n sentryLevel: crashCount === 1 ? \"info\" : \"warning\",\n }\n }\n\n /** Inspect current state — useful for tests and diagnostics. */\n stateFor(packageName: string): CrashState | null {\n return this.packages.get(packageName)?.state ?? null\n }\n\n /** Total respawns since registration — surfaced to Sentry as a counter. */\n respawnCount(packageName: string): number {\n return this.packages.get(packageName)?.totalRespawns ?? 0\n }\n\n /** Currently-disabled packages. Host UI uses this to show banners. */\n disabledPackages(): string[] {\n const out: string[] = []\n for (const [name, entry] of this.packages) {\n if (entry.state.kind === \"CRASHLOOP_DISABLED\") out.push(name)\n }\n return out\n }\n\n /** Manually re-enable a disabled package (user \"Try again\" tap). */\n reset(packageName: string): void {\n const entry = this.packages.get(packageName)\n if (!entry) return\n entry.crashesInWindow = []\n entry.state = {kind: \"RUNNING\", sinceMs: this.now()}\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentraJSRouter.d.ts","sourceRoot":"","sources":["../../src/services/MentraJSRouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;
|
|
1
|
+
{"version":3,"file":"MentraJSRouter.d.ts","sourceRoot":"","sources":["../../src/services/MentraJSRouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* visible without bringing up a separate sink.
|
|
34
34
|
*/
|
|
35
35
|
import devServerBridge from "./DevServerBridge";
|
|
36
|
+
import { fleetEvents } from "./fleet/fleetEvents";
|
|
36
37
|
import { MentraJSLogRingBuffer, MentraJSLogThrottle, redactSecrets } from "./MentraJSLogPipeline";
|
|
37
38
|
import { islandNotifications } from "./NotificationsEmitter";
|
|
38
39
|
import { recordMiniappBuild } from "./miniappBuildKind";
|
|
@@ -130,7 +131,7 @@ export class MentraJSRouter {
|
|
|
130
131
|
// respawn machinery as a crash so it comes back automatically, with
|
|
131
132
|
// the crash controller's backoff + crash-loop protection.
|
|
132
133
|
this.runtime.onLivenessTimeout = (packageName) => {
|
|
133
|
-
this.handleCrash(packageName, "liveness: missed pings");
|
|
134
|
+
this.handleCrash(packageName, "liveness: missed pings", "unresponsive");
|
|
134
135
|
};
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
@@ -237,7 +238,7 @@ export class MentraJSRouter {
|
|
|
237
238
|
* handler. The controller returns an outcome that tells us whether
|
|
238
239
|
* to respawn (with delay) or leave the context dead.
|
|
239
240
|
*/
|
|
240
|
-
handleCrash(packageName, reason) {
|
|
241
|
+
handleCrash(packageName, reason, category = "unknown") {
|
|
241
242
|
const cached = this.spawnCache.get(packageName);
|
|
242
243
|
// A transient action cannot resume after a crash. Remove the worker (which
|
|
243
244
|
// fails its pending invocations); only a new invocation may spawn another.
|
|
@@ -271,6 +272,16 @@ export class MentraJSRouter {
|
|
|
271
272
|
return;
|
|
272
273
|
}
|
|
273
274
|
const outcome = controller.onCrash(packageName, reason);
|
|
275
|
+
// Fleet hears the crash by its category, never its reason text (Phase 12).
|
|
276
|
+
const versionName = this.runtime.installedVersionOf?.(packageName);
|
|
277
|
+
fleetEvents.crash({
|
|
278
|
+
packageName,
|
|
279
|
+
...(versionName ? { versionName } : {}),
|
|
280
|
+
surface: "context",
|
|
281
|
+
category,
|
|
282
|
+
outcome: outcome.surfaceCrashloopBanner || outcome.scheduleRespawnAfterMs == null ? "crash_loop_stopped" : "respawn_scheduled",
|
|
283
|
+
crashesInWindow: Math.max(1, controller.crashCount(packageName)),
|
|
284
|
+
});
|
|
274
285
|
if (outcome.surfaceCrashloopBanner) {
|
|
275
286
|
this.uiRouter?.backgroundStopped(packageName);
|
|
276
287
|
this.onCrashloop?.(packageName, reason);
|
|
@@ -511,7 +522,7 @@ export class MentraJSRouter {
|
|
|
511
522
|
// log path — those don't end the JSContext.
|
|
512
523
|
const isCrash = method === "exception" || method === "unhandledRejection" || method === "uncaught";
|
|
513
524
|
if (isCrash) {
|
|
514
|
-
this.handleCrash(packageName, this.summarizeReason(payload, method));
|
|
525
|
+
this.handleCrash(packageName, this.summarizeReason(payload, method), "uncaught_exception");
|
|
515
526
|
}
|
|
516
527
|
}
|
|
517
528
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentraJSRouter.js","sourceRoot":"","sources":["../../src/services/MentraJSRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview MentraJSRouter — RN-side host that bridges the per-miniapp\n * JS contexts (iOS-JSC and Android-QuickJS via Zipline) into the existing\n * {@link LocalMiniappRuntime} handler set.\n *\n * Rather than re-implement the 33 dispatch arms inline, the router\n * consumes Crust's `mentrajs_message` Expo event and forwards its\n * `__bridge.send(raw)` payloads to {@link LocalMiniappRuntime.handleRawMessage}.\n * The SDK envelope shape (`{type: \"DISPLAY\", ...}` etc.) is unchanged from\n * the WebView path, so every existing handler runs verbatim. What changes\n * is the *transport*:\n *\n * - **Inbound** (background JS → host): `__dispatch(\"__bridge\", \"send\",\n * [rawJsonString])` → `mentrajs_message` event → router →\n * `LocalMiniappRuntime.handleRawMessage(packageName, raw)`.\n * - **Outbound** (host → background JS): every package registered with\n * the router gets a `sendMessage(raw)` fn that calls\n * `Crust.mentraJsDispatchToJs(packageName, {kind: \"bridge\", raw})`.\n * The polyfill's `__deliver` recognises `kind === \"bridge\"` and calls\n * `__mentraDeliverBridgeRaw(raw)`, which routes into the SDK's\n * {@link DispatchTransport.onMessage} handler.\n *\n * Host-side handler bodies (display fan-out, mic state, transcription,\n * navigation, etc.) live untouched in `LocalMiniappRuntime.ts`.\n *\n * PhoneStreamCoordinator delivers stream status through LocalMiniappRuntime\n * to the miniapp's registered `sendMessage` callback, using the same local\n * bridge as other responses and events.\n *\n * The router also bridges native error / log / unhandled-rejection\n * events (`iface: \"__log\"`, `iface: \"__error\"`) into the standard\n * `console.*` + Sentry breadcrumb pipeline so miniapp telemetry is\n * visible without bringing up a separate sink.\n */\n\nimport type {EventSubscription} from \"expo-modules-core\"\n\nimport devServerBridge from \"./DevServerBridge\"\nimport type localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport type {InstalledMiniappManifest} from \"./LocalMiniappRuntime\"\nimport type {MentraJSCrashController} from \"./MentraJSCrashController\"\nimport {MentraJSLogRingBuffer, MentraJSLogThrottle, redactSecrets} from \"./MentraJSLogPipeline\"\nimport {islandNotifications} from \"./NotificationsEmitter\"\nimport type {MentraUIRouter} from \"./MentraUIRouter\"\nimport {recordMiniappBuild} from \"./miniappBuildKind\"\nimport {miniappContextRegistry, miniappRunningRegistry} from \"./MiniappRunningRegistry\"\n\n/** The runtime's runtime instance type — the singleton exported from\n * LocalMiniappRuntime.ts (the file's `export default` is the instance,\n * not the class).\n */\ntype LocalMiniappRuntime = typeof localMiniappRuntime\n\n/**\n * Minimal subset of the Crust native module the router uses. Keeping the\n * binding loose lets us mock the module in unit tests without bringing\n * the whole Expo module surface along.\n */\nexport interface MentraJSCrustBinding {\n mentraJsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void> | void\n mentraJsSetManifest(packageName: string, permissions: string[]): Promise<void> | void\n mentraJsLoadPolyfillBundle?: () => string\n mentraJsSpawn?: (packageName: string, polyfill: string, miniappJs: string) => Promise<boolean> | boolean\n mentraJsKill?: (packageName: string) => Promise<void> | void\n mentraJsAlivePackages?: () => string[]\n addListener: (event: string, handler: (payload: Record<string, unknown>) => void) => EventSubscription\n}\n\nexport interface OutboundMessagePayload {\n packageName: string\n iface: string\n method: string\n argsJson?: string\n args?: unknown[]\n reqId?: string\n // Implementation-internal: any extra fields the dispatcher attached on\n // .forwardToRn (e.g. payload metadata) are passed through verbatim.\n [extra: string]: unknown\n}\n\nexport type RouterLogger = {\n log: (message: string, payload?: unknown) => void\n warn: (message: string, payload?: unknown) => void\n error: (message: string, payload?: unknown) => void\n}\n\nconst defaultLogger: RouterLogger = {\n log: (m, p) => console.log(`[MentraJSRouter] ${m}`, p ?? \"\"),\n\n warn: (m, p) => console.warn(`[MentraJSRouter] ${m}`, p ?? \"\"),\n\n error: (m, p) => console.error(`[MentraJSRouter] ${m}`, p ?? \"\"),\n}\n\n/**\n * How long a spawned background may take to connect and settle its init before\n * the host opens its UI anyway. A hung or broken background must not brick it.\n */\nexport const BACKGROUND_READY_TIMEOUT_MS = 10_000\n\n/** Timer used for the UI-hold deadline and crash respawns. The engine injects BgTimer. */\nexport interface RouterTimer {\n setTimeout(callback: () => void, ms: number): number\n clearTimeout(id: number): void\n}\n\ninterface SpawnCache {\n miniappJs: string\n permissions: string[]\n installedManifest?: InstalledMiniappManifest\n /** Host-derived SYSTEM trust from install provenance; never from the manifest. */\n hostTrustedSystem: boolean\n /** Whether this context is normal user-visible activity (false for a transient action wake). */\n projectRunning: boolean\n /** A local or development build: its data is kept apart and its tokens are marked. */\n developmentBuild: boolean\n}\n\nexport class MentraJSRouter {\n private subscription: EventSubscription | null = null\n private readonly registered: Set<string> = new Set()\n private readonly replacementConnects = new Set<string>()\n private nextDeliveryId = 0\n /** The host's id for each package's current context, sent to it in `init`. */\n private readonly sessions = new Map<string, string>()\n private sessionSeq = 0\n /** Spawned backgrounds whose UI is still held, with their ready deadline. */\n private readonly readyTimers = new Map<string, number>()\n /** Cached spawn arguments so the crash controller can respawn after a backoff. */\n private readonly spawnCache: Map<string, SpawnCache> = new Map()\n /** Active respawn timers (so unregister() can cancel a pending respawn). */\n private readonly respawnTimers: Map<string, number> = new Map()\n\n /**\n * Optional crash controller. When wired, every `__error` frame from a\n * JSContext drives the state machine; an Outcome with\n * scheduleRespawnAfterMs queues a `timer` callback that re-spawns the\n * miniapp's last-known JS source after the backoff delay.\n */\n crashController: MentraJSCrashController | null = null\n\n /**\n * Timer for the UI-hold deadline and crash respawns. MiniappEngine injects\n * BgTimer because plain JS timers pause while the Android app is\n * backgrounded; tests keep the global timers.\n */\n timer: RouterTimer = {\n setTimeout: (callback, ms) => setTimeout(callback, ms) as unknown as number,\n clearTimeout: (id) => clearTimeout(id as unknown as ReturnType<typeof setTimeout>),\n }\n\n /** Hook called when a package transitions to CRASHLOOP_DISABLED. */\n onCrashloop: ((packageName: string, reason: string) => void) | null = null\n /** Hook called on the 2nd-within-window crash (toast UX). */\n onRestartToast: ((packageName: string, reason: string) => void) | null = null\n\n /**\n * Logging plumbing. Every `__log` frame passes through `redactSecrets`\n * (token/password/etc. scrubbed), then the throttle (100/min sustained\n * per pkg, 500 burst), then lands in the ring buffer + the host\n * logger. The ring buffer holds the last 200 lines/pkg so a crash\n * report can attach \"last words\" context.\n *\n * Replace with new instances in tests; production code uses the\n * defaults.\n */\n logThrottle: MentraJSLogThrottle = new MentraJSLogThrottle()\n logRing: MentraJSLogRingBuffer = new MentraJSLogRingBuffer()\n\n /**\n * Optional UI router — when wired, `__bridge.send` envelopes whose\n * inner payload type is `UI_SEND` get routed to the bound WebView\n * instead of through LocalMiniappRuntime. Two-layer miniapps set\n * this; legacy single-bundle WebView miniapps don't and the bridge\n * frames pass through unchanged.\n */\n uiRouter: MentraUIRouter | null = null\n\n constructor(\n private readonly runtime: LocalMiniappRuntime,\n private readonly crust: MentraJSCrustBinding,\n private readonly logger: RouterLogger = defaultLogger,\n ) {}\n\n /**\n * Subscribe to Crust's `mentrajs_message` event. Idempotent — calling\n * start() twice attaches a single listener.\n */\n start(): void {\n if (this.subscription) return\n this.subscription = this.crust.addListener(\"mentrajs_message\", (raw) => {\n try {\n this.handleOutbound(raw as unknown as OutboundMessagePayload)\n } catch (e) {\n this.logger.error(`outbound handler threw`, {error: String(e), raw})\n }\n })\n // A background script unregistered for missing liveness pings is dead\n // weight: its subscriptions are gone (so the mic releases) while the\n // webview keeps rendering stale state. Route it through the same\n // respawn machinery as a crash so it comes back automatically, with\n // the crash controller's backoff + crash-loop protection.\n this.runtime.onLivenessTimeout = (packageName) => {\n this.handleCrash(packageName, \"liveness: missed pings\")\n }\n }\n\n /**\n * Detach the Crust subscription. The router can be restarted later\n * with start(); registrations survive (the runtime's app map is the\n * source of truth).\n */\n stop(): void {\n this.subscription?.remove()\n this.subscription = null\n this.runtime.onLivenessTimeout = null\n }\n\n /**\n * Register a JSContext with the runtime so its handlers can fire\n * `sendMessage(raw)` against the right context. The router builds a\n * `sendMessage` that calls `Crust.mentraJsDispatchToJs(packageName,\n * {kind: \"bridge\", raw})`; the polyfill's `__deliver` then hands the\n * envelope to `DispatchTransport.onMessage` inside the miniapp.\n *\n * Mirrors `MiniappHost.registerRuntime` on the WebView side. Callers\n * (e.g. {@link spawnAndRegister} below, or the host's miniapp launch\n * pipeline) should call this once per spawn.\n */\n registerApp(\n packageName: string,\n installedManifest?: InstalledMiniappManifest,\n hostTrustedSystem = false,\n projectRunning = true,\n developmentBuild = false,\n ): void {\n // Before the context is live: its data partition is pinned, and its tokens are marked, by the build it runs.\n recordMiniappBuild(packageName, {localBuild: developmentBuild})\n this.runtime.registerApp(\n packageName,\n (raw: string) => {\n this.dispatchBridgeRaw(packageName, raw)\n },\n installedManifest,\n hostTrustedSystem,\n projectRunning,\n )\n this.registered.add(packageName)\n miniappContextRegistry.add(packageName)\n this.replacementConnects.add(packageName)\n this.uiRouter?.backgroundStarting(packageName)\n this.armReadyDeadline(packageName)\n // A context normally represents user activity and projects into the\n // running tray. A transient action wake deliberately does not: context\n // liveness and user-visible activity are separate.\n if (projectRunning) miniappRunningRegistry.add(packageName)\n }\n\n /**\n * Convenience: spawn a JSContext via Crust, register the app, and\n * set its declared permissions. Returns true on successful spawn.\n *\n * The host's miniapp launch path\n * (`mobile/src/services/miniapps/launchLocalMiniapp.ts`) calls this\n * once per JSContext mount.\n */\n async spawnAndRegister(\n packageName: string,\n miniappJs: string,\n options?: {\n permissions?: string[]\n installedManifest?: InstalledMiniappManifest\n hostTrustedSystem?: boolean\n /** False for an invocation-scoped transient action wake. */\n projectRunning?: boolean\n /** A local or development build (a local-network install, a development server or snapshot). */\n developmentBuild?: boolean\n },\n ): Promise<boolean> {\n if (!this.crust.mentraJsSpawn) {\n this.logger.warn(\"mentraJsSpawn not available — host binding missing native function\")\n return false\n }\n const polyfill = this.crust.mentraJsLoadPolyfillBundle?.() ?? \"\"\n if (!polyfill) {\n this.logger.warn(`no polyfill bundle loaded for ${packageName}; spawn will likely fail`)\n }\n const ok = await this.crust.mentraJsSpawn(packageName, polyfill, miniappJs)\n if (!ok) {\n this.logger.error(`spawn failed for ${packageName}`)\n return false\n }\n // Two distinct permission gates:\n // 1. Native dispatcher (JSContext → native bridge): `mentraJsSetManifest`\n // checks per-call iface, e.g. __dispatch(\"mic\", \"start\", ...) requires\n // MICROPHONE.\n // 2. LocalMiniappRuntime (SUBSCRIBE envelope): `registerApp(...,\n // installedManifest)` carries the full manifest so SUBSCRIBE's gate\n // can match stream → permission against declared types.\n if (options?.permissions && options.permissions.length > 0) {\n await this.crust.mentraJsSetManifest(packageName, options.permissions)\n }\n const hostTrustedSystem = options?.hostTrustedSystem ?? false\n const projectRunning = options?.projectRunning ?? true\n const developmentBuild = options?.developmentBuild ?? false\n this.registerApp(packageName, options?.installedManifest, hostTrustedSystem, projectRunning, developmentBuild)\n // Cache spawn arguments so the crash controller can respawn the\n // same code after a backoff. permissions + manifest are cached too\n // because the native side resets setManifest on every spawn and\n // LocalMiniappRuntime's installedManifest entry is keyed by\n // connect-time registration.\n this.spawnCache.set(packageName, {\n miniappJs,\n permissions: options?.permissions ?? [],\n installedManifest: options?.installedManifest,\n hostTrustedSystem,\n projectRunning,\n developmentBuild,\n })\n this.crashController?.onSpawn(packageName)\n\n // Fire the `init` envelope so the polyfill's __deliver handler\n // resolves `__mentraInitCallback(sessionId)`. The SDK's\n // `registerMiniapp(handler)` wires that callback to construct\n // MiniappSession and call the user's handler. Without this\n // dispatch the user's code never runs — `registerMiniapp` just\n // assigns to a global and waits.\n const sessionId = this.nextSessionId(packageName)\n this.logger.log(`initializing background for ${packageName}`, {sessionId})\n void this.crust.mentraJsDispatchToJs(packageName, {kind: \"init\", sessionId})\n\n return true\n }\n\n /**\n * Crash-controller integration. Called from the `__error` frame\n * handler. The controller returns an outcome that tells us whether\n * to respawn (with delay) or leave the context dead.\n */\n private handleCrash(packageName: string, reason: string): void {\n const cached = this.spawnCache.get(packageName)\n // A transient action cannot resume after a crash. Remove the worker (which\n // fails its pending invocations); only a new invocation may spawn another.\n if (cached?.projectRunning === false) {\n this.logger.warn(`transient background context crashed for ${packageName}`, {reason})\n void this.unregister(packageName)\n return\n }\n // The native context is dead the instant a crash is observed — invalidate\n // its CONNECT handshake NOW so waitForConnect() blocks through the respawn\n // backoff window instead of resolving immediately against the dead context\n // (an action invoked mid-backoff would otherwise be delivered to nothing).\n this.logger.warn(`background restart for ${packageName}`, {\n reason,\n uiBound: this.uiRouter?.isBound(packageName) ?? false,\n })\n this.replacementConnects.delete(packageName)\n this.sessions.delete(packageName)\n this.clearReadyTimer(packageName)\n this.uiRouter?.backgroundRestarting(packageName)\n this.runtime.resetHandshake(packageName)\n const controller = this.crashController\n if (!controller) {\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n if (!cached) {\n this.logger.warn(`crash for ${packageName} but no cached spawn args — cannot respawn`)\n controller.onCrash(packageName, reason)\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n const outcome = controller.onCrash(packageName, reason)\n if (outcome.surfaceCrashloopBanner) {\n this.uiRouter?.backgroundStopped(packageName)\n this.onCrashloop?.(packageName, reason)\n islandNotifications.emit({kind: \"miniapp_crashloop\", packageName, reason, timestamp: Date.now()})\n return\n }\n if (outcome.showRestartToast) {\n this.onRestartToast?.(packageName, reason)\n }\n if (outcome.scheduleRespawnAfterMs == null) {\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n // Cancel any prior pending respawn before scheduling a new one.\n const existing = this.respawnTimers.get(packageName)\n if (existing !== undefined) this.timer.clearTimeout(existing)\n const timer = this.timer.setTimeout(() => {\n this.respawnTimers.delete(packageName)\n void (async () => {\n try {\n await this.crust.mentraJsKill?.(packageName)\n } catch {\n /* native may have already torn down */\n }\n const ok = await this.crust.mentraJsSpawn?.(\n packageName,\n this.crust.mentraJsLoadPolyfillBundle?.() ?? \"\",\n cached.miniappJs,\n )\n if (!ok) {\n this.logger.error(`crash respawn failed for ${packageName}`)\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n if (cached.permissions.length > 0) {\n await this.crust.mentraJsSetManifest(packageName, cached.permissions)\n }\n this.registerApp(\n packageName,\n cached.installedManifest,\n cached.hostTrustedSystem,\n cached.projectRunning,\n cached.developmentBuild,\n )\n controller.onSpawn(packageName)\n // Re-fire the init envelope so the respawned context's\n // `registerMiniapp` handler runs again.\n const sessionId = this.nextSessionId(packageName)\n void this.crust.mentraJsDispatchToJs(packageName, {kind: \"init\", sessionId})\n this.logger.log(`respawned ${packageName} after crash`, {sessionId})\n })()\n }, outcome.scheduleRespawnAfterMs)\n this.respawnTimers.set(packageName, timer)\n }\n\n /** Promote an already-spawned transient context into normal user activity. */\n projectRunning(packageName: string): void {\n if (!this.registered.has(packageName) || miniappRunningRegistry.has(packageName)) return\n miniappRunningRegistry.add(packageName)\n this.runtime.onUserActivated(packageName)\n const cached = this.spawnCache.get(packageName)\n if (cached) cached.projectRunning = true\n }\n\n /** True iff the context is projected as user-visible running activity. */\n isProjectedRunning(packageName: string): boolean {\n return miniappRunningRegistry.has(packageName)\n }\n\n private summarizeReason(payload: unknown, method: string): string {\n if (payload && typeof payload === \"object\" && \"message\" in (payload as Record<string, unknown>)) {\n return `${method}: ${(payload as {message: string}).message}`\n }\n return method\n }\n\n /**\n * Tear down a JSContext and unregister from the runtime. Synchronous\n * teardown — the spec drops the 50ms WILL_DISCONNECT grace window\n * because there's no transport handshake to flush in the JSC world.\n */\n async unregister(packageName: string): Promise<void> {\n if (!this.registered.has(packageName)) return\n // Cancel any pending crash-respawn before tearing down — we don't\n // want a stale timer to re-spawn a miniapp the user just disabled.\n const pending = this.respawnTimers.get(packageName)\n if (pending !== undefined) {\n this.timer.clearTimeout(pending)\n this.respawnTimers.delete(packageName)\n }\n this.replacementConnects.delete(packageName)\n this.sessions.delete(packageName)\n this.clearReadyTimer(packageName)\n this.uiRouter?.backgroundStopped(packageName)\n this.spawnCache.delete(packageName)\n this.crashController?.onKill(packageName)\n this.runtime.unregisterApp(packageName)\n this.registered.delete(packageName)\n // JSContext torn down → miniapp is no longer \"running\".\n miniappRunningRegistry.remove(packageName)\n miniappContextRegistry.remove(packageName)\n if (this.crust.mentraJsKill) {\n try {\n await this.crust.mentraJsKill(packageName)\n } catch (e) {\n this.logger.warn(`mentraJsKill threw for ${packageName}: ${String(e)}`)\n }\n }\n }\n\n /** List packages the router has registered. */\n registeredPackages(): string[] {\n return Array.from(this.registered)\n }\n\n /**\n * Validate that a reused background JSContext is still responsive when its\n * WebView comes foreground. The runtime owns the ping/timeout mechanics; the\n * router gates this to registered packages and keeps LocalMiniappView from\n * reaching into runtime internals.\n */\n probeForegroundLiveness(packageName: string, reason = \"foreground-open\", timeoutMs?: number): void {\n if (!this.registered.has(packageName)) return\n this.runtime.probeForegroundLiveness(packageName, reason, timeoutMs)\n }\n\n // ----------------------------------------------------------------\n // Outbound message handling\n // ----------------------------------------------------------------\n\n private handleOutbound(msg: OutboundMessagePayload): void {\n const {packageName, iface, method} = msg\n if (!packageName || !iface || !method) {\n this.logger.warn(`bad mentrajs_message — missing fields`, msg)\n return\n }\n\n // 1) Bridge frames: the SDK's DispatchTransport.send(raw) lands here.\n // Forward raw envelope into LocalMiniappRuntime.handleRawMessage\n // so every existing handler arm runs verbatim.\n if (iface === \"__bridge\" && method === \"send\") {\n const raw = this.coerceArgsToBridgeRaw(msg)\n if (raw == null) {\n this.logger.warn(`__bridge.send had no raw payload`, msg)\n return\n }\n // If the payload is a UI_SEND envelope, route it to the bound\n // WebView instead of LocalMiniappRuntime.\n // session.ui.send → DispatchTransport.send → here.\n const innerPayload = this.peekBridgePayloadType(raw)\n if (this.uiRouter && innerPayload?.type === \"UI_SEND\") {\n this.uiRouter.routeFromBackground(packageName, innerPayload)\n return\n }\n // A context killed for a restart can still have CONNECT or READY queued.\n // SDKs that echo the host's session id let us drop them before the\n // runtime handshakes with them; older ones send none and are trusted.\n if (typeof innerPayload?.sessionId === \"string\" && innerPayload.sessionId !== this.sessions.get(packageName)) {\n this.logger.warn(`dropping ${String(innerPayload.type)} from a previous ${packageName} context`)\n return\n }\n this.runtime.handleRawMessage(packageName, raw)\n // CONNECT_ACK is sent synchronously by handleConnect, so UI_OPEN lands\n // after the fresh SDK transport exists.\n if (innerPayload?.type === \"miniapp_connect\") {\n this.logger.log(`CONNECT received for ${packageName}`, {uiBound: this.uiRouter?.isBound(packageName) ?? false})\n if (this.replacementConnects.delete(packageName)) {\n if (!this.readyTimers.has(packageName)) {\n // The deadline passed before CONNECT: the UI is showing and its\n // frames were held for this session. Release them now.\n this.uiRouter?.backgroundReady(packageName)\n } else if (innerPayload.initReady !== true) {\n // SDKs that announce READY keep the UI held until their init\n // settles; older ones are ready once connected.\n this.markBackgroundReady(packageName, \"connect\")\n }\n }\n } else if (innerPayload?.type === \"miniapp_ready\") {\n // Only the current session's READY counts: one queued by a killed\n // context can arrive after its replacement spawned but before it\n // connected.\n if (!this.replacementConnects.has(packageName)) this.markBackgroundReady(packageName, \"ready\")\n }\n return\n }\n\n // 2) Log frames — console.* rewired through host. We tag with the\n // packageName so Sentry breadcrumbs can be filtered downstream.\n // Pipeline:\n // raw args → redactSecrets → throttle decision → ring buffer\n // → host logger (which surfaces to dev console / Sentry)\n if (iface === \"__log\") {\n const rawArgs = this.tryParseArgs(msg.argsJson)\n const safeArgs = redactSecrets(rawArgs)\n const decision = this.logThrottle.consume(packageName)\n const tag = `[${packageName}]`\n const fn =\n method === \"warn\"\n ? this.logger.warn\n : method === \"error\" || method === \"fatal\"\n ? this.logger.error\n : this.logger.log\n // Emit a synthetic \"[throttled N]\" line in place of the next\n // would-be-allowed log to summarise drops.\n if (!decision.allowed) {\n if (decision.throttledLine) {\n fn(`${tag} ${decision.throttledLine}`)\n this.logRing.push(packageName, `${decision.throttledLine}`)\n }\n return\n }\n const formatted = `${tag} console.${method}`\n fn(formatted, safeArgs)\n // Stringify into the ring buffer for crash-report attachment.\n let line: string\n try {\n line = `${method}: ${JSON.stringify(safeArgs)}`\n } catch {\n line = `${method}: <unserializable>`\n }\n this.logRing.push(packageName, line)\n // Forward to the `mentra-miniapp dev` sidecar (if one is connected\n // for this package). Source=\"background\" so the CLI tags lines as\n // [MentraJS]. The bridge silently drops when no dev server is up,\n // which makes this safe to call unconditionally in prod.\n const argsForBridge = Array.isArray(safeArgs) ? (safeArgs as unknown[]) : [safeArgs]\n devServerBridge.forwardLog(packageName, method, argsForBridge, Date.now(), \"background\")\n return\n }\n\n // 3) Error / unhandled rejection frames — surface as warnings AND\n // drive the crash state machine when one is attached. The\n // controller's outcome may schedule a respawn after a backoff;\n // if it doesn't (CRASHLOOP_DISABLED), we leave the JSContext\n // dead and let the host surface a \"tap to retry\" banner.\n if (iface === \"__error\") {\n const payload = this.tryParseArgs(msg.argsJson)\n // ready_nack is a liveness probe, not a crash. Logging it at error\n // painted WHIP-start queue delay as a Mentra-Call exception.\n if (method === \"ready_nack\") {\n this.logger.warn(`[${packageName}] ${method}`, payload)\n } else {\n this.logger.error(`[${packageName}] ${method}`, payload)\n }\n if (this.crashController) {\n // Only treat \"exception\" + \"unhandledRejection\" + \"uncaught\" as\n // crash signals. `console.error` calls also flow through the\n // log path — those don't end the JSContext.\n const isCrash = method === \"exception\" || method === \"unhandledRejection\" || method === \"uncaught\"\n if (isCrash) {\n this.handleCrash(packageName, this.summarizeReason(payload, method))\n }\n }\n return\n }\n\n // 4) Anything else is a `forwardToRn` from the dispatcher — a\n // handler the native dispatcher didn't know about. We don't have\n // inline handlers for any of these; the runtime's existing path\n // treats them as legacy WebView envelopes by falling through.\n // Log so unknown ifaces surface in dev.\n this.logger.log(`unhandled iface=${iface} method=${method} from ${packageName}`)\n }\n\n /**\n * The wire format for `__bridge.send` is `argsJson = JSON.stringify([raw])`\n * — a single-element array containing the original raw envelope. The\n * dispatcher passes the parsed array through as either:\n * - `msg.args` when the dispatcher unpacked (Android path that turns\n * `argsJson` into a List on the way to RN), or\n * - `msg.argsJson` (iOS path that ships the string verbatim).\n * Cover both shapes here.\n */\n /**\n * Parse a raw bridge envelope just enough to peek at the inner\n * payload's `type` field. Used by the UI_SEND interception path.\n * Returns null if the envelope isn't a valid bridge frame.\n */\n /** One deadline per spawn covers a background that never connects and an init that never settles. */\n private nextSessionId(packageName: string): string {\n const sessionId = `${packageName}-${Date.now().toString(36)}-${(this.sessionSeq++).toString(36)}`\n this.sessions.set(packageName, sessionId)\n return sessionId\n }\n\n private armReadyDeadline(packageName: string): void {\n this.clearReadyTimer(packageName)\n this.readyTimers.set(\n packageName,\n this.timer.setTimeout(() => {\n this.readyTimers.delete(packageName)\n if (this.replacementConnects.has(packageName)) {\n // No session yet to deliver to: show the UI, keep its frames held.\n this.logger.warn(`${packageName} not connected ${BACKGROUND_READY_TIMEOUT_MS}ms after spawn; showing UI`)\n this.uiRouter?.revealHeldUi(packageName)\n return\n }\n this.logger.warn(`${packageName} not ready ${BACKGROUND_READY_TIMEOUT_MS}ms after spawn; opening UI`)\n this.uiRouter?.backgroundReady(packageName)\n }, BACKGROUND_READY_TIMEOUT_MS),\n )\n }\n\n private markBackgroundReady(packageName: string, cause: \"connect\" | \"ready\"): void {\n // After the deadline fired (or a duplicate READY) there is nothing left to open.\n if (!this.readyTimers.has(packageName)) return\n this.clearReadyTimer(packageName)\n this.logger.log(`background ready for ${packageName}`, {cause})\n this.uiRouter?.backgroundReady(packageName)\n }\n\n private clearReadyTimer(packageName: string): void {\n const timer = this.readyTimers.get(packageName)\n if (timer === undefined) return\n this.timer.clearTimeout(timer)\n this.readyTimers.delete(packageName)\n }\n\n private peekBridgePayloadType(raw: string): {type: string; [k: string]: unknown} | null {\n try {\n const env = JSON.parse(raw) as {payload?: {type?: string}}\n if (env && typeof env.payload === \"object\" && env.payload && typeof env.payload.type === \"string\") {\n return env.payload as {type: string; [k: string]: unknown}\n }\n } catch {\n return null\n }\n return null\n }\n\n private coerceArgsToBridgeRaw(msg: OutboundMessagePayload): string | null {\n if (Array.isArray(msg.args)) {\n const v = msg.args[0]\n return typeof v === \"string\" ? v : null\n }\n if (typeof msg.argsJson === \"string\") {\n try {\n const parsed = JSON.parse(msg.argsJson) as unknown\n if (Array.isArray(parsed) && typeof parsed[0] === \"string\") {\n return parsed[0]\n }\n } catch {\n // fall through\n }\n }\n return null\n }\n\n private tryParseArgs(argsJson: string | undefined): unknown {\n if (!argsJson) return null\n try {\n return JSON.parse(argsJson)\n } catch {\n return argsJson\n }\n }\n\n /**\n * Push a `kind=\"bridge\"` envelope into the named JSContext's\n * `globalThis.__deliver`. Used by the per-app `sendMessage` registered via\n * {@link registerApp} to deliver runtime responses and events locally.\n */\n private dispatchBridgeRaw(packageName: string, raw: string): void {\n const deliveryId = this.peekBridgePayloadType(raw)?.type === \"miniapp_ping\" ? ++this.nextDeliveryId : undefined\n const envelope = deliveryId === undefined ? {kind: \"bridge\", raw} : {kind: \"bridge\", raw, deliveryId}\n if (deliveryId !== undefined) this.logger.log(`PING submitted for ${packageName}`, {deliveryId})\n const failed = (error: unknown) => {\n // Keep content-bearing exception text and raw bridge payloads out of report diagnostics.\n this.logger.warn(`Native delivery rejected for ${packageName}`, {\n deliveryId,\n errorClass: error instanceof Error ? error.constructor.name : typeof error,\n })\n }\n try {\n const result = this.crust.mentraJsDispatchToJs(packageName, envelope)\n if (result) void result.catch(failed)\n } catch (error) {\n failed(error)\n throw error\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MentraJSRouter.js","sourceRoot":"","sources":["../../src/services/MentraJSRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview MentraJSRouter — RN-side host that bridges the per-miniapp\n * JS contexts (iOS-JSC and Android-QuickJS via Zipline) into the existing\n * {@link LocalMiniappRuntime} handler set.\n *\n * Rather than re-implement the 33 dispatch arms inline, the router\n * consumes Crust's `mentrajs_message` Expo event and forwards its\n * `__bridge.send(raw)` payloads to {@link LocalMiniappRuntime.handleRawMessage}.\n * The SDK envelope shape (`{type: \"DISPLAY\", ...}` etc.) is unchanged from\n * the WebView path, so every existing handler runs verbatim. What changes\n * is the *transport*:\n *\n * - **Inbound** (background JS → host): `__dispatch(\"__bridge\", \"send\",\n * [rawJsonString])` → `mentrajs_message` event → router →\n * `LocalMiniappRuntime.handleRawMessage(packageName, raw)`.\n * - **Outbound** (host → background JS): every package registered with\n * the router gets a `sendMessage(raw)` fn that calls\n * `Crust.mentraJsDispatchToJs(packageName, {kind: \"bridge\", raw})`.\n * The polyfill's `__deliver` recognises `kind === \"bridge\"` and calls\n * `__mentraDeliverBridgeRaw(raw)`, which routes into the SDK's\n * {@link DispatchTransport.onMessage} handler.\n *\n * Host-side handler bodies (display fan-out, mic state, transcription,\n * navigation, etc.) live untouched in `LocalMiniappRuntime.ts`.\n *\n * PhoneStreamCoordinator delivers stream status through LocalMiniappRuntime\n * to the miniapp's registered `sendMessage` callback, using the same local\n * bridge as other responses and events.\n *\n * The router also bridges native error / log / unhandled-rejection\n * events (`iface: \"__log\"`, `iface: \"__error\"`) into the standard\n * `console.*` + Sentry breadcrumb pipeline so miniapp telemetry is\n * visible without bringing up a separate sink.\n */\n\nimport type {FleetCrashCategory} from \"@mentra/cloud-protocol\"\nimport type {EventSubscription} from \"expo-modules-core\"\n\nimport devServerBridge from \"./DevServerBridge\"\nimport {fleetEvents} from \"./fleet/fleetEvents\"\nimport type localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport type {InstalledMiniappManifest} from \"./LocalMiniappRuntime\"\nimport type {MentraJSCrashController} from \"./MentraJSCrashController\"\nimport {MentraJSLogRingBuffer, MentraJSLogThrottle, redactSecrets} from \"./MentraJSLogPipeline\"\nimport {islandNotifications} from \"./NotificationsEmitter\"\nimport type {MentraUIRouter} from \"./MentraUIRouter\"\nimport {recordMiniappBuild} from \"./miniappBuildKind\"\nimport {miniappContextRegistry, miniappRunningRegistry} from \"./MiniappRunningRegistry\"\n\n/** The runtime's runtime instance type — the singleton exported from\n * LocalMiniappRuntime.ts (the file's `export default` is the instance,\n * not the class).\n */\ntype LocalMiniappRuntime = typeof localMiniappRuntime\n\n/**\n * Minimal subset of the Crust native module the router uses. Keeping the\n * binding loose lets us mock the module in unit tests without bringing\n * the whole Expo module surface along.\n */\nexport interface MentraJSCrustBinding {\n mentraJsDispatchToJs(packageName: string, envelope: Record<string, unknown>): Promise<void> | void\n mentraJsSetManifest(packageName: string, permissions: string[]): Promise<void> | void\n mentraJsLoadPolyfillBundle?: () => string\n mentraJsSpawn?: (packageName: string, polyfill: string, miniappJs: string) => Promise<boolean> | boolean\n mentraJsKill?: (packageName: string) => Promise<void> | void\n mentraJsAlivePackages?: () => string[]\n addListener: (event: string, handler: (payload: Record<string, unknown>) => void) => EventSubscription\n}\n\nexport interface OutboundMessagePayload {\n packageName: string\n iface: string\n method: string\n argsJson?: string\n args?: unknown[]\n reqId?: string\n // Implementation-internal: any extra fields the dispatcher attached on\n // .forwardToRn (e.g. payload metadata) are passed through verbatim.\n [extra: string]: unknown\n}\n\nexport type RouterLogger = {\n log: (message: string, payload?: unknown) => void\n warn: (message: string, payload?: unknown) => void\n error: (message: string, payload?: unknown) => void\n}\n\nconst defaultLogger: RouterLogger = {\n log: (m, p) => console.log(`[MentraJSRouter] ${m}`, p ?? \"\"),\n\n warn: (m, p) => console.warn(`[MentraJSRouter] ${m}`, p ?? \"\"),\n\n error: (m, p) => console.error(`[MentraJSRouter] ${m}`, p ?? \"\"),\n}\n\n/**\n * How long a spawned background may take to connect and settle its init before\n * the host opens its UI anyway. A hung or broken background must not brick it.\n */\nexport const BACKGROUND_READY_TIMEOUT_MS = 10_000\n\n/** Timer used for the UI-hold deadline and crash respawns. The engine injects BgTimer. */\nexport interface RouterTimer {\n setTimeout(callback: () => void, ms: number): number\n clearTimeout(id: number): void\n}\n\ninterface SpawnCache {\n miniappJs: string\n permissions: string[]\n installedManifest?: InstalledMiniappManifest\n /** Host-derived SYSTEM trust from install provenance; never from the manifest. */\n hostTrustedSystem: boolean\n /** Whether this context is normal user-visible activity (false for a transient action wake). */\n projectRunning: boolean\n /** A local or development build: its data is kept apart and its tokens are marked. */\n developmentBuild: boolean\n}\n\nexport class MentraJSRouter {\n private subscription: EventSubscription | null = null\n private readonly registered: Set<string> = new Set()\n private readonly replacementConnects = new Set<string>()\n private nextDeliveryId = 0\n /** The host's id for each package's current context, sent to it in `init`. */\n private readonly sessions = new Map<string, string>()\n private sessionSeq = 0\n /** Spawned backgrounds whose UI is still held, with their ready deadline. */\n private readonly readyTimers = new Map<string, number>()\n /** Cached spawn arguments so the crash controller can respawn after a backoff. */\n private readonly spawnCache: Map<string, SpawnCache> = new Map()\n /** Active respawn timers (so unregister() can cancel a pending respawn). */\n private readonly respawnTimers: Map<string, number> = new Map()\n\n /**\n * Optional crash controller. When wired, every `__error` frame from a\n * JSContext drives the state machine; an Outcome with\n * scheduleRespawnAfterMs queues a `timer` callback that re-spawns the\n * miniapp's last-known JS source after the backoff delay.\n */\n crashController: MentraJSCrashController | null = null\n\n /**\n * Timer for the UI-hold deadline and crash respawns. MiniappEngine injects\n * BgTimer because plain JS timers pause while the Android app is\n * backgrounded; tests keep the global timers.\n */\n timer: RouterTimer = {\n setTimeout: (callback, ms) => setTimeout(callback, ms) as unknown as number,\n clearTimeout: (id) => clearTimeout(id as unknown as ReturnType<typeof setTimeout>),\n }\n\n /** Hook called when a package transitions to CRASHLOOP_DISABLED. */\n onCrashloop: ((packageName: string, reason: string) => void) | null = null\n /** Hook called on the 2nd-within-window crash (toast UX). */\n onRestartToast: ((packageName: string, reason: string) => void) | null = null\n\n /**\n * Logging plumbing. Every `__log` frame passes through `redactSecrets`\n * (token/password/etc. scrubbed), then the throttle (100/min sustained\n * per pkg, 500 burst), then lands in the ring buffer + the host\n * logger. The ring buffer holds the last 200 lines/pkg so a crash\n * report can attach \"last words\" context.\n *\n * Replace with new instances in tests; production code uses the\n * defaults.\n */\n logThrottle: MentraJSLogThrottle = new MentraJSLogThrottle()\n logRing: MentraJSLogRingBuffer = new MentraJSLogRingBuffer()\n\n /**\n * Optional UI router — when wired, `__bridge.send` envelopes whose\n * inner payload type is `UI_SEND` get routed to the bound WebView\n * instead of through LocalMiniappRuntime. Two-layer miniapps set\n * this; legacy single-bundle WebView miniapps don't and the bridge\n * frames pass through unchanged.\n */\n uiRouter: MentraUIRouter | null = null\n\n constructor(\n private readonly runtime: LocalMiniappRuntime,\n private readonly crust: MentraJSCrustBinding,\n private readonly logger: RouterLogger = defaultLogger,\n ) {}\n\n /**\n * Subscribe to Crust's `mentrajs_message` event. Idempotent — calling\n * start() twice attaches a single listener.\n */\n start(): void {\n if (this.subscription) return\n this.subscription = this.crust.addListener(\"mentrajs_message\", (raw) => {\n try {\n this.handleOutbound(raw as unknown as OutboundMessagePayload)\n } catch (e) {\n this.logger.error(`outbound handler threw`, {error: String(e), raw})\n }\n })\n // A background script unregistered for missing liveness pings is dead\n // weight: its subscriptions are gone (so the mic releases) while the\n // webview keeps rendering stale state. Route it through the same\n // respawn machinery as a crash so it comes back automatically, with\n // the crash controller's backoff + crash-loop protection.\n this.runtime.onLivenessTimeout = (packageName) => {\n this.handleCrash(packageName, \"liveness: missed pings\", \"unresponsive\")\n }\n }\n\n /**\n * Detach the Crust subscription. The router can be restarted later\n * with start(); registrations survive (the runtime's app map is the\n * source of truth).\n */\n stop(): void {\n this.subscription?.remove()\n this.subscription = null\n this.runtime.onLivenessTimeout = null\n }\n\n /**\n * Register a JSContext with the runtime so its handlers can fire\n * `sendMessage(raw)` against the right context. The router builds a\n * `sendMessage` that calls `Crust.mentraJsDispatchToJs(packageName,\n * {kind: \"bridge\", raw})`; the polyfill's `__deliver` then hands the\n * envelope to `DispatchTransport.onMessage` inside the miniapp.\n *\n * Mirrors `MiniappHost.registerRuntime` on the WebView side. Callers\n * (e.g. {@link spawnAndRegister} below, or the host's miniapp launch\n * pipeline) should call this once per spawn.\n */\n registerApp(\n packageName: string,\n installedManifest?: InstalledMiniappManifest,\n hostTrustedSystem = false,\n projectRunning = true,\n developmentBuild = false,\n ): void {\n // Before the context is live: its data partition is pinned, and its tokens are marked, by the build it runs.\n recordMiniappBuild(packageName, {localBuild: developmentBuild})\n this.runtime.registerApp(\n packageName,\n (raw: string) => {\n this.dispatchBridgeRaw(packageName, raw)\n },\n installedManifest,\n hostTrustedSystem,\n projectRunning,\n )\n this.registered.add(packageName)\n miniappContextRegistry.add(packageName)\n this.replacementConnects.add(packageName)\n this.uiRouter?.backgroundStarting(packageName)\n this.armReadyDeadline(packageName)\n // A context normally represents user activity and projects into the\n // running tray. A transient action wake deliberately does not: context\n // liveness and user-visible activity are separate.\n if (projectRunning) miniappRunningRegistry.add(packageName)\n }\n\n /**\n * Convenience: spawn a JSContext via Crust, register the app, and\n * set its declared permissions. Returns true on successful spawn.\n *\n * The host's miniapp launch path\n * (`mobile/src/services/miniapps/launchLocalMiniapp.ts`) calls this\n * once per JSContext mount.\n */\n async spawnAndRegister(\n packageName: string,\n miniappJs: string,\n options?: {\n permissions?: string[]\n installedManifest?: InstalledMiniappManifest\n hostTrustedSystem?: boolean\n /** False for an invocation-scoped transient action wake. */\n projectRunning?: boolean\n /** A local or development build (a local-network install, a development server or snapshot). */\n developmentBuild?: boolean\n },\n ): Promise<boolean> {\n if (!this.crust.mentraJsSpawn) {\n this.logger.warn(\"mentraJsSpawn not available — host binding missing native function\")\n return false\n }\n const polyfill = this.crust.mentraJsLoadPolyfillBundle?.() ?? \"\"\n if (!polyfill) {\n this.logger.warn(`no polyfill bundle loaded for ${packageName}; spawn will likely fail`)\n }\n const ok = await this.crust.mentraJsSpawn(packageName, polyfill, miniappJs)\n if (!ok) {\n this.logger.error(`spawn failed for ${packageName}`)\n return false\n }\n // Two distinct permission gates:\n // 1. Native dispatcher (JSContext → native bridge): `mentraJsSetManifest`\n // checks per-call iface, e.g. __dispatch(\"mic\", \"start\", ...) requires\n // MICROPHONE.\n // 2. LocalMiniappRuntime (SUBSCRIBE envelope): `registerApp(...,\n // installedManifest)` carries the full manifest so SUBSCRIBE's gate\n // can match stream → permission against declared types.\n if (options?.permissions && options.permissions.length > 0) {\n await this.crust.mentraJsSetManifest(packageName, options.permissions)\n }\n const hostTrustedSystem = options?.hostTrustedSystem ?? false\n const projectRunning = options?.projectRunning ?? true\n const developmentBuild = options?.developmentBuild ?? false\n this.registerApp(packageName, options?.installedManifest, hostTrustedSystem, projectRunning, developmentBuild)\n // Cache spawn arguments so the crash controller can respawn the\n // same code after a backoff. permissions + manifest are cached too\n // because the native side resets setManifest on every spawn and\n // LocalMiniappRuntime's installedManifest entry is keyed by\n // connect-time registration.\n this.spawnCache.set(packageName, {\n miniappJs,\n permissions: options?.permissions ?? [],\n installedManifest: options?.installedManifest,\n hostTrustedSystem,\n projectRunning,\n developmentBuild,\n })\n this.crashController?.onSpawn(packageName)\n\n // Fire the `init` envelope so the polyfill's __deliver handler\n // resolves `__mentraInitCallback(sessionId)`. The SDK's\n // `registerMiniapp(handler)` wires that callback to construct\n // MiniappSession and call the user's handler. Without this\n // dispatch the user's code never runs — `registerMiniapp` just\n // assigns to a global and waits.\n const sessionId = this.nextSessionId(packageName)\n this.logger.log(`initializing background for ${packageName}`, {sessionId})\n void this.crust.mentraJsDispatchToJs(packageName, {kind: \"init\", sessionId})\n\n return true\n }\n\n /**\n * Crash-controller integration. Called from the `__error` frame\n * handler. The controller returns an outcome that tells us whether\n * to respawn (with delay) or leave the context dead.\n */\n private handleCrash(packageName: string, reason: string, category: FleetCrashCategory = \"unknown\"): void {\n const cached = this.spawnCache.get(packageName)\n // A transient action cannot resume after a crash. Remove the worker (which\n // fails its pending invocations); only a new invocation may spawn another.\n if (cached?.projectRunning === false) {\n this.logger.warn(`transient background context crashed for ${packageName}`, {reason})\n void this.unregister(packageName)\n return\n }\n // The native context is dead the instant a crash is observed — invalidate\n // its CONNECT handshake NOW so waitForConnect() blocks through the respawn\n // backoff window instead of resolving immediately against the dead context\n // (an action invoked mid-backoff would otherwise be delivered to nothing).\n this.logger.warn(`background restart for ${packageName}`, {\n reason,\n uiBound: this.uiRouter?.isBound(packageName) ?? false,\n })\n this.replacementConnects.delete(packageName)\n this.sessions.delete(packageName)\n this.clearReadyTimer(packageName)\n this.uiRouter?.backgroundRestarting(packageName)\n this.runtime.resetHandshake(packageName)\n const controller = this.crashController\n if (!controller) {\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n if (!cached) {\n this.logger.warn(`crash for ${packageName} but no cached spawn args — cannot respawn`)\n controller.onCrash(packageName, reason)\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n const outcome = controller.onCrash(packageName, reason)\n // Fleet hears the crash by its category, never its reason text (Phase 12).\n const versionName = this.runtime.installedVersionOf?.(packageName)\n fleetEvents.crash({\n packageName,\n ...(versionName ? {versionName} : {}),\n surface: \"context\",\n category,\n outcome: outcome.surfaceCrashloopBanner || outcome.scheduleRespawnAfterMs == null ? \"crash_loop_stopped\" : \"respawn_scheduled\",\n crashesInWindow: Math.max(1, controller.crashCount(packageName)),\n })\n if (outcome.surfaceCrashloopBanner) {\n this.uiRouter?.backgroundStopped(packageName)\n this.onCrashloop?.(packageName, reason)\n islandNotifications.emit({kind: \"miniapp_crashloop\", packageName, reason, timestamp: Date.now()})\n return\n }\n if (outcome.showRestartToast) {\n this.onRestartToast?.(packageName, reason)\n }\n if (outcome.scheduleRespawnAfterMs == null) {\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n // Cancel any prior pending respawn before scheduling a new one.\n const existing = this.respawnTimers.get(packageName)\n if (existing !== undefined) this.timer.clearTimeout(existing)\n const timer = this.timer.setTimeout(() => {\n this.respawnTimers.delete(packageName)\n void (async () => {\n try {\n await this.crust.mentraJsKill?.(packageName)\n } catch {\n /* native may have already torn down */\n }\n const ok = await this.crust.mentraJsSpawn?.(\n packageName,\n this.crust.mentraJsLoadPolyfillBundle?.() ?? \"\",\n cached.miniappJs,\n )\n if (!ok) {\n this.logger.error(`crash respawn failed for ${packageName}`)\n this.uiRouter?.backgroundStopped(packageName)\n return\n }\n if (cached.permissions.length > 0) {\n await this.crust.mentraJsSetManifest(packageName, cached.permissions)\n }\n this.registerApp(\n packageName,\n cached.installedManifest,\n cached.hostTrustedSystem,\n cached.projectRunning,\n cached.developmentBuild,\n )\n controller.onSpawn(packageName)\n // Re-fire the init envelope so the respawned context's\n // `registerMiniapp` handler runs again.\n const sessionId = this.nextSessionId(packageName)\n void this.crust.mentraJsDispatchToJs(packageName, {kind: \"init\", sessionId})\n this.logger.log(`respawned ${packageName} after crash`, {sessionId})\n })()\n }, outcome.scheduleRespawnAfterMs)\n this.respawnTimers.set(packageName, timer)\n }\n\n /** Promote an already-spawned transient context into normal user activity. */\n projectRunning(packageName: string): void {\n if (!this.registered.has(packageName) || miniappRunningRegistry.has(packageName)) return\n miniappRunningRegistry.add(packageName)\n this.runtime.onUserActivated(packageName)\n const cached = this.spawnCache.get(packageName)\n if (cached) cached.projectRunning = true\n }\n\n /** True iff the context is projected as user-visible running activity. */\n isProjectedRunning(packageName: string): boolean {\n return miniappRunningRegistry.has(packageName)\n }\n\n private summarizeReason(payload: unknown, method: string): string {\n if (payload && typeof payload === \"object\" && \"message\" in (payload as Record<string, unknown>)) {\n return `${method}: ${(payload as {message: string}).message}`\n }\n return method\n }\n\n /**\n * Tear down a JSContext and unregister from the runtime. Synchronous\n * teardown — the spec drops the 50ms WILL_DISCONNECT grace window\n * because there's no transport handshake to flush in the JSC world.\n */\n async unregister(packageName: string): Promise<void> {\n if (!this.registered.has(packageName)) return\n // Cancel any pending crash-respawn before tearing down — we don't\n // want a stale timer to re-spawn a miniapp the user just disabled.\n const pending = this.respawnTimers.get(packageName)\n if (pending !== undefined) {\n this.timer.clearTimeout(pending)\n this.respawnTimers.delete(packageName)\n }\n this.replacementConnects.delete(packageName)\n this.sessions.delete(packageName)\n this.clearReadyTimer(packageName)\n this.uiRouter?.backgroundStopped(packageName)\n this.spawnCache.delete(packageName)\n this.crashController?.onKill(packageName)\n this.runtime.unregisterApp(packageName)\n this.registered.delete(packageName)\n // JSContext torn down → miniapp is no longer \"running\".\n miniappRunningRegistry.remove(packageName)\n miniappContextRegistry.remove(packageName)\n if (this.crust.mentraJsKill) {\n try {\n await this.crust.mentraJsKill(packageName)\n } catch (e) {\n this.logger.warn(`mentraJsKill threw for ${packageName}: ${String(e)}`)\n }\n }\n }\n\n /** List packages the router has registered. */\n registeredPackages(): string[] {\n return Array.from(this.registered)\n }\n\n /**\n * Validate that a reused background JSContext is still responsive when its\n * WebView comes foreground. The runtime owns the ping/timeout mechanics; the\n * router gates this to registered packages and keeps LocalMiniappView from\n * reaching into runtime internals.\n */\n probeForegroundLiveness(packageName: string, reason = \"foreground-open\", timeoutMs?: number): void {\n if (!this.registered.has(packageName)) return\n this.runtime.probeForegroundLiveness(packageName, reason, timeoutMs)\n }\n\n // ----------------------------------------------------------------\n // Outbound message handling\n // ----------------------------------------------------------------\n\n private handleOutbound(msg: OutboundMessagePayload): void {\n const {packageName, iface, method} = msg\n if (!packageName || !iface || !method) {\n this.logger.warn(`bad mentrajs_message — missing fields`, msg)\n return\n }\n\n // 1) Bridge frames: the SDK's DispatchTransport.send(raw) lands here.\n // Forward raw envelope into LocalMiniappRuntime.handleRawMessage\n // so every existing handler arm runs verbatim.\n if (iface === \"__bridge\" && method === \"send\") {\n const raw = this.coerceArgsToBridgeRaw(msg)\n if (raw == null) {\n this.logger.warn(`__bridge.send had no raw payload`, msg)\n return\n }\n // If the payload is a UI_SEND envelope, route it to the bound\n // WebView instead of LocalMiniappRuntime.\n // session.ui.send → DispatchTransport.send → here.\n const innerPayload = this.peekBridgePayloadType(raw)\n if (this.uiRouter && innerPayload?.type === \"UI_SEND\") {\n this.uiRouter.routeFromBackground(packageName, innerPayload)\n return\n }\n // A context killed for a restart can still have CONNECT or READY queued.\n // SDKs that echo the host's session id let us drop them before the\n // runtime handshakes with them; older ones send none and are trusted.\n if (typeof innerPayload?.sessionId === \"string\" && innerPayload.sessionId !== this.sessions.get(packageName)) {\n this.logger.warn(`dropping ${String(innerPayload.type)} from a previous ${packageName} context`)\n return\n }\n this.runtime.handleRawMessage(packageName, raw)\n // CONNECT_ACK is sent synchronously by handleConnect, so UI_OPEN lands\n // after the fresh SDK transport exists.\n if (innerPayload?.type === \"miniapp_connect\") {\n this.logger.log(`CONNECT received for ${packageName}`, {uiBound: this.uiRouter?.isBound(packageName) ?? false})\n if (this.replacementConnects.delete(packageName)) {\n if (!this.readyTimers.has(packageName)) {\n // The deadline passed before CONNECT: the UI is showing and its\n // frames were held for this session. Release them now.\n this.uiRouter?.backgroundReady(packageName)\n } else if (innerPayload.initReady !== true) {\n // SDKs that announce READY keep the UI held until their init\n // settles; older ones are ready once connected.\n this.markBackgroundReady(packageName, \"connect\")\n }\n }\n } else if (innerPayload?.type === \"miniapp_ready\") {\n // Only the current session's READY counts: one queued by a killed\n // context can arrive after its replacement spawned but before it\n // connected.\n if (!this.replacementConnects.has(packageName)) this.markBackgroundReady(packageName, \"ready\")\n }\n return\n }\n\n // 2) Log frames — console.* rewired through host. We tag with the\n // packageName so Sentry breadcrumbs can be filtered downstream.\n // Pipeline:\n // raw args → redactSecrets → throttle decision → ring buffer\n // → host logger (which surfaces to dev console / Sentry)\n if (iface === \"__log\") {\n const rawArgs = this.tryParseArgs(msg.argsJson)\n const safeArgs = redactSecrets(rawArgs)\n const decision = this.logThrottle.consume(packageName)\n const tag = `[${packageName}]`\n const fn =\n method === \"warn\"\n ? this.logger.warn\n : method === \"error\" || method === \"fatal\"\n ? this.logger.error\n : this.logger.log\n // Emit a synthetic \"[throttled N]\" line in place of the next\n // would-be-allowed log to summarise drops.\n if (!decision.allowed) {\n if (decision.throttledLine) {\n fn(`${tag} ${decision.throttledLine}`)\n this.logRing.push(packageName, `${decision.throttledLine}`)\n }\n return\n }\n const formatted = `${tag} console.${method}`\n fn(formatted, safeArgs)\n // Stringify into the ring buffer for crash-report attachment.\n let line: string\n try {\n line = `${method}: ${JSON.stringify(safeArgs)}`\n } catch {\n line = `${method}: <unserializable>`\n }\n this.logRing.push(packageName, line)\n // Forward to the `mentra-miniapp dev` sidecar (if one is connected\n // for this package). Source=\"background\" so the CLI tags lines as\n // [MentraJS]. The bridge silently drops when no dev server is up,\n // which makes this safe to call unconditionally in prod.\n const argsForBridge = Array.isArray(safeArgs) ? (safeArgs as unknown[]) : [safeArgs]\n devServerBridge.forwardLog(packageName, method, argsForBridge, Date.now(), \"background\")\n return\n }\n\n // 3) Error / unhandled rejection frames — surface as warnings AND\n // drive the crash state machine when one is attached. The\n // controller's outcome may schedule a respawn after a backoff;\n // if it doesn't (CRASHLOOP_DISABLED), we leave the JSContext\n // dead and let the host surface a \"tap to retry\" banner.\n if (iface === \"__error\") {\n const payload = this.tryParseArgs(msg.argsJson)\n // ready_nack is a liveness probe, not a crash. Logging it at error\n // painted WHIP-start queue delay as a Mentra-Call exception.\n if (method === \"ready_nack\") {\n this.logger.warn(`[${packageName}] ${method}`, payload)\n } else {\n this.logger.error(`[${packageName}] ${method}`, payload)\n }\n if (this.crashController) {\n // Only treat \"exception\" + \"unhandledRejection\" + \"uncaught\" as\n // crash signals. `console.error` calls also flow through the\n // log path — those don't end the JSContext.\n const isCrash = method === \"exception\" || method === \"unhandledRejection\" || method === \"uncaught\"\n if (isCrash) {\n this.handleCrash(packageName, this.summarizeReason(payload, method), \"uncaught_exception\")\n }\n }\n return\n }\n\n // 4) Anything else is a `forwardToRn` from the dispatcher — a\n // handler the native dispatcher didn't know about. We don't have\n // inline handlers for any of these; the runtime's existing path\n // treats them as legacy WebView envelopes by falling through.\n // Log so unknown ifaces surface in dev.\n this.logger.log(`unhandled iface=${iface} method=${method} from ${packageName}`)\n }\n\n /**\n * The wire format for `__bridge.send` is `argsJson = JSON.stringify([raw])`\n * — a single-element array containing the original raw envelope. The\n * dispatcher passes the parsed array through as either:\n * - `msg.args` when the dispatcher unpacked (Android path that turns\n * `argsJson` into a List on the way to RN), or\n * - `msg.argsJson` (iOS path that ships the string verbatim).\n * Cover both shapes here.\n */\n /**\n * Parse a raw bridge envelope just enough to peek at the inner\n * payload's `type` field. Used by the UI_SEND interception path.\n * Returns null if the envelope isn't a valid bridge frame.\n */\n /** One deadline per spawn covers a background that never connects and an init that never settles. */\n private nextSessionId(packageName: string): string {\n const sessionId = `${packageName}-${Date.now().toString(36)}-${(this.sessionSeq++).toString(36)}`\n this.sessions.set(packageName, sessionId)\n return sessionId\n }\n\n private armReadyDeadline(packageName: string): void {\n this.clearReadyTimer(packageName)\n this.readyTimers.set(\n packageName,\n this.timer.setTimeout(() => {\n this.readyTimers.delete(packageName)\n if (this.replacementConnects.has(packageName)) {\n // No session yet to deliver to: show the UI, keep its frames held.\n this.logger.warn(`${packageName} not connected ${BACKGROUND_READY_TIMEOUT_MS}ms after spawn; showing UI`)\n this.uiRouter?.revealHeldUi(packageName)\n return\n }\n this.logger.warn(`${packageName} not ready ${BACKGROUND_READY_TIMEOUT_MS}ms after spawn; opening UI`)\n this.uiRouter?.backgroundReady(packageName)\n }, BACKGROUND_READY_TIMEOUT_MS),\n )\n }\n\n private markBackgroundReady(packageName: string, cause: \"connect\" | \"ready\"): void {\n // After the deadline fired (or a duplicate READY) there is nothing left to open.\n if (!this.readyTimers.has(packageName)) return\n this.clearReadyTimer(packageName)\n this.logger.log(`background ready for ${packageName}`, {cause})\n this.uiRouter?.backgroundReady(packageName)\n }\n\n private clearReadyTimer(packageName: string): void {\n const timer = this.readyTimers.get(packageName)\n if (timer === undefined) return\n this.timer.clearTimeout(timer)\n this.readyTimers.delete(packageName)\n }\n\n private peekBridgePayloadType(raw: string): {type: string; [k: string]: unknown} | null {\n try {\n const env = JSON.parse(raw) as {payload?: {type?: string}}\n if (env && typeof env.payload === \"object\" && env.payload && typeof env.payload.type === \"string\") {\n return env.payload as {type: string; [k: string]: unknown}\n }\n } catch {\n return null\n }\n return null\n }\n\n private coerceArgsToBridgeRaw(msg: OutboundMessagePayload): string | null {\n if (Array.isArray(msg.args)) {\n const v = msg.args[0]\n return typeof v === \"string\" ? v : null\n }\n if (typeof msg.argsJson === \"string\") {\n try {\n const parsed = JSON.parse(msg.argsJson) as unknown\n if (Array.isArray(parsed) && typeof parsed[0] === \"string\") {\n return parsed[0]\n }\n } catch {\n // fall through\n }\n }\n return null\n }\n\n private tryParseArgs(argsJson: string | undefined): unknown {\n if (!argsJson) return null\n try {\n return JSON.parse(argsJson)\n } catch {\n return argsJson\n }\n }\n\n /**\n * Push a `kind=\"bridge\"` envelope into the named JSContext's\n * `globalThis.__deliver`. Used by the per-app `sendMessage` registered via\n * {@link registerApp} to deliver runtime responses and events locally.\n */\n private dispatchBridgeRaw(packageName: string, raw: string): void {\n const deliveryId = this.peekBridgePayloadType(raw)?.type === \"miniapp_ping\" ? ++this.nextDeliveryId : undefined\n const envelope = deliveryId === undefined ? {kind: \"bridge\", raw} : {kind: \"bridge\", raw, deliveryId}\n if (deliveryId !== undefined) this.logger.log(`PING submitted for ${packageName}`, {deliveryId})\n const failed = (error: unknown) => {\n // Keep content-bearing exception text and raw bridge payloads out of report diagnostics.\n this.logger.warn(`Native delivery rejected for ${packageName}`, {\n deliveryId,\n errorClass: error instanceof Error ? error.constructor.name : typeof error,\n })\n }\n try {\n const result = this.crust.mentraJsDispatchToJs(packageName, envelope)\n if (result) void result.catch(failed)\n } catch (error) {\n failed(error)\n throw error\n }\n }\n}\n"]}
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
* a system miniapp). A launch the person makes in front of the app may ask their age once; any other launch needs it
|
|
36
36
|
* declared already.
|
|
37
37
|
*/
|
|
38
|
+
import type { FleetLaunchFailureReason } from "@mentra/cloud-protocol";
|
|
38
39
|
import { type InstalledMiniappManifest } from "./LocalMiniappRuntime";
|
|
39
40
|
import type { MentraJSRouter } from "./MentraJSRouter";
|
|
40
41
|
import { type AgeGateVerdict, type AgeRatedMiniapp } from "./miniappAgeGate";
|
|
@@ -125,6 +126,11 @@ export interface LaunchResult {
|
|
|
125
126
|
/** A development build: a live development server or a development snapshot. */
|
|
126
127
|
devBuild: boolean;
|
|
127
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is
|
|
131
|
+
* `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.
|
|
132
|
+
*/
|
|
133
|
+
export declare function launchFailureReason(error: unknown): FleetLaunchFailureReason;
|
|
128
134
|
declare class MiniappLauncher {
|
|
129
135
|
private deps;
|
|
130
136
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MiniappLauncher.d.ts","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;
|
|
1
|
+
{"version":3,"file":"MiniappLauncher.d.ts","sourceRoot":"","sources":["../../src/services/MiniappLauncher.ts"],"names":[],"mappings":"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"}
|
|
@@ -95,6 +95,22 @@ function serverOrigin(url) {
|
|
|
95
95
|
const match = /^(https?:\/\/[^/?#]+)/i.exec(url);
|
|
96
96
|
return match ? match[1].toLowerCase() : null;
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Why a launch failed, as the code Fleet takes (Phase 12): never the error's text. A bundle that cannot be found is
|
|
100
|
+
* `bundle_missing`; a deployment, age or access block `policy_blocked`; a context that did not start `spawn_failed`.
|
|
101
|
+
*/
|
|
102
|
+
export function launchFailureReason(error) {
|
|
103
|
+
if (error instanceof MiniappAgeRestrictedError || error instanceof MiniappAccessRevokedError)
|
|
104
|
+
return "policy_blocked";
|
|
105
|
+
const message = error instanceof Error ? error.message : "";
|
|
106
|
+
if (message.includes("cannot resolve bundle"))
|
|
107
|
+
return "bundle_missing";
|
|
108
|
+
if (message.includes("by deployment policy"))
|
|
109
|
+
return "policy_blocked";
|
|
110
|
+
if (message.includes("spawn failed"))
|
|
111
|
+
return "spawn_failed";
|
|
112
|
+
return "unknown";
|
|
113
|
+
}
|
|
98
114
|
class MiniappLauncher {
|
|
99
115
|
deps = null;
|
|
100
116
|
/**
|
|
@@ -396,7 +412,7 @@ class MiniappLauncher {
|
|
|
396
412
|
catch (error) {
|
|
397
413
|
// A visible launch reports its own failure; one joined to a hidden wake fails with it.
|
|
398
414
|
if (projectRunning && joinedHidden)
|
|
399
|
-
this.reportLaunch(packageName, "failed", runtimeOptions);
|
|
415
|
+
this.reportLaunch(packageName, "failed", runtimeOptions, undefined, launchFailureReason(error));
|
|
400
416
|
throw error;
|
|
401
417
|
}
|
|
402
418
|
if (projectRunning && this.promote(packageName))
|
|
@@ -412,7 +428,7 @@ class MiniappLauncher {
|
|
|
412
428
|
}
|
|
413
429
|
catch (error) {
|
|
414
430
|
if (projectRunning)
|
|
415
|
-
this.reportLaunch(packageName, "failed", runtimeOptions);
|
|
431
|
+
this.reportLaunch(packageName, "failed", runtimeOptions, undefined, launchFailureReason(error));
|
|
416
432
|
throw error;
|
|
417
433
|
}
|
|
418
434
|
finally {
|
|
@@ -429,13 +445,14 @@ class MiniappLauncher {
|
|
|
429
445
|
return router.isProjectedRunning(packageName);
|
|
430
446
|
}
|
|
431
447
|
/** Reports a launch this call made to Fleet, unless the apps store's `start` is making it and reports it itself. */
|
|
432
|
-
reportLaunch(packageName, outcome, runtimeOptions, manifest) {
|
|
448
|
+
reportLaunch(packageName, outcome, runtimeOptions, manifest, failureReason) {
|
|
433
449
|
fleetEvents.launchUnlessClaimed({
|
|
434
450
|
packageName,
|
|
435
451
|
...(manifest?.name ? { miniappName: manifest.name } : {}),
|
|
436
452
|
...(manifest?.version ? { versionName: manifest.version } : {}),
|
|
437
453
|
start: runtimeOptions?.trigger ?? "automatic",
|
|
438
454
|
outcome,
|
|
455
|
+
...(outcome === "failed" ? { failureReason: failureReason ?? "unknown" } : {}),
|
|
439
456
|
});
|
|
440
457
|
}
|
|
441
458
|
/** Resolve the bundle and spawn the context. Serialized via {@link inFlight}. */
|