@condorinst/mobile-bridge 0.16.0 → 0.18.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.d.cts CHANGED
@@ -82,6 +82,49 @@ interface DevPanelOptions {
82
82
  autoRespondDelayMs?: number;
83
83
  }
84
84
 
85
+ /**
86
+ * Detecção do host nativo + adapter pattern.
87
+ *
88
+ * **v0.1.0:** lançava erro quando `window.flutter_inappwebview` ausente.
89
+ * **v0.2.0:** ativa mock adapter (`createMockAdapter`) — destrava dev em
90
+ * browser comum sem buildar APK.
91
+ * **v0.18.0:** canal `window.CondorNative` (JavaScript channel do
92
+ * `webview_flutter`) — detectado **antes** do `flutter_inappwebview`, que
93
+ * segue suportado por transição. O canal só aceita string: a mensagem vai
94
+ * serializada em JSON. E o gancho `setOutgoingStamp` permite ao shell
95
+ * carimbar toda mensagem de saída (ex.: nonce da Camada 3) sem monkey-patch
96
+ * do host.
97
+ *
98
+ * Adapter ativo é cached na primeira chamada de `getActiveAdapter()`.
99
+ * Tests devem chamar `__resetAdapterForTests()` no `beforeEach` para
100
+ * forçar re-detecção (ex.: após instalar/remover `window.CondorNative`).
101
+ */
102
+
103
+ /**
104
+ * Nome do JavaScript channel registrado pelo nativo
105
+ * (`WebViewController.addJavaScriptChannel`). Não pode ser `CondorBridge`: o
106
+ * plugin publica o canal como `window.<nome>`, o que colidiria com o emitter
107
+ * `window.CondorBridge` instalado por este SDK.
108
+ */
109
+ declare const NATIVE_CHANNEL_NAME = "CondorNative";
110
+ interface FlutterInAppWebView {
111
+ callHandler(name: string, ...args: unknown[]): Promise<unknown>;
112
+ }
113
+ interface NativeChannel {
114
+ postMessage(message: string): void;
115
+ }
116
+ declare global {
117
+ interface Window {
118
+ flutter_inappwebview?: FlutterInAppWebView;
119
+ CondorNative?: NativeChannel;
120
+ }
121
+ }
122
+ /**
123
+ * Transforma toda mensagem JS → Native imediatamente antes do envio.
124
+ * Recebe e devolve o envelope `{type, version, payload}`.
125
+ */
126
+ type OutgoingStamp = (message: Record<string, unknown>) => Record<string, unknown>;
127
+
85
128
  /**
86
129
  * Catálogo das chaves do bridge JS↔Native.
87
130
  *
@@ -132,7 +175,8 @@ type BridgeKey = (typeof BRIDGE_KEYS)[keyof typeof BRIDGE_KEYS];
132
175
  */
133
176
  /**
134
177
  * Mensagem JS → Native, enviada via
135
- * `window.flutter_inappwebview.callHandler('CondorBridge', msg)`.
178
+ * `window.CondorNative.postMessage(JSON.stringify(msg))` (v0.18.0+) ou, no
179
+ * host legado, `window.flutter_inappwebview.callHandler('CondorBridge', msg)`.
136
180
  */
137
181
  interface BridgeIncomingMessage {
138
182
  type: string;
@@ -207,6 +251,11 @@ interface UserDevicePayload {
207
251
  * defensivamente. Não são opcionais no tipo de propósito — opcional
208
252
  * esconderia o skew de versão em vez de deixá-lo explícito.
209
253
  *
254
+ * ⚠️ O `nativeVersion` só chega ao cliente a partir do **SDK 0.17.0**: até a
255
+ * `0.16.x` o shell respondia o handshake do cliente apenas com o
256
+ * `shellVersion`, então essa checagem era impossível de cumprir de dentro do
257
+ * iframe.
258
+ *
210
259
  * `null` significa **canal ausente nesta geração de hardware**, nunca "leitura
211
260
  * zero": `0.0` é leitura válida. Mesma distinção que o backend faz com
212
261
  * `float | null`.
@@ -557,8 +606,8 @@ interface MockState {
557
606
  interface CondorBridge {
558
607
  /**
559
608
  * `true` quando o adapter ativo é mock (browser dev, sem
560
- * `window.flutter_inappwebview`). `false` quando rodando dentro do
561
- * WebView Flutter.
609
+ * `window.CondorNative` nem `window.flutter_inappwebview`). `false` quando
610
+ * rodando dentro do WebView Flutter.
562
611
  */
563
612
  readonly isMock: boolean;
564
613
  /**
@@ -593,6 +642,16 @@ interface CondorBridge {
593
642
  * `Bridge.send('openSettings')`.
594
643
  */
595
644
  openSettings(): void;
645
+ /**
646
+ * Registra uma transformação aplicada a **toda** mensagem JS → Native
647
+ * (`handshake` inclusive) imediatamente antes do envio. `null` remove.
648
+ *
649
+ * Existe para o shell carimbar o nonce da Camada 3
650
+ * (`payload._shellNonce`) num ponto único — antes da 0.18.0 isso exigia
651
+ * monkey-patch de `flutter_inappwebview.callHandler`, que não existe no
652
+ * canal `CondorNative`. Código de cliente não deve usar.
653
+ */
654
+ setOutgoingStamp(stamp: OutgoingStamp | null): void;
596
655
  /**
597
656
  * Dispara `window.CondorBridge.emit(key, payload)` programaticamente —
598
657
  * útil para simular eventos nativos em testes e dev em browser.
@@ -639,6 +698,16 @@ declare const Bridge: CondorBridge;
639
698
 
640
699
  interface BridgeClientHandshakeAck {
641
700
  shellVersion: string;
701
+ /**
702
+ * Versão do schema do **nativo**, repassada pelo shell — `null` se o nativo
703
+ * ainda não respondeu ou não existe (mock de browser).
704
+ *
705
+ * É por este número que um cliente decide se pode depender de capacidade
706
+ * nova: não há negociação de versão, e o cliente sandboxed não alcança o
707
+ * nativo por nenhum outro caminho. Ex.: canais crus de luz
708
+ * (`light_sensor_*` em `deviceLightSensors`) existem a partir da `0.16.0`.
709
+ */
710
+ nativeVersion: string | null;
642
711
  accepted: boolean;
643
712
  }
644
713
  interface CondorBridgeClient {
@@ -755,6 +824,7 @@ declare const ClientToShellMessage: v.VariantSchema<"type", [v.ObjectSchema<{
755
824
  readonly type: v.LiteralSchema<"sandbox-verify-response", undefined>;
756
825
  readonly flutterBridgeReachable: v.BooleanSchema<undefined>;
757
826
  readonly condorBridgeReachable: v.BooleanSchema<undefined>;
827
+ readonly nativeChannelReachable: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
758
828
  }, undefined>], undefined>;
759
829
  type ClientToShellMessage = v.InferOutput<typeof ClientToShellMessage>;
760
830
  declare const ShellToClientMessage: v.VariantSchema<"type", [v.ObjectSchema<{
@@ -765,6 +835,15 @@ declare const ShellToClientMessage: v.VariantSchema<"type", [v.ObjectSchema<{
765
835
  readonly type: v.LiteralSchema<"handshake-ack", undefined>;
766
836
  readonly ack: v.ObjectSchema<{
767
837
  readonly shellVersion: v.StringSchema<undefined>;
838
+ /**
839
+ * Versão do schema declarada pelo **nativo**, repassada pelo shell.
840
+ *
841
+ * `null` quando o nativo ainda não respondeu (ou não existe, como no
842
+ * mock de browser). O cliente roda dentro de um iframe sandboxed e não
843
+ * fala com o nativo diretamente, então este é o **único** caminho pelo
844
+ * qual ele descobre de qual versão são as capacidades disponíveis.
845
+ */
846
+ readonly nativeVersion: v.NullableSchema<v.StringSchema<undefined>, undefined>;
768
847
  readonly accepted: v.BooleanSchema<undefined>;
769
848
  }, undefined>;
770
849
  }, undefined>, v.ObjectSchema<{
@@ -817,6 +896,8 @@ interface ConfirmationRequest {
817
896
  interface SandboxVerifyResult {
818
897
  flutterBridgeReachable: boolean;
819
898
  condorBridgeReachable: boolean;
899
+ /** `window.CondorNative` visível no iframe (v0.18.0). */
900
+ nativeChannelReachable: boolean;
820
901
  ok: boolean;
821
902
  }
822
903
 
@@ -868,6 +949,13 @@ declare class BridgeShell {
868
949
  private readonly onConfirm?;
869
950
  private readonly subscriptions;
870
951
  private detached;
952
+ /**
953
+ * Versão do nativo, obtida uma vez e cacheada. `null` até o nativo
954
+ * responder — e permanentemente `null` num browser sem nativo.
955
+ */
956
+ private nativeVersion;
957
+ /** Handshake com o nativo em voo; garante uma única chamada. */
958
+ private nativeHandshake;
871
959
  private pendingVerifies;
872
960
  constructor(opts: BridgeShellOptions);
873
961
  /**
@@ -923,7 +1011,17 @@ declare class BridgeShell {
923
1011
  private handleSubscribe;
924
1012
  private handleUnsubscribe;
925
1013
  private handleSend;
1014
+ /**
1015
+ * Obtém o `nativeVersion`, disparando o handshake com o nativo na primeira
1016
+ * vez e cacheando o resultado.
1017
+ *
1018
+ * ⚠️ `Bridge.handshake` **não tem timeout**: a Promise só resolve quando o
1019
+ * nativo emite `handshake-ack`. Num browser sem nativo ela nunca resolve,
1020
+ * então o `race` abaixo é o que garante que o cliente sempre receba
1021
+ * resposta — com `null` — em vez de ficar pendurado.
1022
+ */
1023
+ private resolveNativeVersion;
926
1024
  private postToClient;
927
1025
  }
928
1026
 
929
- export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };
1027
+ export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };
package/dist/index.d.ts CHANGED
@@ -82,6 +82,49 @@ interface DevPanelOptions {
82
82
  autoRespondDelayMs?: number;
83
83
  }
84
84
 
85
+ /**
86
+ * Detecção do host nativo + adapter pattern.
87
+ *
88
+ * **v0.1.0:** lançava erro quando `window.flutter_inappwebview` ausente.
89
+ * **v0.2.0:** ativa mock adapter (`createMockAdapter`) — destrava dev em
90
+ * browser comum sem buildar APK.
91
+ * **v0.18.0:** canal `window.CondorNative` (JavaScript channel do
92
+ * `webview_flutter`) — detectado **antes** do `flutter_inappwebview`, que
93
+ * segue suportado por transição. O canal só aceita string: a mensagem vai
94
+ * serializada em JSON. E o gancho `setOutgoingStamp` permite ao shell
95
+ * carimbar toda mensagem de saída (ex.: nonce da Camada 3) sem monkey-patch
96
+ * do host.
97
+ *
98
+ * Adapter ativo é cached na primeira chamada de `getActiveAdapter()`.
99
+ * Tests devem chamar `__resetAdapterForTests()` no `beforeEach` para
100
+ * forçar re-detecção (ex.: após instalar/remover `window.CondorNative`).
101
+ */
102
+
103
+ /**
104
+ * Nome do JavaScript channel registrado pelo nativo
105
+ * (`WebViewController.addJavaScriptChannel`). Não pode ser `CondorBridge`: o
106
+ * plugin publica o canal como `window.<nome>`, o que colidiria com o emitter
107
+ * `window.CondorBridge` instalado por este SDK.
108
+ */
109
+ declare const NATIVE_CHANNEL_NAME = "CondorNative";
110
+ interface FlutterInAppWebView {
111
+ callHandler(name: string, ...args: unknown[]): Promise<unknown>;
112
+ }
113
+ interface NativeChannel {
114
+ postMessage(message: string): void;
115
+ }
116
+ declare global {
117
+ interface Window {
118
+ flutter_inappwebview?: FlutterInAppWebView;
119
+ CondorNative?: NativeChannel;
120
+ }
121
+ }
122
+ /**
123
+ * Transforma toda mensagem JS → Native imediatamente antes do envio.
124
+ * Recebe e devolve o envelope `{type, version, payload}`.
125
+ */
126
+ type OutgoingStamp = (message: Record<string, unknown>) => Record<string, unknown>;
127
+
85
128
  /**
86
129
  * Catálogo das chaves do bridge JS↔Native.
87
130
  *
@@ -132,7 +175,8 @@ type BridgeKey = (typeof BRIDGE_KEYS)[keyof typeof BRIDGE_KEYS];
132
175
  */
133
176
  /**
134
177
  * Mensagem JS → Native, enviada via
135
- * `window.flutter_inappwebview.callHandler('CondorBridge', msg)`.
178
+ * `window.CondorNative.postMessage(JSON.stringify(msg))` (v0.18.0+) ou, no
179
+ * host legado, `window.flutter_inappwebview.callHandler('CondorBridge', msg)`.
136
180
  */
137
181
  interface BridgeIncomingMessage {
138
182
  type: string;
@@ -207,6 +251,11 @@ interface UserDevicePayload {
207
251
  * defensivamente. Não são opcionais no tipo de propósito — opcional
208
252
  * esconderia o skew de versão em vez de deixá-lo explícito.
209
253
  *
254
+ * ⚠️ O `nativeVersion` só chega ao cliente a partir do **SDK 0.17.0**: até a
255
+ * `0.16.x` o shell respondia o handshake do cliente apenas com o
256
+ * `shellVersion`, então essa checagem era impossível de cumprir de dentro do
257
+ * iframe.
258
+ *
210
259
  * `null` significa **canal ausente nesta geração de hardware**, nunca "leitura
211
260
  * zero": `0.0` é leitura válida. Mesma distinção que o backend faz com
212
261
  * `float | null`.
@@ -557,8 +606,8 @@ interface MockState {
557
606
  interface CondorBridge {
558
607
  /**
559
608
  * `true` quando o adapter ativo é mock (browser dev, sem
560
- * `window.flutter_inappwebview`). `false` quando rodando dentro do
561
- * WebView Flutter.
609
+ * `window.CondorNative` nem `window.flutter_inappwebview`). `false` quando
610
+ * rodando dentro do WebView Flutter.
562
611
  */
563
612
  readonly isMock: boolean;
564
613
  /**
@@ -593,6 +642,16 @@ interface CondorBridge {
593
642
  * `Bridge.send('openSettings')`.
594
643
  */
595
644
  openSettings(): void;
645
+ /**
646
+ * Registra uma transformação aplicada a **toda** mensagem JS → Native
647
+ * (`handshake` inclusive) imediatamente antes do envio. `null` remove.
648
+ *
649
+ * Existe para o shell carimbar o nonce da Camada 3
650
+ * (`payload._shellNonce`) num ponto único — antes da 0.18.0 isso exigia
651
+ * monkey-patch de `flutter_inappwebview.callHandler`, que não existe no
652
+ * canal `CondorNative`. Código de cliente não deve usar.
653
+ */
654
+ setOutgoingStamp(stamp: OutgoingStamp | null): void;
596
655
  /**
597
656
  * Dispara `window.CondorBridge.emit(key, payload)` programaticamente —
598
657
  * útil para simular eventos nativos em testes e dev em browser.
@@ -639,6 +698,16 @@ declare const Bridge: CondorBridge;
639
698
 
640
699
  interface BridgeClientHandshakeAck {
641
700
  shellVersion: string;
701
+ /**
702
+ * Versão do schema do **nativo**, repassada pelo shell — `null` se o nativo
703
+ * ainda não respondeu ou não existe (mock de browser).
704
+ *
705
+ * É por este número que um cliente decide se pode depender de capacidade
706
+ * nova: não há negociação de versão, e o cliente sandboxed não alcança o
707
+ * nativo por nenhum outro caminho. Ex.: canais crus de luz
708
+ * (`light_sensor_*` em `deviceLightSensors`) existem a partir da `0.16.0`.
709
+ */
710
+ nativeVersion: string | null;
642
711
  accepted: boolean;
643
712
  }
644
713
  interface CondorBridgeClient {
@@ -755,6 +824,7 @@ declare const ClientToShellMessage: v.VariantSchema<"type", [v.ObjectSchema<{
755
824
  readonly type: v.LiteralSchema<"sandbox-verify-response", undefined>;
756
825
  readonly flutterBridgeReachable: v.BooleanSchema<undefined>;
757
826
  readonly condorBridgeReachable: v.BooleanSchema<undefined>;
827
+ readonly nativeChannelReachable: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
758
828
  }, undefined>], undefined>;
759
829
  type ClientToShellMessage = v.InferOutput<typeof ClientToShellMessage>;
760
830
  declare const ShellToClientMessage: v.VariantSchema<"type", [v.ObjectSchema<{
@@ -765,6 +835,15 @@ declare const ShellToClientMessage: v.VariantSchema<"type", [v.ObjectSchema<{
765
835
  readonly type: v.LiteralSchema<"handshake-ack", undefined>;
766
836
  readonly ack: v.ObjectSchema<{
767
837
  readonly shellVersion: v.StringSchema<undefined>;
838
+ /**
839
+ * Versão do schema declarada pelo **nativo**, repassada pelo shell.
840
+ *
841
+ * `null` quando o nativo ainda não respondeu (ou não existe, como no
842
+ * mock de browser). O cliente roda dentro de um iframe sandboxed e não
843
+ * fala com o nativo diretamente, então este é o **único** caminho pelo
844
+ * qual ele descobre de qual versão são as capacidades disponíveis.
845
+ */
846
+ readonly nativeVersion: v.NullableSchema<v.StringSchema<undefined>, undefined>;
768
847
  readonly accepted: v.BooleanSchema<undefined>;
769
848
  }, undefined>;
770
849
  }, undefined>, v.ObjectSchema<{
@@ -817,6 +896,8 @@ interface ConfirmationRequest {
817
896
  interface SandboxVerifyResult {
818
897
  flutterBridgeReachable: boolean;
819
898
  condorBridgeReachable: boolean;
899
+ /** `window.CondorNative` visível no iframe (v0.18.0). */
900
+ nativeChannelReachable: boolean;
820
901
  ok: boolean;
821
902
  }
822
903
 
@@ -868,6 +949,13 @@ declare class BridgeShell {
868
949
  private readonly onConfirm?;
869
950
  private readonly subscriptions;
870
951
  private detached;
952
+ /**
953
+ * Versão do nativo, obtida uma vez e cacheada. `null` até o nativo
954
+ * responder — e permanentemente `null` num browser sem nativo.
955
+ */
956
+ private nativeVersion;
957
+ /** Handshake com o nativo em voo; garante uma única chamada. */
958
+ private nativeHandshake;
871
959
  private pendingVerifies;
872
960
  constructor(opts: BridgeShellOptions);
873
961
  /**
@@ -923,7 +1011,17 @@ declare class BridgeShell {
923
1011
  private handleSubscribe;
924
1012
  private handleUnsubscribe;
925
1013
  private handleSend;
1014
+ /**
1015
+ * Obtém o `nativeVersion`, disparando o handshake com o nativo na primeira
1016
+ * vez e cacheando o resultado.
1017
+ *
1018
+ * ⚠️ `Bridge.handshake` **não tem timeout**: a Promise só resolve quando o
1019
+ * nativo emite `handshake-ack`. Num browser sem nativo ela nunca resolve,
1020
+ * então o `race` abaixo é o que garante que o cliente sempre receba
1021
+ * resposta — com `null` — em vez de ficar pendurado.
1022
+ */
1023
+ private resolveNativeVersion;
926
1024
  private postToClient;
927
1025
  }
928
1026
 
929
- export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };
1027
+ export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type LightSensorsActlumus1Data, type LightSensorsActlumus2Data, type LightSensorsRawChannels, type MockMessage, type MockState, NATIVE_CHANNEL_NAME, type OutgoingStamp, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };
package/dist/index.js CHANGED
@@ -430,8 +430,22 @@ function createMockAdapter() {
430
430
  }
431
431
 
432
432
  // src/internal/native.ts
433
+ var NATIVE_CHANNEL_NAME = "CondorNative";
434
+ var outgoingStamp = null;
435
+ function setOutgoingStamp(stamp) {
436
+ outgoingStamp = stamp;
437
+ }
433
438
  var activeAdapter = null;
434
- function createRealAdapter(host) {
439
+ function createChannelAdapter(channel) {
440
+ return {
441
+ isMock: false,
442
+ // Canal único: o `handlerName` não viaja — o nativo roteia por `type`.
443
+ callHandler(_handlerName, payload) {
444
+ channel.postMessage(JSON.stringify(payload));
445
+ }
446
+ };
447
+ }
448
+ function createLegacyAdapter(host) {
435
449
  return {
436
450
  isMock: false,
437
451
  callHandler(handlerName, payload) {
@@ -443,9 +457,13 @@ function detectAdapter() {
443
457
  if (typeof window === "undefined") {
444
458
  return createMockAdapter();
445
459
  }
460
+ const channel = window.CondorNative;
461
+ if (channel && typeof channel.postMessage === "function") {
462
+ return createChannelAdapter(channel);
463
+ }
446
464
  const host = window.flutter_inappwebview;
447
465
  if (host && typeof host.callHandler === "function") {
448
- return createRealAdapter(host);
466
+ return createLegacyAdapter(host);
449
467
  }
450
468
  return createMockAdapter();
451
469
  }
@@ -456,7 +474,8 @@ function getActiveAdapter() {
456
474
  return activeAdapter;
457
475
  }
458
476
  function callNative(handlerName, payload) {
459
- getActiveAdapter().callHandler(handlerName, payload);
477
+ const stamped = outgoingStamp ? outgoingStamp(payload) : payload;
478
+ getActiveAdapter().callHandler(handlerName, stamped);
460
479
  }
461
480
 
462
481
  // src/internal/subscribers.ts
@@ -572,6 +591,9 @@ var Bridge = {
572
591
  openSettings() {
573
592
  this.send("openSettings");
574
593
  },
594
+ setOutgoingStamp(stamp) {
595
+ setOutgoingStamp(stamp);
596
+ },
575
597
  mockEmit(key, payload) {
576
598
  requireMockAdapter().triggerEmit(key, payload ?? {});
577
599
  },
@@ -632,6 +654,8 @@ function onMessage(event) {
632
654
  pendingHandshake2 = null;
633
655
  resolve({
634
656
  shellVersion: msg.ack?.shellVersion ?? "unknown",
657
+ // `?? null` cobre o shell anterior à 0.17.0, que não mandava o campo.
658
+ nativeVersion: msg.ack?.nativeVersion ?? null,
635
659
  accepted: msg.ack?.accepted ?? false
636
660
  });
637
661
  }
@@ -645,7 +669,8 @@ function onMessage(event) {
645
669
  postToShell({
646
670
  type: "sandbox-verify-response",
647
671
  flutterBridgeReachable: typeof w.flutter_inappwebview !== "undefined",
648
- condorBridgeReachable: typeof w.CondorBridge !== "undefined"
672
+ condorBridgeReachable: typeof w.CondorBridge !== "undefined",
673
+ nativeChannelReachable: typeof w.CondorNative !== "undefined"
649
674
  });
650
675
  break;
651
676
  }
@@ -771,7 +796,9 @@ var JsErrorMessage = v.object({
771
796
  var SandboxVerifyResponseMessage = v.object({
772
797
  type: v.literal("sandbox-verify-response"),
773
798
  flutterBridgeReachable: v.boolean(),
774
- condorBridgeReachable: v.boolean()
799
+ condorBridgeReachable: v.boolean(),
800
+ // Opcional: clientes com SDK < 0.18.0 não enviam.
801
+ nativeChannelReachable: v.optional(v.boolean())
775
802
  });
776
803
  var ClientToShellMessage = v.variant("type", [
777
804
  SubscribeMessage,
@@ -790,6 +817,15 @@ var HandshakeAckMessage = v.object({
790
817
  type: v.literal("handshake-ack"),
791
818
  ack: v.object({
792
819
  shellVersion: v.string(),
820
+ /**
821
+ * Versão do schema declarada pelo **nativo**, repassada pelo shell.
822
+ *
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.
827
+ */
828
+ nativeVersion: v.nullable(v.string()),
793
829
  accepted: v.boolean()
794
830
  })
795
831
  });
@@ -810,12 +846,14 @@ var ShellToClientMessage = v.variant("type", [
810
846
  ]);
811
847
 
812
848
  // src/bridge-shell.ts
813
- var SHELL_VERSION = "0.5.1";
849
+ var SHELL_VERSION = "0.6.0";
850
+ var NATIVE_HANDSHAKE_TIMEOUT_MS = 2e3;
814
851
  var VERIFY_TIMEOUT_MS = 2e3;
815
852
  var OVERRIDE_PREFIX = `<script>
816
853
  (function(){
817
854
  try { delete window.flutter_inappwebview; } catch (_) {}
818
855
  try { delete window.CondorBridge; } catch (_) {}
856
+ try { delete window.CondorNative; } catch (_) {}
819
857
  try {
820
858
  Object.defineProperty(window, 'flutter_inappwebview', {
821
859
  get: function(){ return undefined; },
@@ -823,6 +861,13 @@ var OVERRIDE_PREFIX = `<script>
823
861
  configurable: false,
824
862
  });
825
863
  } catch (_) {}
864
+ try {
865
+ Object.defineProperty(window, 'CondorNative', {
866
+ get: function(){ return undefined; },
867
+ set: function(_){ /* no-op: bloqueia o canal do webview_flutter no iframe */ },
868
+ configurable: false,
869
+ });
870
+ } catch (_) {}
826
871
  try {
827
872
  Object.defineProperty(window, 'CondorBridge', {
828
873
  get: function(){ return undefined; },
@@ -848,6 +893,13 @@ var BridgeShell = class {
848
893
  constructor(opts) {
849
894
  this.subscriptions = /* @__PURE__ */ new Map();
850
895
  this.detached = false;
896
+ /**
897
+ * Versão do nativo, obtida uma vez e cacheada. `null` até o nativo
898
+ * responder — e permanentemente `null` num browser sem nativo.
899
+ */
900
+ this.nativeVersion = null;
901
+ /** Handshake com o nativo em voo; garante uma única chamada. */
902
+ this.nativeHandshake = null;
851
903
  // Map de `verify-sandbox` round-trips pendentes — key gerada por nonce.
852
904
  this.pendingVerifies = /* @__PURE__ */ new Map();
853
905
  this.onMessage = async (event) => {
@@ -872,9 +924,11 @@ var BridgeShell = class {
872
924
  await this.handleSend(msg.command, msg.args ?? {});
873
925
  break;
874
926
  case "handshake":
875
- this.postToClient({
876
- type: "handshake-ack",
877
- ack: { shellVersion: SHELL_VERSION, accepted: true }
927
+ void this.resolveNativeVersion().then((nativeVersion) => {
928
+ this.postToClient({
929
+ type: "handshake-ack",
930
+ ack: { shellVersion: SHELL_VERSION, nativeVersion, accepted: true }
931
+ });
878
932
  });
879
933
  break;
880
934
  case "jsError":
@@ -894,10 +948,12 @@ var BridgeShell = class {
894
948
  if (pending) {
895
949
  clearTimeout(pending.timer);
896
950
  this.pendingVerifies.delete(first);
897
- const ok = !msg.flutterBridgeReachable && !msg.condorBridgeReachable;
951
+ const nativeChannelReachable = msg.nativeChannelReachable ?? false;
952
+ const ok = !msg.flutterBridgeReachable && !msg.condorBridgeReachable && !nativeChannelReachable;
898
953
  pending.resolve({
899
954
  flutterBridgeReachable: msg.flutterBridgeReachable,
900
955
  condorBridgeReachable: msg.condorBridgeReachable,
956
+ nativeChannelReachable,
901
957
  ok
902
958
  });
903
959
  }
@@ -1053,6 +1109,29 @@ var BridgeShell = class {
1053
1109
  }
1054
1110
  Bridge.send(command, args);
1055
1111
  }
1112
+ /**
1113
+ * Obtém o `nativeVersion`, disparando o handshake com o nativo na primeira
1114
+ * vez e cacheando o resultado.
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.
1120
+ */
1121
+ async resolveNativeVersion() {
1122
+ if (this.nativeVersion !== null) return this.nativeVersion;
1123
+ if (!this.nativeHandshake) {
1124
+ this.nativeHandshake = Bridge.handshake({ clientVersion: SHELL_VERSION }).then((ack) => {
1125
+ this.nativeVersion = ack.nativeVersion;
1126
+ }).catch(() => {
1127
+ });
1128
+ }
1129
+ await Promise.race([
1130
+ this.nativeHandshake,
1131
+ new Promise((resolve) => setTimeout(resolve, NATIVE_HANDSHAKE_TIMEOUT_MS))
1132
+ ]);
1133
+ return this.nativeVersion;
1134
+ }
1056
1135
  postToClient(msg) {
1057
1136
  if (this.detached) return;
1058
1137
  this.iframe.contentWindow?.postMessage(msg, "*");
@@ -1062,6 +1141,7 @@ export {
1062
1141
  BRIDGE_KEYS,
1063
1142
  Bridge,
1064
1143
  BridgeClient,
1065
- BridgeShell
1144
+ BridgeShell,
1145
+ NATIVE_CHANNEL_NAME
1066
1146
  };
1067
1147
  //# sourceMappingURL=index.js.map