webview.io 1.0.6 → 1.2.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
@@ -10,6 +10,42 @@ var __assign = (this && this.__assign) || function () {
10
10
  };
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
13
49
  var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
14
50
  if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
15
51
  if (ar || !(i in from)) {
@@ -20,6 +56,33 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
20
56
  return to.concat(ar || Array.prototype.slice.call(from));
21
57
  };
22
58
  Object.defineProperty(exports, "__esModule", { value: true });
59
+ // Current protocol version
60
+ var PROTOCOL_VERSION = 1;
61
+ /**
62
+ * The exact fields, in the exact order, that a signature covers.
63
+ *
64
+ * Kept as data rather than written out at each call site because there are
65
+ * three implementations of this protocol — the class signing, the class
66
+ * verifying, and the hand-written bridge that getInjectedJavaScript() injects
67
+ * into the WebView — and they did not agree. The class signed over `size`; the
68
+ * injected bridge left it out of both its sign and its verify. Every signed
69
+ * message the native side sent was therefore refused by the WebView, in
70
+ * silence, for as long as cryptoAuth has existed.
71
+ *
72
+ * The injected bridge now interpolates this same array, so the three cannot
73
+ * drift apart again without changing one line.
74
+ */
75
+ var CANONICAL_FIELDS = ['v', '_event', 'payload', 'cid', 'timestamp', 'size', 'token'];
76
+ function canonicalMessage(data, ts, nonce) {
77
+ var canonical = {};
78
+ for (var _i = 0, CANONICAL_FIELDS_1 = CANONICAL_FIELDS; _i < CANONICAL_FIELDS_1.length; _i++) {
79
+ var field = CANONICAL_FIELDS_1[_i];
80
+ canonical[field] = data[field];
81
+ }
82
+ canonical.ts = ts;
83
+ canonical.nonce = nonce;
84
+ return JSON.stringify(canonical);
85
+ }
23
86
  function newObject(data) {
24
87
  return JSON.parse(JSON.stringify(data));
25
88
  }
@@ -40,11 +103,101 @@ function sanitizePayload(payload, maxSize) {
40
103
  // Basic sanitization - remove functions and undefined values
41
104
  return JSON.parse(JSON.stringify(payload));
42
105
  }
106
+ function constantTimeEqual(a, b) {
107
+ if (a.length !== b.length)
108
+ return false;
109
+ var out = 0;
110
+ for (var i = 0; i < a.length; i++)
111
+ out |= a.charCodeAt(i) ^ b.charCodeAt(i);
112
+ return out === 0;
113
+ }
114
+ function getGlobalCrypto() {
115
+ return (typeof crypto !== 'undefined'
116
+ ? crypto
117
+ : (typeof window !== 'undefined' && window.crypto)
118
+ || (typeof globalThis !== 'undefined' && globalThis.crypto));
119
+ }
120
+ function randomHex(bytes) {
121
+ try {
122
+ var globalCrypto = getGlobalCrypto();
123
+ if (globalCrypto && typeof globalCrypto.getRandomValues === 'function') {
124
+ var buf = new Uint8Array(bytes);
125
+ globalCrypto.getRandomValues(buf);
126
+ return Array.from(buf).map(function (b) { return b.toString(16).padStart(2, '0'); }).join('');
127
+ }
128
+ }
129
+ catch (_a) { }
130
+ // Fallback (NOT cryptographically strong)
131
+ return Array.from({ length: bytes }, function () { return Math.floor(Math.random() * 256).toString(16).padStart(2, '0'); }).join('');
132
+ }
133
+ function hmacSha256Base64Url(secret, message) {
134
+ return __awaiter(this, void 0, void 0, function () {
135
+ var globalCrypto, subtle, enc, key, sig, bytes, bin, i, b64, _a, nodeCrypto, b64;
136
+ return __generator(this, function (_b) {
137
+ switch (_b.label) {
138
+ case 0:
139
+ _b.trys.push([0, 4, , 5]);
140
+ globalCrypto = getGlobalCrypto();
141
+ subtle = globalCrypto === null || globalCrypto === void 0 ? void 0 : globalCrypto.subtle;
142
+ if (!(subtle && typeof subtle.importKey === 'function')) return [3 /*break*/, 3];
143
+ enc = new TextEncoder();
144
+ return [4 /*yield*/, subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])];
145
+ case 1:
146
+ key = _b.sent();
147
+ return [4 /*yield*/, subtle.sign('HMAC', key, enc.encode(message))];
148
+ case 2:
149
+ sig = _b.sent();
150
+ bytes = new Uint8Array(sig);
151
+ bin = '';
152
+ for (i = 0; i < bytes.length; i++)
153
+ bin += String.fromCharCode(bytes[i]);
154
+ b64 = btoa(bin);
155
+ return [2 /*return*/, b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')];
156
+ case 3: return [3 /*break*/, 5];
157
+ case 4:
158
+ _a = _b.sent();
159
+ return [3 /*break*/, 5];
160
+ case 5:
161
+ // Node.js (commonjs) - optional (React Native metro can provide crypto polyfills in some setups)
162
+ try {
163
+ nodeCrypto = globalThis.__wio_node_crypto
164
+ || (globalThis.__wio_node_crypto = (typeof globalThis.require === 'function'
165
+ ? globalThis.require('crypto')
166
+ : undefined));
167
+ if (!nodeCrypto)
168
+ throw new Error('node crypto unavailable');
169
+ b64 = nodeCrypto.createHmac('sha256', secret).update(message).digest('base64');
170
+ return [2 /*return*/, b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')];
171
+ }
172
+ catch (_c) {
173
+ throw new Error('No crypto implementation available for HMAC-SHA256');
174
+ }
175
+ return [2 /*return*/];
176
+ }
177
+ });
178
+ });
179
+ }
43
180
  var ackId = function () {
44
181
  var rmin = 100000, rmax = 999999, timestamp = Date.now(), random = Math.floor(Math.random() * (rmax - rmin + 1) + rmin);
45
182
  return "".concat(timestamp, "_").concat(random);
46
183
  };
47
184
  var generateToken = function () {
185
+ // Prefer cryptographically strong randomness when available
186
+ try {
187
+ var globalCrypto = (typeof crypto !== 'undefined'
188
+ ? crypto
189
+ : (typeof window !== 'undefined' && window.crypto)
190
+ || (typeof globalThis !== 'undefined' && globalThis.crypto));
191
+ if (globalCrypto && typeof globalCrypto.getRandomValues === 'function') {
192
+ var buffer = new Uint32Array(4);
193
+ globalCrypto.getRandomValues(buffer);
194
+ var randomPart = Array.from(buffer).map(function (n) { return n.toString(16); }).join('');
195
+ return "".concat(Date.now(), "_").concat(randomPart);
196
+ }
197
+ }
198
+ catch (_a) {
199
+ // Fall back to Math.random-based implementation below
200
+ }
48
201
  return "".concat(Date.now(), "_").concat(Math.random().toString(36).substring(2, 15));
49
202
  };
50
203
  var RESERVED_EVENTS = [
@@ -64,6 +217,7 @@ var WIO = /** @class */ (function () {
64
217
  this.reconnectAttempts = 0;
65
218
  this.maxReconnectAttempts = 5;
66
219
  this.connectionAttempts = 0;
220
+ this.seenNonces = new Map();
67
221
  if (options && typeof options !== 'object')
68
222
  throw new Error('Invalid Options');
69
223
  this.options = __assign({ debug: false, heartbeatInterval: 30000, connectionTimeout: 10000, maxMessageSize: 1024 * 1024, maxMessagesPerSecond: 100, autoReconnect: true, messageQueueSize: 50, connectionPingInterval: 2000, maxConnectionAttempts: 5 }, options);
@@ -72,6 +226,102 @@ var WIO = /** @class */ (function () {
72
226
  if (options.type)
73
227
  this.peer.type = options.type;
74
228
  }
229
+ WIO.prototype.cryptoCfg = function () {
230
+ var _a, _b;
231
+ if (!this.options.cryptoAuth)
232
+ return undefined;
233
+ return {
234
+ secret: this.options.cryptoAuth.secret,
235
+ requireSigned: !!this.options.cryptoAuth.requireSigned,
236
+ maxSkewMs: (_a = this.options.cryptoAuth.maxSkewMs) !== null && _a !== void 0 ? _a : 2 * 60 * 1000,
237
+ replayWindowSize: (_b = this.options.cryptoAuth.replayWindowSize) !== null && _b !== void 0 ? _b : 500
238
+ };
239
+ };
240
+ /**
241
+ * Forget nonces that can no longer be replayed, and only then cap the map.
242
+ *
243
+ * Age is what decides replayability: a captured message is refused once its
244
+ * `ts` falls outside maxSkewMs, so a nonce is only worth keeping that long.
245
+ * Pruning purely by count made the two defaults contradict each other — 500
246
+ * remembered nonces at the default 100 messages a second is five seconds of
247
+ * history guarding a two-minute acceptance window.
248
+ */
249
+ WIO.prototype.pruneNonces = function (maxSize) {
250
+ var _this = this;
251
+ var _a, _b;
252
+ var cutoff = Date.now() - ((_b = (_a = this.cryptoCfg()) === null || _a === void 0 ? void 0 : _a.maxSkewMs) !== null && _b !== void 0 ? _b : 2 * 60 * 1000), stale = [];
253
+ this.seenNonces.forEach(function (ts, nonce) { ts < cutoff && stale.push(nonce); });
254
+ stale.forEach(function (nonce) { return _this.seenNonces.delete(nonce); });
255
+ if (this.seenNonces.size <= maxSize)
256
+ return;
257
+ this.fire('error', {
258
+ type: 'REPLAY_WINDOW_EXCEEDED',
259
+ remembered: this.seenNonces.size,
260
+ maxSize: maxSize
261
+ });
262
+ var toRemove = this.seenNonces.size - maxSize, keys = Array.from(this.seenNonces.keys());
263
+ for (var k = 0; k < toRemove && k < keys.length; k++)
264
+ this.seenNonces.delete(keys[k]);
265
+ };
266
+ WIO.prototype.signOutgoing = function (messageData) {
267
+ return __awaiter(this, void 0, void 0, function () {
268
+ var cfg, ts, nonce, sig;
269
+ return __generator(this, function (_a) {
270
+ switch (_a.label) {
271
+ case 0:
272
+ cfg = this.cryptoCfg();
273
+ if (!cfg)
274
+ return [2 /*return*/, undefined];
275
+ ts = Date.now(), nonce = randomHex(16);
276
+ return [4 /*yield*/, hmacSha256Base64Url(cfg.secret, canonicalMessage(messageData, ts, nonce))];
277
+ case 1:
278
+ sig = _a.sent();
279
+ return [2 /*return*/, { alg: 'HMAC-SHA256', ts: ts, nonce: nonce, sig: sig }];
280
+ }
281
+ });
282
+ });
283
+ };
284
+ WIO.prototype.verifyIncomingAuth = function (data) {
285
+ return __awaiter(this, void 0, void 0, function () {
286
+ var cfg, _a, alg, ts, nonce, sig, now, expected;
287
+ return __generator(this, function (_b) {
288
+ switch (_b.label) {
289
+ case 0:
290
+ cfg = this.cryptoCfg();
291
+ if (!cfg)
292
+ return [2 /*return*/, true];
293
+ if (!data.auth) {
294
+ return [2 /*return*/, !cfg.requireSigned];
295
+ }
296
+ _a = data.auth, alg = _a.alg, ts = _a.ts, nonce = _a.nonce, sig = _a.sig;
297
+ if (alg !== 'HMAC-SHA256')
298
+ return [2 /*return*/, false];
299
+ if (typeof ts !== 'number' || typeof nonce !== 'string' || typeof sig !== 'string')
300
+ return [2 /*return*/, false];
301
+ now = Date.now();
302
+ if (Math.abs(now - ts) > cfg.maxSkewMs)
303
+ return [2 /*return*/, false
304
+ // The nonce is recorded only once the signature is known good: burning it
305
+ // here let an unsigned or badly signed message consume the nonce of a
306
+ // legitimate one still in flight.
307
+ ];
308
+ // The nonce is recorded only once the signature is known good: burning it
309
+ // here let an unsigned or badly signed message consume the nonce of a
310
+ // legitimate one still in flight.
311
+ if (this.seenNonces.has(nonce))
312
+ return [2 /*return*/, false];
313
+ return [4 /*yield*/, hmacSha256Base64Url(cfg.secret, canonicalMessage(data, ts, nonce))];
314
+ case 1:
315
+ expected = _b.sent();
316
+ if (!constantTimeEqual(expected, sig))
317
+ return [2 /*return*/, false];
318
+ this.seenNonces.set(nonce, ts);
319
+ this.pruneNonces(cfg.replayWindowSize);
320
+ return [2 /*return*/, true];
321
+ }
322
+ });
323
+ });
324
+ };
75
325
  WIO.prototype.debug = function () {
76
326
  var args = [];
77
327
  for (var _i = 0; _i < arguments.length; _i++) {
@@ -324,12 +574,29 @@ var WIO = /** @class */ (function () {
324
574
  * Handle incoming message from WebView
325
575
  */
326
576
  WIO.prototype.handleMessage = function (event) {
577
+ var _this = this;
327
578
  try {
328
579
  var data = JSON.parse(event.nativeEvent.data);
329
580
  // Enhanced security: check valid message structure
330
581
  if (typeof data !== 'object' || !data.hasOwnProperty('_event'))
331
582
  return;
332
- var _a = data, _event = _a._event, payload = _a.payload, cid = _a.cid, timestamp = _a.timestamp, token = _a.token;
583
+ var _a = data, v = _a.v, _event_1 = _a._event, payload_1 = _a.payload, cid_1 = _a.cid, timestamp = _a.timestamp, token = _a.token;
584
+ /**
585
+ * A peer that predates versioning sends no `v`, so absence reads as 1
586
+ * rather than as a refusal. Only a peer speaking a NEWER protocol than
587
+ * this build understands is turned away.
588
+ */
589
+ var messageVersion = v || 1;
590
+ if (messageVersion > PROTOCOL_VERSION) {
591
+ this.fire('error', {
592
+ type: 'UNSUPPORTED_VERSION',
593
+ received: messageVersion,
594
+ supported: PROTOCOL_VERSION
595
+ });
596
+ return;
597
+ }
598
+ if (!this.peer.protocolVersion || this.peer.protocolVersion < messageVersion)
599
+ this.peer.protocolVersion = messageVersion;
333
600
  // Validate origin if specified
334
601
  if (this.peer.origin && event.nativeEvent && 'origin' in event.nativeEvent) {
335
602
  var messageOrigin = event.nativeEvent.origin;
@@ -339,18 +606,18 @@ var WIO = /** @class */ (function () {
339
606
  }
340
607
  }
341
608
  // Handle heartbeat responses
342
- if (_event === '__heartbeat_response') {
609
+ if (_event_1 === '__heartbeat_response') {
343
610
  this.peer.lastHeartbeat = Date.now();
344
611
  return;
345
612
  }
346
613
  // Handle heartbeat requests
347
- if (_event === '__heartbeat') {
614
+ if (_event_1 === '__heartbeat') {
348
615
  this.emit('__heartbeat_response', { timestamp: Date.now() });
349
616
  this.peer.lastHeartbeat = Date.now();
350
617
  return;
351
618
  }
352
619
  // Handle embedded ready announcement
353
- if (_event === '__embedded_ready') {
620
+ if (_event_1 === '__embedded_ready') {
354
621
  this.peer.embeddedReady = true;
355
622
  this.debug("[".concat(this.peer.type, "] Embedded peer ready"));
356
623
  // If we're WEBVIEW and not connected, send ping
@@ -360,13 +627,13 @@ var WIO = /** @class */ (function () {
360
627
  return;
361
628
  }
362
629
  // Handle webview ready signal
363
- if (_event === '__webview_ready') {
630
+ if (_event_1 === '__webview_ready') {
364
631
  this.debug("[".concat(this.peer.type, "] WebView peer ready"));
365
632
  return;
366
633
  }
367
- this.debug("[".concat(this.peer.type, "] Message: ").concat(_event), payload || '');
634
+ this.debug("[".concat(this.peer.type, "] Message: ").concat(_event_1), payload_1 || '');
368
635
  // Handshake: ping event
369
- if (_event === 'ping') {
636
+ if (_event_1 === 'ping') {
370
637
  // EMBEDDED receives ping from WEBVIEW
371
638
  if (this.peer.type === 'EMBEDDED') {
372
639
  this.connectionToken = token;
@@ -377,7 +644,7 @@ var WIO = /** @class */ (function () {
377
644
  return;
378
645
  }
379
646
  // Handshake: pong event
380
- if (_event === 'pong') {
647
+ if (_event_1 === 'pong') {
381
648
  // WEBVIEW receives pong from EMBEDDED
382
649
  if (this.peer.type === 'WEBVIEW') {
383
650
  // Validate token if provided
@@ -400,7 +667,7 @@ var WIO = /** @class */ (function () {
400
667
  return;
401
668
  }
402
669
  // Handshake: connection ack
403
- if (_event === '__connection_ack') {
670
+ if (_event_1 === '__connection_ack') {
404
671
  // EMBEDDED receives ack from WEBVIEW
405
672
  if (this.peer.type === 'EMBEDDED') {
406
673
  // Validate token if provided
@@ -419,8 +686,63 @@ var WIO = /** @class */ (function () {
419
686
  }
420
687
  return;
421
688
  }
689
+ // Cryptographic authentication (optional)
690
+ if (this.options.cryptoAuth) {
691
+ this.verifyIncomingAuth(data)
692
+ .then(function (ok) {
693
+ if (!ok) {
694
+ _this.fire('error', { type: 'AUTH_FAILED', event: _event_1 });
695
+ return;
696
+ }
697
+ // Optional application-level incoming validation (non-reserved events only)
698
+ if (!RESERVED_EVENTS.includes(_event_1)) {
699
+ if (_this.options.allowedIncomingEvents
700
+ && !_this.options.allowedIncomingEvents.includes(_event_1)) {
701
+ _this.fire('error', {
702
+ type: 'DISALLOWED_EVENT',
703
+ direction: 'incoming',
704
+ event: _event_1
705
+ });
706
+ return;
707
+ }
708
+ if (_this.options.validateIncoming
709
+ && !_this.options.validateIncoming(_event_1, payload_1)) {
710
+ _this.fire('error', {
711
+ type: 'INVALID_MESSAGE',
712
+ direction: 'incoming',
713
+ event: _event_1
714
+ });
715
+ return;
716
+ }
717
+ }
718
+ _this.fire(_event_1, payload_1, cid_1);
719
+ })
720
+ .catch(function (error) { return _this.fire('error', { type: 'AUTH_ERROR', event: _event_1, error: String(error) }); });
721
+ return;
722
+ }
723
+ // Optional application-level incoming validation (non-reserved events only)
724
+ if (!RESERVED_EVENTS.includes(_event_1)) {
725
+ if (this.options.allowedIncomingEvents
726
+ && !this.options.allowedIncomingEvents.includes(_event_1)) {
727
+ this.fire('error', {
728
+ type: 'DISALLOWED_EVENT',
729
+ direction: 'incoming',
730
+ event: _event_1
731
+ });
732
+ return;
733
+ }
734
+ if (this.options.validateIncoming
735
+ && !this.options.validateIncoming(_event_1, payload_1)) {
736
+ this.fire('error', {
737
+ type: 'INVALID_MESSAGE',
738
+ direction: 'incoming',
739
+ event: _event_1
740
+ });
741
+ return;
742
+ }
743
+ }
422
744
  // Fire available event listeners
423
- this.fire(_event, payload, cid);
745
+ this.fire(_event_1, payload_1, cid_1);
424
746
  }
425
747
  catch (error) {
426
748
  this.debug("[".concat(this.peer.type, "] Message handling error:"), error);
@@ -509,6 +831,7 @@ var WIO = /** @class */ (function () {
509
831
  });
510
832
  }
511
833
  var messageData = {
834
+ v: PROTOCOL_VERSION,
512
835
  _event: _event,
513
836
  payload: sanitizedPayload,
514
837
  cid: cid,
@@ -531,6 +854,105 @@ var WIO = /** @class */ (function () {
531
854
  }
532
855
  return this;
533
856
  };
857
+ /**
858
+ * Send a signed message (HMAC-SHA256) when `options.cryptoAuth` is configured.
859
+ * This is async because WebCrypto signing is async.
860
+ */
861
+ WIO.prototype.emitSigned = function (_event, payload, fn) {
862
+ var _a;
863
+ return __awaiter(this, void 0, void 0, function () {
864
+ var sanitizedPayload, cid, ackFunction_2, unsigned, auth, messageData, error_1;
865
+ return __generator(this, function (_b) {
866
+ switch (_b.label) {
867
+ case 0:
868
+ if (!this.checkRateLimit())
869
+ return [2 /*return*/, this];
870
+ if (!this.options.cryptoAuth) {
871
+ this.emit(_event, payload, fn);
872
+ return [2 /*return*/, this];
873
+ }
874
+ if (!this.isConnected() && !RESERVED_EVENTS.includes(_event)) {
875
+ this.queueMessage(_event, payload, fn);
876
+ return [2 /*return*/, this];
877
+ }
878
+ if (!this.peer.webViewRef) {
879
+ this.fire('error', { type: 'NO_CONNECTION', event: _event });
880
+ return [2 /*return*/, this];
881
+ }
882
+ if (typeof payload == 'function') {
883
+ fn = payload;
884
+ payload = undefined;
885
+ }
886
+ _b.label = 1;
887
+ case 1:
888
+ _b.trys.push([1, 3, , 4]);
889
+ sanitizedPayload = payload
890
+ ? sanitizePayload(payload, this.options.maxMessageSize)
891
+ : payload;
892
+ cid = void 0;
893
+ if (typeof fn === 'function') {
894
+ ackFunction_2 = fn;
895
+ cid = ackId();
896
+ this.once("".concat(_event, "--").concat(cid, "--@ack"), function (_a) {
897
+ var error = _a.error, args = _a.args;
898
+ return ackFunction_2.apply(void 0, __spreadArray([error], args, false));
899
+ });
900
+ }
901
+ unsigned = {
902
+ v: PROTOCOL_VERSION,
903
+ _event: _event,
904
+ payload: sanitizedPayload,
905
+ cid: cid,
906
+ timestamp: Date.now(),
907
+ size: getMessageSize(sanitizedPayload),
908
+ token: RESERVED_EVENTS.includes(_event) ? this.connectionToken : undefined
909
+ };
910
+ return [4 /*yield*/, this.signOutgoing(unsigned)];
911
+ case 2:
912
+ auth = _b.sent();
913
+ messageData = __assign(__assign({}, unsigned), { auth: auth });
914
+ (_a = this.peer.webViewRef.current) === null || _a === void 0 ? void 0 : _a.postMessage(JSON.stringify(newObject(messageData)));
915
+ return [3 /*break*/, 4];
916
+ case 3:
917
+ error_1 = _b.sent();
918
+ this.debug("[".concat(this.peer.type, "] EmitSigned error:"), error_1);
919
+ this.fire('error', {
920
+ type: 'EMIT_ERROR',
921
+ event: _event,
922
+ error: error_1 instanceof Error ? error_1.message : String(error_1)
923
+ });
924
+ typeof fn === 'function'
925
+ && fn(error_1 instanceof Error ? error_1.message : String(error_1));
926
+ return [3 /*break*/, 4];
927
+ case 4: return [2 /*return*/, this];
928
+ }
929
+ });
930
+ });
931
+ };
932
+ WIO.prototype.emitAsyncSigned = function (_event, payload, timeout) {
933
+ if (timeout === void 0) { timeout = 5000; }
934
+ return __awaiter(this, void 0, void 0, function () {
935
+ var _this = this;
936
+ return __generator(this, function (_a) {
937
+ return [2 /*return*/, new Promise(function (resolve, reject) {
938
+ var timeoutId = setTimeout(function () { return reject(new Error("Event '".concat(_event, "' acknowledgment timeout after ").concat(timeout, "ms"))); }, timeout);
939
+ _this.emitSigned(_event, payload, function (error) {
940
+ var args = [];
941
+ for (var _i = 1; _i < arguments.length; _i++) {
942
+ args[_i - 1] = arguments[_i];
943
+ }
944
+ clearTimeout(timeoutId);
945
+ error
946
+ ? reject(new Error(typeof error === 'string' ? error : 'Ack error'))
947
+ : resolve(args.length === 0 ? undefined : args.length === 1 ? args[0] : args);
948
+ }).catch(function (err) {
949
+ clearTimeout(timeoutId);
950
+ reject(err);
951
+ });
952
+ })];
953
+ });
954
+ });
955
+ };
534
956
  WIO.prototype.on = function (_event, fn) {
535
957
  // Add Event listener
536
958
  if (!this.Events[_event])
@@ -673,7 +1095,9 @@ var WIO = /** @class */ (function () {
673
1095
  * NOTE: Does not auto-initialize - page must call window._wio.listen()
674
1096
  */
675
1097
  WIO.prototype.getInjectedJavaScript = function () {
676
- return "\n (function() {\n try {\n console.log('[EMBEDDED] Initializing WIO bridge...')\n \n const RESERVED_EVENTS = [\n 'ping',\n 'pong',\n '__heartbeat',\n '__heartbeat_response',\n '__embedded_ready',\n '__connection_ack',\n '__webview_ready'\n ]\n \n // Use closure variable to avoid 'this' binding issues\n window._wio = {\n type: 'EMBEDDED',\n connected: false,\n Events: {},\n messageQueue: [],\n connectionToken: null,\n setupComplete: false,\n\n listen: function(){\n if( this.setupComplete ){\n console.warn('[EMBEDDED] Already listening')\n return this\n }\n\n console.log('[EMBEDDED] Setting up message listeners...')\n\n // Listen to messages from React Native\n window.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data\n window._wio.handleMessage( message )\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error ) }\n })\n \n // Android support\n if( typeof document !== 'undefined' ){\n document.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data\n window._wio.handleMessage( message )\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error ) }\n })\n }\n\n this.setupComplete = true\n console.log('[EMBEDDED] Setup complete, starting ready announcements')\n\n // Start announcing readiness\n this.announceReady()\n\n return this\n },\n \n ackId: function(){\n const\n rmin = 100000,\n rmax = 999999,\n timestamp = Date.now(),\n random = Math.floor( Math.random() * ( rmax - rmin + 1 ) + rmin )\n\n return timestamp + '_' + random\n },\n \n fire: function( _event, payload, cid ){\n if( !window._wio.Events[_event] && !window._wio.Events[_event + '--@once'] ){\n console.log('[EMBEDDED] No listener for:', _event)\n return\n }\n \n const ackFn = cid\n ? ( error, ...args ) => window._wio.emit( _event + '--' + cid + '--@ack', { error: error || false, args } )\n : undefined\n \n let listeners = []\n if( window._wio.Events[_event + '--@once'] ){\n _event += '--@once'\n listeners = window._wio.Events[_event]\n\n delete window._wio.Events[_event]\n }\n else listeners = window._wio.Events[_event] || []\n \n listeners.forEach( fn => {\n try { payload !== undefined ? fn( payload, ackFn ) : fn( ackFn ) }\n catch( error ){ console.error('[EMBEDDED] Listener error:', error ) }\n })\n },\n \n emit: function( _event, payload, fn ){\n if( typeof payload === 'function' ){\n fn = payload\n payload = undefined\n }\n \n if( !window._wio.connected && !RESERVED_EVENTS.includes(_event) ){\n window._wio.messageQueue.push({ _event, payload, fn, timestamp: Date.now() })\n console.log('[EMBEDDED] Queued message:', _event)\n return\n }\n \n try {\n let cid\n if( typeof fn === 'function' ){\n cid = window._wio.ackId()\n window._wio.once( _event + '--' + cid + '--@ack', ({ error, args }) => fn( error, ...args ) )\n }\n \n const messageData = {\n _event,\n payload,\n cid,\n timestamp: Date.now(),\n token: RESERVED_EVENTS.includes(_event) ? window._wio.connectionToken : undefined\n }\n \n if( typeof window.ReactNativeWebView !== 'undefined' )\n window.ReactNativeWebView.postMessage( JSON.stringify( messageData ) )\n else console.error('[EMBEDDED] ReactNativeWebView not available')\n }\n catch( error ){\n console.error('[EMBEDDED] Emit error:', error )\n typeof fn === 'function' && fn( String(error) )\n }\n },\n \n on: function( _event, fn ){\n if( !window._wio.Events[_event] ) window._wio.Events[_event] = []\n window._wio.Events[_event].push( fn )\n\n return window._wio\n },\n \n once: function( _event, fn ){\n _event += '--@once'\n if( !window._wio.Events[_event] ) window._wio.Events[_event] = []\n\n window._wio.Events[_event].push( fn )\n\n return window._wio\n },\n \n off: function( _event, fn ){\n if( fn && window._wio.Events[_event] ){\n const index = window._wio.Events[_event].indexOf( fn )\n if( index > -1 ){\n window._wio.Events[_event].splice( index, 1 )\n if( window._wio.Events[_event].length === 0 ) delete window._wio.Events[_event]\n }\n }\n else delete window._wio.Events[_event]\n\n return window._wio\n },\n \n processMessageQueue: function(){\n if( !window._wio.connected || window._wio.messageQueue.length === 0 ) return\n \n console.log('[EMBEDDED] Processing', window._wio.messageQueue.length, 'queued messages')\n const queue = [ ...window._wio.messageQueue ]\n window._wio.messageQueue = []\n \n queue.forEach( msg => {\n try { window._wio.emit( msg._event, msg.payload, msg.fn ) }\n catch( error ){ console.error('[EMBEDDED] Queue process error:', error ) }\n })\n },\n \n handleMessage: function( data ){\n if( !data || !data._event ) return\n \n const { _event, payload, cid, token } = data\n \n console.log('[EMBEDDED] Received:', _event )\n \n // Handle heartbeat response\n if( _event === '__heartbeat_response' )\n return\n \n // Handle heartbeat request\n if( _event === '__heartbeat' ){\n window._wio.emit('__heartbeat_response', { timestamp: Date.now() })\n return\n }\n \n // Handle webview ready signal\n if( _event === '__webview_ready' ){\n console.log('[EMBEDDED] WebView ready signal received')\n return\n }\n \n // Handle ping from WEBVIEW\n if( _event === 'ping' ){\n console.log('[EMBEDDED] Received ping, sending pong')\n window._wio.connectionToken = token\n\n window._wio.emit('pong', { token: window._wio.connectionToken })\n return\n }\n \n // Handle connection acknowledgment\n if( _event === '__connection_ack' ){\n if( token && token !== window._wio.connectionToken ){\n console.error('[EMBEDDED] Invalid connection token in ack')\n return\n }\n \n console.log('[EMBEDDED] Connection established (received ack)')\n\n window._wio.connected = true\n window._wio.processMessageQueue()\n window._wio.fire('connect')\n\n return\n }\n \n // Fire event listeners\n window._wio.fire( _event, payload, cid )\n },\n \n announceReady: function(){\n let attempts = 0\n const maxAttempts = 10\n const interval = 1000\n \n console.log('[EMBEDDED] Starting ready announcements')\n \n const announce = () => {\n if( window._wio.connected ){\n console.log('[EMBEDDED] Connected, stopping announcements')\n return\n }\n \n attempts++\n if( attempts > maxAttempts ){\n console.log('[EMBEDDED] Max announcement attempts reached')\n return\n }\n \n console.log('[EMBEDDED] Announcing ready (attempt', attempts + '/' + maxAttempts + ')')\n window._wio.emit('__embedded_ready')\n \n setTimeout( announce, interval )\n }\n \n announce()\n }\n }\n\n console.log('[EMBEDDED] WIO bridge ready. Call window._wio.listen() to connect.')\n }\n catch( error ){\n console.error('[EMBEDDED] Setup failed:', error )\n \n // Create minimal fallback\n window._wio = {\n error: error.toString(),\n listen: function(){ console.error('[EMBEDDED] WIO failed to initialize') },\n emit: function(){ console.error('[EMBEDDED] WIO failed to initialize') },\n on: function(){},\n once: function(){},\n off: function(){}\n }\n }\n\n true\n })()\n ";
1098
+ var _a, _b, _c, _d, _e;
1099
+ var authSecret = (_a = this.options.cryptoAuth) === null || _a === void 0 ? void 0 : _a.secret;
1100
+ return "\n (function() {\n try {\n console.debug('[EMBEDDED] Initializing WIO bridge...')\n \n const RESERVED_EVENTS = [\n 'ping',\n 'pong',\n '__heartbeat',\n '__heartbeat_response',\n '__embedded_ready',\n '__connection_ack',\n '__webview_ready'\n ]\n \n // Use closure variable to avoid 'this' binding issues\n window._wio = {\n type: 'EMBEDDED',\n connected: false,\n Events: {},\n messageQueue: [],\n connectionToken: null,\n authSecret: ".concat(authSecret ? JSON.stringify(authSecret) : 'null', ",\n setupComplete: false,\n seenNonces: new Map(),\n maxSkewMs: ").concat(((_c = (_b = this.options.cryptoAuth) === null || _b === void 0 ? void 0 : _b.maxSkewMs) !== null && _c !== void 0 ? _c : 2 * 60 * 1000), ",\n replayWindowSize: ").concat(((_e = (_d = this.options.cryptoAuth) === null || _d === void 0 ? void 0 : _d.replayWindowSize) !== null && _e !== void 0 ? _e : 500), ",\n\n listen: function(){\n if( this.setupComplete ){\n console.warn('[EMBEDDED] Already listening')\n return this\n }\n\n console.debug('[EMBEDDED] Setting up message listeners...')\n\n // Listen to messages from React Native\n window.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data\n window._wio.handleMessage( message )\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error ) }\n })\n \n // Android support\n if( typeof document !== 'undefined' ){\n document.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data\n window._wio.handleMessage( message )\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error ) }\n })\n }\n\n this.setupComplete = true\n console.debug('[EMBEDDED] Setup complete, starting ready announcements')\n\n // Start announcing readiness\n this.announceReady()\n\n return this\n },\n \n ackId: function(){\n const\n rmin = 100000,\n rmax = 999999,\n timestamp = Date.now(),\n random = Math.floor( Math.random() * ( rmax - rmin + 1 ) + rmin )\n\n return timestamp + '_' + random\n },\n \n fire: function( _event, payload, cid ){\n if( !window._wio.Events[_event] && !window._wio.Events[_event + '--@once'] ){\n console.debug('[EMBEDDED] No listener for:', _event)\n return\n }\n \n const ackFn = cid\n ? ( error, ...args ) => window._wio.emit( _event + '--' + cid + '--@ack', { error: error || false, args } )\n : undefined\n \n let listeners = []\n if( window._wio.Events[_event + '--@once'] ){\n _event += '--@once'\n listeners = window._wio.Events[_event]\n\n delete window._wio.Events[_event]\n }\n else listeners = window._wio.Events[_event] || []\n \n listeners.forEach( fn => {\n try { payload !== undefined ? fn( payload, ackFn ) : fn( ackFn ) }\n catch( error ){ console.error('[EMBEDDED] Listener error:', error ) }\n })\n },\n \n emit: function( _event, payload, fn ){\n if( typeof payload === 'function' ){\n fn = payload\n payload = undefined\n }\n \n if( !window._wio.connected && !RESERVED_EVENTS.includes(_event) ){\n window._wio.messageQueue.push({ _event, payload, fn, timestamp: Date.now() })\n console.debug('[EMBEDDED] Queued message:', _event)\n return\n }\n \n try {\n let cid\n if( typeof fn === 'function' ){\n cid = window._wio.ackId()\n window._wio.once( _event + '--' + cid + '--@ack', ({ error, args }) => fn( error, ...args ) )\n }\n \n const messageData = {\n _event,\n payload,\n cid,\n timestamp: Date.now(),\n token: RESERVED_EVENTS.includes(_event) ? window._wio.connectionToken : undefined\n }\n \n if( typeof window.ReactNativeWebView !== 'undefined' )\n window.ReactNativeWebView.postMessage( JSON.stringify( messageData ) )\n else console.error('[EMBEDDED] ReactNativeWebView not available')\n }\n catch( error ){\n console.error('[EMBEDDED] Emit error:', error )\n typeof fn === 'function' && fn( String(error) )\n }\n },\n\n emitSigned: async function( _event, payload, fn ){\n if( typeof payload === 'function' ){\n fn = payload\n payload = undefined\n }\n\n if( !window._wio.connected && !RESERVED_EVENTS.includes(_event) ){\n window._wio.messageQueue.push({ _event, payload, fn, timestamp: Date.now(), signed: true })\n console.debug('[EMBEDDED] Queued signed message:', _event)\n return\n }\n\n try {\n let cid\n if( typeof fn === 'function' ){\n cid = window._wio.ackId()\n window._wio.once( _event + '--' + cid + '--@ack', ({ error, args }) => fn( error, ...args ) )\n }\n\n const unsigned = {\n v: ").concat(PROTOCOL_VERSION, ",\n _event,\n payload,\n cid,\n timestamp: Date.now(),\n size: (function(){ try { return JSON.stringify( payload ).length } catch(e){ return 0 } })(),\n token: RESERVED_EVENTS.includes(_event) ? window._wio.connectionToken : undefined\n }\n\n const auth = await window._wio.sign(unsigned)\n const messageData = { ...unsigned, auth }\n\n if( typeof window.ReactNativeWebView !== 'undefined' )\n window.ReactNativeWebView.postMessage( JSON.stringify( messageData ) )\n else console.error('[EMBEDDED] ReactNativeWebView not available')\n }\n catch( error ){\n console.error('[EMBEDDED] EmitSigned error:', error )\n typeof fn === 'function' && fn( String(error) )\n }\n },\n \n on: function( _event, fn ){\n if( !window._wio.Events[_event] ) window._wio.Events[_event] = []\n window._wio.Events[_event].push( fn )\n\n return window._wio\n },\n \n once: function( _event, fn ){\n _event += '--@once'\n if( !window._wio.Events[_event] ) window._wio.Events[_event] = []\n\n window._wio.Events[_event].push( fn )\n\n return window._wio\n },\n \n off: function( _event, fn ){\n if( fn && window._wio.Events[_event] ){\n const index = window._wio.Events[_event].indexOf( fn )\n if( index > -1 ){\n window._wio.Events[_event].splice( index, 1 )\n if( window._wio.Events[_event].length === 0 ) delete window._wio.Events[_event]\n }\n }\n else delete window._wio.Events[_event]\n\n return window._wio\n },\n \n processMessageQueue: function(){\n if( !window._wio.connected || window._wio.messageQueue.length === 0 ) return\n \n console.debug('[EMBEDDED] Processing', window._wio.messageQueue.length, 'queued messages')\n const queue = [ ...window._wio.messageQueue ]\n window._wio.messageQueue = []\n \n queue.forEach( msg => {\n try {\n msg.signed\n ? window._wio.emitSigned( msg._event, msg.payload, msg.fn )\n : window._wio.emit( msg._event, msg.payload, msg.fn )\n }\n catch( error ){ console.error('[EMBEDDED] Queue process error:', error ) }\n })\n },\n\n canonicalFields: ").concat(JSON.stringify(CANONICAL_FIELDS), ",\n\n // Must produce byte-identical output to canonicalMessage() on the\n // native side \u2014 hence the shared field list above.\n canonical: function( data, ts, nonce ){\n const out = {}\n window._wio.canonicalFields.forEach(function( field ){ out[field] = data[field] })\n out.ts = ts\n out.nonce = nonce\n return JSON.stringify( out )\n },\n\n pruneNonces: function(){\n const cutoff = Date.now() - window._wio.maxSkewMs\n const stale = []\n window._wio.seenNonces.forEach(function( ts, nonce ){ if( ts < cutoff ) stale.push( nonce ) })\n stale.forEach(function( nonce ){ window._wio.seenNonces.delete( nonce ) })\n\n if( window._wio.seenNonces.size <= window._wio.replayWindowSize ) return\n\n const toRemove = window._wio.seenNonces.size - window._wio.replayWindowSize\n const keys = Array.from( window._wio.seenNonces.keys() )\n for( let k = 0; k < toRemove && k < keys.length; k++ )\n window._wio.seenNonces.delete( keys[k] )\n },\n\n constantTimeEqual: function(a, b){\n if( a.length !== b.length ) return false\n let out = 0\n for( let i = 0; i < a.length; i++ ) out |= a.charCodeAt(i) ^ b.charCodeAt(i)\n return out === 0\n },\n\n hmacSha256Base64Url: async function(secret, message){\n if( !secret ) throw new Error('Missing auth secret')\n if( !window.crypto || !window.crypto.subtle ) throw new Error('WebCrypto unavailable')\n\n const enc = new TextEncoder()\n const key = await window.crypto.subtle.importKey(\n 'raw',\n enc.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const sig = await window.crypto.subtle.sign('HMAC', key, enc.encode(message))\n const bytes = new Uint8Array(sig)\n let bin = ''\n for( let i = 0; i < bytes.length; i++ ) bin += String.fromCharCode(bytes[i])\n const b64 = btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '')\n return b64\n },\n\n sign: async function(unsigned){\n if( !window._wio.authSecret ) return null\n const ts = Date.now()\n const nonce = (function(){\n try {\n if( window.crypto && window.crypto.getRandomValues ){\n const buf = new Uint8Array(16)\n window.crypto.getRandomValues(buf)\n let out = ''\n for( let i = 0; i < buf.length; i++ ){\n out += ('0' + buf[i].toString(16)).slice(-2)\n }\n return out\n }\n } catch(e){}\n return (Math.random().toString(16).slice(2) + Math.random().toString(16).slice(2)).slice(0, 32)\n })()\n const sig = await window._wio.hmacSha256Base64Url(window._wio.authSecret, window._wio.canonical(unsigned, ts, nonce))\n return { alg: 'HMAC-SHA256', ts, nonce, sig }\n },\n\n verify: async function(data){\n if( !window._wio.authSecret ) return true\n if( !data.auth ) return false\n const { alg, ts, nonce, sig } = data.auth\n if( alg !== 'HMAC-SHA256' ) return false\n\n const now = Date.now()\n if( Math.abs(now - ts) > window._wio.maxSkewMs ) return false\n\n if( window._wio.seenNonces.has(nonce) ) return false\n\n const expected = await window._wio.hmacSha256Base64Url(window._wio.authSecret, window._wio.canonical(data, ts, nonce))\n if( !window._wio.constantTimeEqual(expected, sig) ) return false\n\n window._wio.seenNonces.set(nonce, ts)\n window._wio.pruneNonces()\n\n return true\n },\n \n handleMessage: function( data ){\n if( !data || !data._event ) return\n \n const { _event, payload, cid, token } = data\n \n console.debug('[EMBEDDED] Received:', _event )\n \n // Handle heartbeat response\n if( _event === '__heartbeat_response' )\n return\n \n // Handle heartbeat request\n if( _event === '__heartbeat' ){\n window._wio.emit('__heartbeat_response', { timestamp: Date.now() })\n return\n }\n \n // Handle webview ready signal\n if( _event === '__webview_ready' ){\n console.debug('[EMBEDDED] WebView ready signal received')\n return\n }\n \n // Handle ping from WEBVIEW\n if( _event === 'ping' ){\n console.debug('[EMBEDDED] Received ping, sending pong')\n window._wio.connectionToken = token\n\n window._wio.emit('pong', { token: window._wio.connectionToken })\n return\n }\n \n // Handle connection acknowledgment\n if( _event === '__connection_ack' ){\n if( token && token !== window._wio.connectionToken ){\n console.error('[EMBEDDED] Invalid connection token in ack')\n return\n }\n \n console.debug('[EMBEDDED] Connection established (received ack)')\n\n window._wio.connected = true\n window._wio.processMessageQueue()\n window._wio.fire('connect')\n\n return\n }\n\n // Auth verification (optional, if authSecret is set)\n if( window._wio.authSecret ){\n window._wio.verify(data).then(ok => {\n if( !ok ){\n console.error('[EMBEDDED] Auth verification failed for', _event)\n return\n }\n window._wio.fire( _event, payload, cid )\n }).catch(err => console.error('[EMBEDDED] Auth error:', err))\n return\n }\n \n // Fire event listeners\n window._wio.fire( _event, payload, cid )\n },\n \n announceReady: function(){\n let attempts = 0\n const maxAttempts = 10\n const interval = 1000\n \n console.debug('[EMBEDDED] Starting ready announcements')\n \n const announce = () => {\n if( window._wio.connected ){\n console.debug('[EMBEDDED] Connected, stopping announcements')\n return\n }\n \n attempts++\n if( attempts > maxAttempts ){\n console.debug('[EMBEDDED] Max announcement attempts reached')\n return\n }\n \n console.debug('[EMBEDDED] Announcing ready (attempt', attempts + '/' + maxAttempts + ')')\n window._wio.emit('__embedded_ready')\n \n setTimeout( announce, interval )\n }\n \n announce()\n }\n }\n\n console.debug('[EMBEDDED] WIO bridge ready. Call window._wio.listen() to connect.')\n }\n catch( error ){\n console.error('[EMBEDDED] Setup failed:', error )\n \n // Create minimal fallback\n window._wio = {\n error: error.toString(),\n listen: function(){ console.error('[EMBEDDED] WIO failed to initialize') },\n emit: function(){ console.error('[EMBEDDED] WIO failed to initialize') },\n on: function(){},\n once: function(){},\n off: function(){}\n }\n }\n\n true\n })()\n ");
677
1101
  };
678
1102
  return WIO;
679
1103
  }());
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "webview.io",
3
- "version": "1.0.6",
3
+ "version": "1.2.0",
4
4
  "description": "Easy and friendly API to connect and interact between React Native and WebView with enhanced security, reliability, and modern async/await support.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "private": false,
8
8
  "scripts": {
9
9
  "compile": "rimraf ./dist && tsc",
10
- "test": "yarn run compile && yarn run test:types && yarn run test:unit",
11
- "test:types": "tsd",
12
- "test:unit": "nyc mocha --require ts-node/register --reporter spec --slow 200 --bail --timeout 10000 test/webview.io.ts",
13
- "prepack": "yarn run compile"
10
+ "test": "npm run compile && node --test \"test/*.test.mjs\"",
11
+ "prepack": "npm run compile"
14
12
  },
15
13
  "dependencies": {
16
14
  "events": "^3.3.0"