@condorinst/mobile-bridge 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -52,10 +52,10 @@ var BRIDGE_KEYS = {
52
52
  dfu: "dfu",
53
53
  systemStatus: "systemStatus",
54
54
  /**
55
- * Native → client: o back do SO foi pressionado e o cliente reportou
56
- * ter histórico interno (`reportNavigation(true)`). O cliente deve voltar
57
- * UMA página interna. Emitido só quando o nativo decidiu delegar o back
58
- * ao cliente. Vide `BridgeClient.onBack`.
55
+ * Native → client: the OS back button was pressed and the client had
56
+ * reported internal history via `reportNavigation(true)`. Go back exactly
57
+ * ONE internal page. Emitted only when the native side decided to delegate
58
+ * the back gesture. See `BridgeClient.onBack`.
59
59
  */
60
60
  navigationBack: "navigationBack",
61
61
  /**
@@ -63,7 +63,7 @@ var BRIDGE_KEYS = {
63
63
  * AND the language explicitly chosen in the app Settings, so the client can
64
64
  * apply its own precedence rule — including rendering a language the native
65
65
  * chrome does not support. Emitted on attach, after `handshake-ack`, and on
66
- * every change in Settings. Vide `LanguagePayload`.
66
+ * every change in Settings. See `LanguagePayload`.
67
67
  */
68
68
  language: "language"
69
69
  };
@@ -479,7 +479,8 @@ var activeAdapter = null;
479
479
  function createChannelAdapter(channel) {
480
480
  return {
481
481
  isMock: false,
482
- // Canal único: o `handlerName` não viaja — o nativo roteia por `type`.
482
+ // Single channel: `handlerName` does not travel — the native side routes
483
+ // on `type`.
483
484
  callHandler(_handlerName, payload) {
484
485
  channel.postMessage(JSON.stringify(payload));
485
486
  }
@@ -694,7 +695,7 @@ function onMessage(event) {
694
695
  pendingHandshake2 = null;
695
696
  resolve({
696
697
  shellVersion: msg.ack?.shellVersion ?? "unknown",
697
- // `?? null` cobre o shell anterior à 0.17.0, que não mandava o campo.
698
+ // `?? null` covers shells older than 0.17.0, which omitted the field.
698
699
  nativeVersion: msg.ack?.nativeVersion ?? null,
699
700
  accepted: msg.ack?.accepted ?? false
700
701
  });
@@ -837,10 +838,10 @@ var SandboxVerifyResponseMessage = v.object({
837
838
  type: v.literal("sandbox-verify-response"),
838
839
  flutterBridgeReachable: v.boolean(),
839
840
  condorBridgeReachable: v.boolean(),
840
- // Opcional: clientes com SDK < 0.18.0 não enviam.
841
+ // Optional: clients on SDK < 0.18.0 do not send it.
841
842
  nativeChannelReachable: v.optional(v.boolean())
842
843
  });
843
- var ClientToShellMessage = v.variant("type", [
844
+ var ClientToShellMessageSchema = v.variant("type", [
844
845
  SubscribeMessage,
845
846
  UnsubscribeMessage,
846
847
  SendMessage,
@@ -858,12 +859,12 @@ var HandshakeAckMessage = v.object({
858
859
  ack: v.object({
859
860
  shellVersion: v.string(),
860
861
  /**
861
- * Versão do schema declarada pelo **nativo**, repassada pelo shell.
862
+ * Schema version declared by the **native** side, relayed by the shell.
862
863
  *
863
- * `null` quando o nativo ainda não respondeu (ou não existe, como no
864
- * mock de browser). O cliente roda dentro de um iframe sandboxed e não
865
- * fala com o nativo diretamente, então este é o **único** caminho pelo
866
- * qual ele descobre de qual versão são as capacidades disponíveis.
864
+ * `null` when the native side has not answered, or does not exist (browser
865
+ * mock mode). The client runs inside a sandboxed iframe and never talks to
866
+ * the native side directly, so this is the **only** way it can learn which
867
+ * capabilities are available.
867
868
  */
868
869
  nativeVersion: v.nullable(v.string()),
869
870
  accepted: v.boolean()
@@ -878,7 +879,7 @@ var NackMessage = v.object({
878
879
  var VerifySandboxMessage = v.object({
879
880
  type: v.literal("verify-sandbox")
880
881
  });
881
- var ShellToClientMessage = v.variant("type", [
882
+ var ShellToClientMessageSchema = v.variant("type", [
882
883
  EventMessage,
883
884
  HandshakeAckMessage,
884
885
  NackMessage,
@@ -897,7 +898,7 @@ var OVERRIDE_PREFIX = `<script>
897
898
  try {
898
899
  Object.defineProperty(window, 'flutter_inappwebview', {
899
900
  get: function(){ return undefined; },
900
- set: function(_){ /* no-op: bloqueia inje\xE7\xE3o do plugin no iframe */ },
901
+ set: function(_){ /* no-op: blocks the plugin injecting into the iframe */ },
901
902
  configurable: false,
902
903
  });
903
904
  } catch (_) {}
@@ -916,11 +917,11 @@ var OVERRIDE_PREFIX = `<script>
916
917
  });
917
918
  } catch (_) {}
918
919
  try {
919
- // Neutraliza navigator.sendBeacon: canal de exfil que a CSP connect-src
920
- // n\xE3o barra de forma confi\xE1vel em alguns WebViews (pentest 2026-06-24,
921
- // PT-10). O cliente comunica s\xF3 via BridgeClient/postMessage \u2014 n\xE3o tem
922
- // uso leg\xEDtimo de sendBeacon. Getter no-op retorna fun\xE7\xE3o que devolve
923
- // false; setter no-op ignora tentativa de restaurar.
920
+ // Neutralize navigator.sendBeacon: an exfiltration channel that CSP
921
+ // connect-src does not reliably block in some WebViews (pen test PT-10).
922
+ // The client communicates only through BridgeClient/postMessage, so it has
923
+ // no legitimate use for sendBeacon. The no-op getter returns a function
924
+ // that answers false; the no-op setter ignores attempts to restore it.
924
925
  Object.defineProperty(navigator, 'sendBeacon', {
925
926
  get: function(){ return function(){ return false; }; },
926
927
  set: function(_){ /* no-op */ },
@@ -934,18 +935,18 @@ var BridgeShell = class {
934
935
  this.subscriptions = /* @__PURE__ */ new Map();
935
936
  this.detached = false;
936
937
  /**
937
- * Versão do nativo, obtida uma vez e cacheada. `null` até o nativo
938
- * responder — e permanentemente `null` num browser sem nativo.
938
+ * The native version, fetched once and cached. `null` until the native side
939
+ * answers, and permanently `null` in a browser with no native host.
939
940
  */
940
941
  this.nativeVersion = null;
941
- /** Handshake com o nativo em voo; garante uma única chamada. */
942
+ /** In-flight native handshake; guarantees a single call. */
942
943
  this.nativeHandshake = null;
943
- // Map de `verify-sandbox` round-trips pendentes — key gerada por nonce.
944
+ // Pending `verify-sandbox` round trips, keyed by a generated nonce.
944
945
  this.pendingVerifies = /* @__PURE__ */ new Map();
945
946
  this.onMessage = async (event) => {
946
947
  if (this.detached) return;
947
948
  if (event.source !== this.iframe.contentWindow) return;
948
- const parsed = v2.safeParse(ClientToShellMessage, event.data);
949
+ const parsed = v2.safeParse(ClientToShellMessageSchema, event.data);
949
950
  if (!parsed.success) {
950
951
  if (typeof console !== "undefined") {
951
952
  console.debug("[BridgeShell] dropped malformed message:", parsed.issues?.[0]?.message);
@@ -1010,41 +1011,40 @@ var BridgeShell = class {
1010
1011
  }
1011
1012
  }
1012
1013
  /**
1013
- * **Camada 1 do Risco 1.** Bootstrap via fetch de URL.
1014
+ * **Layer 1.** Bootstraps by fetching a URL.
1014
1015
  *
1015
- * Atalho para o caso comum HTTP/HTTPS: faz `fetch(clientPath)` e
1016
- * delega para `bootstrapClientIframeWithHtml`.
1016
+ * A shortcut for the common HTTP/HTTPS case: it runs `fetch(clientPath)` and
1017
+ * delegates to `bootstrapClientIframeWithHtml`.
1017
1018
  *
1018
- * **Não funciona em scheme `file://`** — `fetch` em WebView Android
1019
- * (Chromium) bloqueia o scheme com "URL scheme 'file' is not supported".
1020
- * Para asset bundled em APK ou bundle inlined no build-time, use
1021
- * `bootstrapClientIframeWithHtml(htmlString)`.
1019
+ * **It does not work on the `file://` scheme** — Android's Chromium WebView
1020
+ * blocks it with "URL scheme 'file' is not supported". For an asset bundled
1021
+ * in the APK, or a bundle inlined at build time, use
1022
+ * `bootstrapClientIframeWithHtml(htmlString)` instead.
1022
1023
  *
1023
- * **Devo ser chamado uma vez no boot do shell**, antes de qualquer
1024
- * outra interação. Após retornar, `verifySandbox()` deve ser invocado
1025
- * para atestation (Camada 2).
1024
+ * **Call it once at shell boot**, before any other interaction. After it
1025
+ * returns, invoke `verifySandbox()` for attestation (Layer 2).
1026
1026
  */
1027
1027
  async bootstrapClientIframe(clientPath = "client.html") {
1028
1028
  const res = await fetch(clientPath);
1029
- if (!res.ok) throw new Error(`BridgeShell: falha ao carregar ${clientPath} (${res.status})`);
1029
+ if (!res.ok) throw new Error(`BridgeShell: failed to load ${clientPath} (${res.status})`);
1030
1030
  const clientHtml = await res.text();
1031
1031
  await this.bootstrapClientIframeWithHtml(clientHtml);
1032
1032
  }
1033
1033
  /**
1034
- * **Camada 1 do Risco 1.** Bootstrap a partir de HTML string.
1034
+ * **Layer 1.** Bootstraps from an HTML string.
1035
1035
  *
1036
- * Aceita HTML do cliente diretamente (já lido de arquivo, embedado em
1037
- * build-time via `esbuild.define`, lido de IndexedDB, etc.) e seta
1038
- * como `srcdoc` do iframe prefixado pelo override script. Override
1039
- * roda em parse-time, antes de qualquer outro script do cliente.
1036
+ * Takes the client HTML directly — already read from a file, embedded at
1037
+ * build time, read from storage — and sets it as the iframe's `srcdoc`,
1038
+ * prefixed by the override script, which runs at parse time before any
1039
+ * client script.
1040
1040
  *
1041
- * Use quando `fetch` não é aplicável (asset bundled em `file://`,
1042
- * bundle inlined no build, qualquer cenário sem rede). Para HTTP
1043
- * use `bootstrapClientIframe(clientPath)`.
1041
+ * Use this when `fetch` does not apply: an asset bundled under `file://`, a
1042
+ * bundle inlined at build time, any scenario without network. Android
1043
+ * WebView blocks `fetch` on the `file://` scheme, so this is the path the
1044
+ * app actually takes. For HTTP, use `bootstrapClientIframe(clientPath)`.
1044
1045
  *
1045
- * **Devo ser chamado uma vez no boot do shell**, antes de qualquer
1046
- * outra interação. Após retornar, `verifySandbox()` deve ser invocado
1047
- * para atestation (Camada 2).
1046
+ * **Call it once at shell boot**, before any other interaction. After it
1047
+ * returns, invoke `verifySandbox()` for attestation (Layer 2).
1048
1048
  */
1049
1049
  async bootstrapClientIframeWithHtml(clientHtml) {
1050
1050
  this.iframe.srcdoc = OVERRIDE_PREFIX + clientHtml;
@@ -1060,31 +1060,31 @@ var BridgeShell = class {
1060
1060
  });
1061
1061
  }
1062
1062
  /**
1063
- * **Camada 2 do Risco 1.** Round-trip `postMessage` que pede ao iframe
1064
- * atestation autoritativa do estado de `window.flutter_inappwebview`
1065
- * e `window.CondorBridge`. Timeout em 2s.
1063
+ * **Layer 2.** A `postMessage` round trip asking the iframe for an
1064
+ * authoritative attestation of whether the native globals are reachable.
1065
+ * Times out after 2 s.
1066
1066
  *
1067
- * `ok` é `true` apenas se ambos forem `undefined` no iframe. Se vier
1068
- * `false`, **shell deve abortar o boot do app** — Camada 1 falhou e
1069
- * Camada 4 (dual WebView) precisa ser acionada manualmente.
1067
+ * `ok` is `true` only when none of them is reachable. If it comes back
1068
+ * `false`, **the shell must abort the app boot**: Layer 1 failed, and client
1069
+ * code would run with a path to the native layer.
1070
1070
  */
1071
1071
  verifySandbox() {
1072
1072
  if (!this.iframe.contentWindow) {
1073
- return Promise.reject(new Error("BridgeShell: iframe sem contentWindow"));
1073
+ return Promise.reject(new Error("BridgeShell: iframe has no contentWindow"));
1074
1074
  }
1075
1075
  return new Promise((resolve, reject) => {
1076
1076
  const nonce = Math.random().toString(36).slice(2);
1077
1077
  const timer = setTimeout(() => {
1078
1078
  this.pendingVerifies.delete(nonce);
1079
- reject(new Error(`BridgeShell: verify-sandbox timeout ap\xF3s ${VERIFY_TIMEOUT_MS}ms`));
1079
+ reject(new Error(`BridgeShell: verify-sandbox timed out after ${VERIFY_TIMEOUT_MS}ms`));
1080
1080
  }, VERIFY_TIMEOUT_MS);
1081
1081
  this.pendingVerifies.set(nonce, { resolve, reject, timer });
1082
1082
  this.postToClient({ type: "verify-sandbox" });
1083
1083
  });
1084
1084
  }
1085
1085
  /**
1086
- * Encerra o shell — remove listener `message`, cancela subscriptions
1087
- * pendentes no `Bridge` nativo, limpa estado interno.
1086
+ * Tears the shell down: removes the `message` listener, cancels pending
1087
+ * subscriptions on the native `Bridge`, and clears internal state.
1088
1088
  */
1089
1089
  detach() {
1090
1090
  if (this.detached) return;
@@ -1150,13 +1150,13 @@ var BridgeShell = class {
1150
1150
  Bridge.send(command, args);
1151
1151
  }
1152
1152
  /**
1153
- * Obtém o `nativeVersion`, disparando o handshake com o nativo na primeira
1154
- * vez e cacheando o resultado.
1153
+ * Resolves `nativeVersion`, firing the native handshake the first time and
1154
+ * caching the result.
1155
1155
  *
1156
- * ⚠️ `Bridge.handshake` **não tem timeout**: a Promise só resolve quando o
1157
- * nativo emite `handshake-ack`. Num browser sem nativo ela nunca resolve,
1158
- * então o `race` abaixo é o que garante que o cliente sempre receba
1159
- * resposta — com `null` — em vez de ficar pendurado.
1156
+ * ⚠️ `Bridge.handshake` has **no timeout**: its promise only settles when the
1157
+ * native side emits `handshake-ack`. In a browser with no native host it
1158
+ * never settles, so the race below is what guarantees the client always gets
1159
+ * an answer — `null` — instead of hanging.
1160
1160
  */
1161
1161
  async resolveNativeVersion() {
1162
1162
  if (this.nativeVersion !== null) return this.nativeVersion;