@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.js CHANGED
@@ -12,10 +12,10 @@ var BRIDGE_KEYS = {
12
12
  dfu: "dfu",
13
13
  systemStatus: "systemStatus",
14
14
  /**
15
- * Native → client: o back do SO foi pressionado e o cliente reportou
16
- * ter histórico interno (`reportNavigation(true)`). O cliente deve voltar
17
- * UMA página interna. Emitido só quando o nativo decidiu delegar o back
18
- * ao cliente. Vide `BridgeClient.onBack`.
15
+ * Native → client: the OS back button was pressed and the client had
16
+ * reported internal history via `reportNavigation(true)`. Go back exactly
17
+ * ONE internal page. Emitted only when the native side decided to delegate
18
+ * the back gesture. See `BridgeClient.onBack`.
19
19
  */
20
20
  navigationBack: "navigationBack",
21
21
  /**
@@ -23,7 +23,7 @@ var BRIDGE_KEYS = {
23
23
  * AND the language explicitly chosen in the app Settings, so the client can
24
24
  * apply its own precedence rule — including rendering a language the native
25
25
  * chrome does not support. Emitted on attach, after `handshake-ack`, and on
26
- * every change in Settings. Vide `LanguagePayload`.
26
+ * every change in Settings. See `LanguagePayload`.
27
27
  */
28
28
  language: "language"
29
29
  };
@@ -439,7 +439,8 @@ var activeAdapter = null;
439
439
  function createChannelAdapter(channel) {
440
440
  return {
441
441
  isMock: false,
442
- // Canal único: o `handlerName` não viaja — o nativo roteia por `type`.
442
+ // Single channel: `handlerName` does not travel — the native side routes
443
+ // on `type`.
443
444
  callHandler(_handlerName, payload) {
444
445
  channel.postMessage(JSON.stringify(payload));
445
446
  }
@@ -654,7 +655,7 @@ function onMessage(event) {
654
655
  pendingHandshake2 = null;
655
656
  resolve({
656
657
  shellVersion: msg.ack?.shellVersion ?? "unknown",
657
- // `?? null` cobre o shell anterior à 0.17.0, que não mandava o campo.
658
+ // `?? null` covers shells older than 0.17.0, which omitted the field.
658
659
  nativeVersion: msg.ack?.nativeVersion ?? null,
659
660
  accepted: msg.ack?.accepted ?? false
660
661
  });
@@ -797,10 +798,10 @@ var SandboxVerifyResponseMessage = v.object({
797
798
  type: v.literal("sandbox-verify-response"),
798
799
  flutterBridgeReachable: v.boolean(),
799
800
  condorBridgeReachable: v.boolean(),
800
- // Opcional: clientes com SDK < 0.18.0 não enviam.
801
+ // Optional: clients on SDK < 0.18.0 do not send it.
801
802
  nativeChannelReachable: v.optional(v.boolean())
802
803
  });
803
- var ClientToShellMessage = v.variant("type", [
804
+ var ClientToShellMessageSchema = v.variant("type", [
804
805
  SubscribeMessage,
805
806
  UnsubscribeMessage,
806
807
  SendMessage,
@@ -818,12 +819,12 @@ var HandshakeAckMessage = v.object({
818
819
  ack: v.object({
819
820
  shellVersion: v.string(),
820
821
  /**
821
- * Versão do schema declarada pelo **nativo**, repassada pelo shell.
822
+ * Schema version declared by the **native** side, relayed by the shell.
822
823
  *
823
- * `null` quando o nativo ainda não respondeu (ou não existe, como no
824
- * mock de browser). O cliente roda dentro de um iframe sandboxed e não
825
- * fala com o nativo diretamente, então este é o **único** caminho pelo
826
- * qual ele descobre de qual versão são as capacidades disponíveis.
824
+ * `null` when the native side has not answered, or does not exist (browser
825
+ * mock mode). The client runs inside a sandboxed iframe and never talks to
826
+ * the native side directly, so this is the **only** way it can learn which
827
+ * capabilities are available.
827
828
  */
828
829
  nativeVersion: v.nullable(v.string()),
829
830
  accepted: v.boolean()
@@ -838,7 +839,7 @@ var NackMessage = v.object({
838
839
  var VerifySandboxMessage = v.object({
839
840
  type: v.literal("verify-sandbox")
840
841
  });
841
- var ShellToClientMessage = v.variant("type", [
842
+ var ShellToClientMessageSchema = v.variant("type", [
842
843
  EventMessage,
843
844
  HandshakeAckMessage,
844
845
  NackMessage,
@@ -857,7 +858,7 @@ var OVERRIDE_PREFIX = `<script>
857
858
  try {
858
859
  Object.defineProperty(window, 'flutter_inappwebview', {
859
860
  get: function(){ return undefined; },
860
- set: function(_){ /* no-op: bloqueia inje\xE7\xE3o do plugin no iframe */ },
861
+ set: function(_){ /* no-op: blocks the plugin injecting into the iframe */ },
861
862
  configurable: false,
862
863
  });
863
864
  } catch (_) {}
@@ -876,11 +877,11 @@ var OVERRIDE_PREFIX = `<script>
876
877
  });
877
878
  } catch (_) {}
878
879
  try {
879
- // Neutraliza navigator.sendBeacon: canal de exfil que a CSP connect-src
880
- // n\xE3o barra de forma confi\xE1vel em alguns WebViews (pentest 2026-06-24,
881
- // PT-10). O cliente comunica s\xF3 via BridgeClient/postMessage \u2014 n\xE3o tem
882
- // uso leg\xEDtimo de sendBeacon. Getter no-op retorna fun\xE7\xE3o que devolve
883
- // false; setter no-op ignora tentativa de restaurar.
880
+ // Neutralize navigator.sendBeacon: an exfiltration channel that CSP
881
+ // connect-src does not reliably block in some WebViews (pen test PT-10).
882
+ // The client communicates only through BridgeClient/postMessage, so it has
883
+ // no legitimate use for sendBeacon. The no-op getter returns a function
884
+ // that answers false; the no-op setter ignores attempts to restore it.
884
885
  Object.defineProperty(navigator, 'sendBeacon', {
885
886
  get: function(){ return function(){ return false; }; },
886
887
  set: function(_){ /* no-op */ },
@@ -894,18 +895,18 @@ var BridgeShell = class {
894
895
  this.subscriptions = /* @__PURE__ */ new Map();
895
896
  this.detached = false;
896
897
  /**
897
- * Versão do nativo, obtida uma vez e cacheada. `null` até o nativo
898
- * responder — e permanentemente `null` num browser sem nativo.
898
+ * The native version, fetched once and cached. `null` until the native side
899
+ * answers, and permanently `null` in a browser with no native host.
899
900
  */
900
901
  this.nativeVersion = null;
901
- /** Handshake com o nativo em voo; garante uma única chamada. */
902
+ /** In-flight native handshake; guarantees a single call. */
902
903
  this.nativeHandshake = null;
903
- // Map de `verify-sandbox` round-trips pendentes — key gerada por nonce.
904
+ // Pending `verify-sandbox` round trips, keyed by a generated nonce.
904
905
  this.pendingVerifies = /* @__PURE__ */ new Map();
905
906
  this.onMessage = async (event) => {
906
907
  if (this.detached) return;
907
908
  if (event.source !== this.iframe.contentWindow) return;
908
- const parsed = v2.safeParse(ClientToShellMessage, event.data);
909
+ const parsed = v2.safeParse(ClientToShellMessageSchema, event.data);
909
910
  if (!parsed.success) {
910
911
  if (typeof console !== "undefined") {
911
912
  console.debug("[BridgeShell] dropped malformed message:", parsed.issues?.[0]?.message);
@@ -970,41 +971,40 @@ var BridgeShell = class {
970
971
  }
971
972
  }
972
973
  /**
973
- * **Camada 1 do Risco 1.** Bootstrap via fetch de URL.
974
+ * **Layer 1.** Bootstraps by fetching a URL.
974
975
  *
975
- * Atalho para o caso comum HTTP/HTTPS: faz `fetch(clientPath)` e
976
- * delega para `bootstrapClientIframeWithHtml`.
976
+ * A shortcut for the common HTTP/HTTPS case: it runs `fetch(clientPath)` and
977
+ * delegates to `bootstrapClientIframeWithHtml`.
977
978
  *
978
- * **Não funciona em scheme `file://`** — `fetch` em WebView Android
979
- * (Chromium) bloqueia o scheme com "URL scheme 'file' is not supported".
980
- * Para asset bundled em APK ou bundle inlined no build-time, use
981
- * `bootstrapClientIframeWithHtml(htmlString)`.
979
+ * **It does not work on the `file://` scheme** — Android's Chromium WebView
980
+ * blocks it with "URL scheme 'file' is not supported". For an asset bundled
981
+ * in the APK, or a bundle inlined at build time, use
982
+ * `bootstrapClientIframeWithHtml(htmlString)` instead.
982
983
  *
983
- * **Devo ser chamado uma vez no boot do shell**, antes de qualquer
984
- * outra interação. Após retornar, `verifySandbox()` deve ser invocado
985
- * para atestation (Camada 2).
984
+ * **Call it once at shell boot**, before any other interaction. After it
985
+ * returns, invoke `verifySandbox()` for attestation (Layer 2).
986
986
  */
987
987
  async bootstrapClientIframe(clientPath = "client.html") {
988
988
  const res = await fetch(clientPath);
989
- if (!res.ok) throw new Error(`BridgeShell: falha ao carregar ${clientPath} (${res.status})`);
989
+ if (!res.ok) throw new Error(`BridgeShell: failed to load ${clientPath} (${res.status})`);
990
990
  const clientHtml = await res.text();
991
991
  await this.bootstrapClientIframeWithHtml(clientHtml);
992
992
  }
993
993
  /**
994
- * **Camada 1 do Risco 1.** Bootstrap a partir de HTML string.
994
+ * **Layer 1.** Bootstraps from an HTML string.
995
995
  *
996
- * Aceita HTML do cliente diretamente (já lido de arquivo, embedado em
997
- * build-time via `esbuild.define`, lido de IndexedDB, etc.) e seta
998
- * como `srcdoc` do iframe prefixado pelo override script. Override
999
- * roda em parse-time, antes de qualquer outro script do cliente.
996
+ * Takes the client HTML directly — already read from a file, embedded at
997
+ * build time, read from storage — and sets it as the iframe's `srcdoc`,
998
+ * prefixed by the override script, which runs at parse time before any
999
+ * client script.
1000
1000
  *
1001
- * Use quando `fetch` não é aplicável (asset bundled em `file://`,
1002
- * bundle inlined no build, qualquer cenário sem rede). Para HTTP
1003
- * use `bootstrapClientIframe(clientPath)`.
1001
+ * Use this when `fetch` does not apply: an asset bundled under `file://`, a
1002
+ * bundle inlined at build time, any scenario without network. Android
1003
+ * WebView blocks `fetch` on the `file://` scheme, so this is the path the
1004
+ * app actually takes. For HTTP, use `bootstrapClientIframe(clientPath)`.
1004
1005
  *
1005
- * **Devo ser chamado uma vez no boot do shell**, antes de qualquer
1006
- * outra interação. Após retornar, `verifySandbox()` deve ser invocado
1007
- * para atestation (Camada 2).
1006
+ * **Call it once at shell boot**, before any other interaction. After it
1007
+ * returns, invoke `verifySandbox()` for attestation (Layer 2).
1008
1008
  */
1009
1009
  async bootstrapClientIframeWithHtml(clientHtml) {
1010
1010
  this.iframe.srcdoc = OVERRIDE_PREFIX + clientHtml;
@@ -1020,31 +1020,31 @@ var BridgeShell = class {
1020
1020
  });
1021
1021
  }
1022
1022
  /**
1023
- * **Camada 2 do Risco 1.** Round-trip `postMessage` que pede ao iframe
1024
- * atestation autoritativa do estado de `window.flutter_inappwebview`
1025
- * e `window.CondorBridge`. Timeout em 2s.
1023
+ * **Layer 2.** A `postMessage` round trip asking the iframe for an
1024
+ * authoritative attestation of whether the native globals are reachable.
1025
+ * Times out after 2 s.
1026
1026
  *
1027
- * `ok` é `true` apenas se ambos forem `undefined` no iframe. Se vier
1028
- * `false`, **shell deve abortar o boot do app** — Camada 1 falhou e
1029
- * Camada 4 (dual WebView) precisa ser acionada manualmente.
1027
+ * `ok` is `true` only when none of them is reachable. If it comes back
1028
+ * `false`, **the shell must abort the app boot**: Layer 1 failed, and client
1029
+ * code would run with a path to the native layer.
1030
1030
  */
1031
1031
  verifySandbox() {
1032
1032
  if (!this.iframe.contentWindow) {
1033
- return Promise.reject(new Error("BridgeShell: iframe sem contentWindow"));
1033
+ return Promise.reject(new Error("BridgeShell: iframe has no contentWindow"));
1034
1034
  }
1035
1035
  return new Promise((resolve, reject) => {
1036
1036
  const nonce = Math.random().toString(36).slice(2);
1037
1037
  const timer = setTimeout(() => {
1038
1038
  this.pendingVerifies.delete(nonce);
1039
- reject(new Error(`BridgeShell: verify-sandbox timeout ap\xF3s ${VERIFY_TIMEOUT_MS}ms`));
1039
+ reject(new Error(`BridgeShell: verify-sandbox timed out after ${VERIFY_TIMEOUT_MS}ms`));
1040
1040
  }, VERIFY_TIMEOUT_MS);
1041
1041
  this.pendingVerifies.set(nonce, { resolve, reject, timer });
1042
1042
  this.postToClient({ type: "verify-sandbox" });
1043
1043
  });
1044
1044
  }
1045
1045
  /**
1046
- * Encerra o shell — remove listener `message`, cancela subscriptions
1047
- * pendentes no `Bridge` nativo, limpa estado interno.
1046
+ * Tears the shell down: removes the `message` listener, cancels pending
1047
+ * subscriptions on the native `Bridge`, and clears internal state.
1048
1048
  */
1049
1049
  detach() {
1050
1050
  if (this.detached) return;
@@ -1110,13 +1110,13 @@ var BridgeShell = class {
1110
1110
  Bridge.send(command, args);
1111
1111
  }
1112
1112
  /**
1113
- * Obtém o `nativeVersion`, disparando o handshake com o nativo na primeira
1114
- * vez e cacheando o resultado.
1113
+ * Resolves `nativeVersion`, firing the native handshake the first time and
1114
+ * caching the result.
1115
1115
  *
1116
- * ⚠️ `Bridge.handshake` **não tem timeout**: a Promise só resolve quando o
1117
- * nativo emite `handshake-ack`. Num browser sem nativo ela nunca resolve,
1118
- * então o `race` abaixo é o que garante que o cliente sempre receba
1119
- * resposta — com `null` — em vez de ficar pendurado.
1116
+ * ⚠️ `Bridge.handshake` has **no timeout**: its promise only settles when the
1117
+ * native side emits `handshake-ack`. In a browser with no native host it
1118
+ * never settles, so the race below is what guarantees the client always gets
1119
+ * an answer — `null` — instead of hanging.
1120
1120
  */
1121
1121
  async resolveNativeVersion() {
1122
1122
  if (this.nativeVersion !== null) return this.nativeVersion;