@kookapp/web-bridge 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -9,7 +9,8 @@ A PostMessage-based bridge for iframe communication, providing symmetric callHan
9
9
  - **Origin Validation**: Strict domain whitelist verification
10
10
  - **Type-Safe**: Full TypeScript support with comprehensive type definitions
11
11
  - **Promise & Callback**: Supports both async/await and callback patterns
12
- - **Error Handling**: Comprehensive error codes and timeout management
12
+ - **No Exceptions**: Errors returned via result values, never throws exceptions (like globalBridge)
13
+ - **Error Handling**: Comprehensive error reporting through return values
13
14
 
14
15
  ## Installation
15
16
 
@@ -39,10 +40,22 @@ bridge.registerHandler('getUserInfo', (data, callback) => {
39
40
  const iframeElement = document.getElementById('myIframe') as HTMLIFrameElement
40
41
  const channel = bridge.createChannel(iframeElement)
41
42
 
42
- // Call a handler inside iframe
43
- channel.callHandler('getIframeData', { type: 'user' })
44
- .then(result => console.log('Result:', result))
45
- .catch(error => console.error('Error:', error))
43
+ // Call a handler inside iframe (using callback)
44
+ channel.callHandler('getIframeData', { type: 'user' }, (result) => {
45
+ if (result.error) {
46
+ console.error('Error:', result.error)
47
+ } else {
48
+ console.log('Result:', result)
49
+ }
50
+ })
51
+
52
+ // Or using async/await (without callback)
53
+ const result = await channel.callHandler('getIframeData', { type: 'user' })
54
+ if (result.error) {
55
+ console.error('Error:', result.error)
56
+ } else {
57
+ console.log('Result:', result)
58
+ }
46
59
 
47
60
  // Clean up
48
61
  // bridge.destroy()
@@ -64,10 +77,22 @@ bridge.registerHandler('getIframeData', (data, callback) => {
64
77
  callback(response)
65
78
  })
66
79
 
67
- // Call a handler on parent
68
- bridge.callHandler('getUserInfo', {})
69
- .then(userInfo => console.log('User:', userInfo))
70
- .catch(error => console.error('Error:', error))
80
+ // Call a handler on parent (using callback)
81
+ bridge.callHandler('getUserInfo', {}, (userInfo) => {
82
+ if (userInfo.error) {
83
+ console.error('Error:', userInfo.error)
84
+ } else {
85
+ console.log('User:', userInfo)
86
+ }
87
+ })
88
+
89
+ // Or using async/await
90
+ const userInfo = await bridge.callHandler('getUserInfo', {})
91
+ if (userInfo.error) {
92
+ console.error('Error:', userInfo.error)
93
+ } else {
94
+ console.log('User:', userInfo)
95
+ }
71
96
  ```
72
97
 
73
98
  ## API Reference
@@ -121,13 +146,40 @@ interface BridgeConfig {
121
146
 
122
147
  ## Error Handling
123
148
 
124
- The bridge throws `BridgeError` with specific error codes:
149
+ **WebBridge never throws exceptions** - all errors are returned through the result value, keeping behavior consistent with globalBridge.
150
+
151
+ All errors are returned as objects with an `error` property:
152
+
153
+ ```typescript
154
+ // Using callback
155
+ bridge.callHandler('someHandler', {}, (result) => {
156
+ if (result.error) {
157
+ // Handle error cases:
158
+ // - Handler not found
159
+ // - Request timeout
160
+ // - Invalid message
161
+ // - Connection issues
162
+ console.error('Error:', result.error)
163
+ } else {
164
+ console.log('Success:', result)
165
+ }
166
+ })
167
+
168
+ // Using Promise (never rejects!)
169
+ const result = await bridge.callHandler('someHandler', {})
170
+ if (result.error) {
171
+ console.error('Error:', result.error)
172
+ } else {
173
+ console.log('Success:', result)
174
+ }
175
+ ```
176
+
177
+ **Common error types:**
178
+ - Handler not found: `{ error: 'Handler "handlerName" not found' }`
179
+ - Timeout: `{ error: 'Timeout: no response after 5000ms' }`
180
+ - Connection issues: `{ error: 'iframe contentWindow is not available' }`
125
181
 
126
- - `HANDLER_NOT_FOUND`: Handler not found on the other side
127
- - `ORIGIN_NOT_ALLOWED`: Message origin not in whitelist
128
- - `TIMEOUT`: Request exceeded timeout
129
- - `INVALID_MESSAGE`: Malformed message received
130
- - `CONNECTION_LOST`: Connection to other side lost
182
+ See [ERROR_HANDLING.md](./ERROR_HANDLING.md) for complete documentation.
131
183
 
132
184
  ## Security
133
185
 
@@ -18,6 +18,8 @@ export declare class ChildBridge extends MessageHandler {
18
18
  private notifyReady;
19
19
  /**
20
20
  * Call a handler on parent
21
+ * If callback is provided: calls callback with result (data or error object), returns Promise<undefined>
22
+ * If callback is not provided: returns Promise<result|error>
21
23
  */
22
24
  callHandler(handlerName: string, data?: any, callback?: (result: any) => void): Promise<any>;
23
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ChildBridge.d.ts","sourceRoot":"","sources":["../../src/child/ChildBridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAGxD,qBAAa,WAAY,SAAQ,cAAc;IAC7C,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,SAAS,CAAiB;gBAEtB,MAAM,EAAE,iBAAiB;IA+BrC;;OAEG;IACH,OAAO,CAAC,eAAe;IAyBvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;OAEG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,EACV,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAC/B,OAAO,CAAC,GAAG,CAAC;IAUf;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAezE;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,OAAO,IAAI,IAAI;CAQhB"}
1
+ {"version":3,"file":"ChildBridge.d.ts","sourceRoot":"","sources":["../../src/child/ChildBridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAGxD,qBAAa,WAAY,SAAQ,cAAc;IAC7C,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,SAAS,CAAiB;gBAEtB,MAAM,EAAE,iBAAiB;IA+BrC;;OAEG;IACH,OAAO,CAAC,eAAe;IAyBvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;;;OAIG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,EACV,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAC/B,OAAO,CAAC,GAAG,CAAC;IAaf;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAezE;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,OAAO,IAAI,IAAI;CAQhB"}
@@ -74,12 +74,17 @@ export class ChildBridge extends MessageHandler {
74
74
  }
75
75
  /**
76
76
  * Call a handler on parent
77
+ * If callback is provided: calls callback with result (data or error object), returns Promise<undefined>
78
+ * If callback is not provided: returns Promise<result|error>
77
79
  */
78
80
  async callHandler(handlerName, data, callback) {
79
81
  const promise = this.callHandlerWithTimeout(this.parentOrigin, handlerName, data);
80
82
  if (callback) {
81
- promise.then(callback).catch(error => console.error(error));
83
+ // With callback: pass result to callback, return undefined
84
+ promise.then(callback);
85
+ return undefined;
82
86
  }
87
+ // Without callback: return the promise with result or error object
83
88
  return promise;
84
89
  }
85
90
  /**
@@ -14,6 +14,8 @@ export declare class IframeChannel extends MessageHandler implements IframeChann
14
14
  private extractOrigin;
15
15
  /**
16
16
  * Call a handler in the iframe
17
+ * If callback is provided: calls callback with result (data or error object), returns Promise<undefined>
18
+ * If callback is not provided: returns Promise<result|error>
17
19
  */
18
20
  callHandler(handlerName: string, data?: any, callback?: (result: any) => void): Promise<any>;
19
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"IframeChannel.d.ts","sourceRoot":"","sources":["../../src/parent/IframeChannel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAExD,qBAAa,aAAc,SAAQ,cAAe,YAAW,sBAAsB;IACjF,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,SAAS,CAAiB;gBAGhC,MAAM,EAAE,iBAAiB,EACzB,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,GAAE,MAAa,EACtB,KAAK,GAAE,OAAe;IAmBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,EACV,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAC/B,OAAO,CAAC,GAAG,CAAC;IAcf;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAkBzE;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACH,SAAS,IAAI,iBAAiB;IAI9B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAItC;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
1
+ {"version":3,"file":"IframeChannel.d.ts","sourceRoot":"","sources":["../../src/parent/IframeChannel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAExD,qBAAa,aAAc,SAAQ,cAAe,YAAW,sBAAsB;IACjF,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,SAAS,CAAiB;gBAGhC,MAAM,EAAE,iBAAiB,EACzB,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,GAAE,MAAa,EACtB,KAAK,GAAE,OAAe;IAmBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;;;OAIG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,EACV,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAC/B,OAAO,CAAC,GAAG,CAAC;IAsBf;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAkBzE;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC;;OAEG;IACH,SAAS,IAAI,iBAAiB;IAI9B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAItC;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
@@ -34,15 +34,25 @@ export class IframeChannel extends MessageHandler {
34
34
  }
35
35
  /**
36
36
  * Call a handler in the iframe
37
+ * If callback is provided: calls callback with result (data or error object), returns Promise<undefined>
38
+ * If callback is not provided: returns Promise<result|error>
37
39
  */
38
40
  async callHandler(handlerName, data, callback) {
39
41
  if (!this.iframe.contentWindow) {
40
- throw new Error('iframe contentWindow is not available');
42
+ const error = { error: 'iframe contentWindow is not available' };
43
+ if (callback) {
44
+ callback(error);
45
+ return undefined;
46
+ }
47
+ return error;
41
48
  }
42
49
  const promise = this.callHandlerWithTimeout(this.iframeOrigin, handlerName, data);
43
50
  if (callback) {
44
- promise.then(callback).catch(error => console.error(error));
51
+ // With callback: pass result to callback, return undefined
52
+ promise.then(callback);
53
+ return undefined;
45
54
  }
55
+ // Without callback: return the promise with result or error object
46
56
  return promise;
47
57
  }
48
58
  /**
@@ -25,6 +25,7 @@ export declare abstract class MessageHandler {
25
25
  unregisterHandler(handlerName: string): void;
26
26
  /**
27
27
  * Call a handler on the other side with timeout
28
+ * Never rejects - errors are resolved as {error: string}
28
29
  */
29
30
  protected callHandlerWithTimeout(targetOrigin: string, handlerName: string, data?: any): Promise<any>;
30
31
  /**
@@ -45,6 +46,7 @@ export declare abstract class MessageHandler {
45
46
  private handleCall;
46
47
  /**
47
48
  * Handle incoming response message
49
+ * Never rejects - always resolves with data or error object
48
50
  */
49
51
  private handleResponse;
50
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/shared/MessageHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAIlD,8BAAsB,cAAc;IAClC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAY;IAC/C,SAAS,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAY;IAClE,SAAS,CAAC,YAAY,EAAE,YAAY,CAAqB;IACzD,SAAS,CAAC,eAAe,EAAE,eAAe,CAAA;IAC1C,SAAS,CAAC,WAAW,EAAE,WAAW,CAAoB;IACtD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,KAAK,EAAE,OAAO,CAAA;IACxB,SAAS,CAAC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;gBAE5D,cAAc,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAwB,EAAE,KAAK,GAAE,OAAe;IAM/F;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAO9F;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAO5C;;OAEG;IACH,SAAS,CAAC,sBAAsB,CAC9B,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,GACT,OAAO,CAAC,GAAG,CAAC;IA2Bf;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAElF;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,MAAM;IAE5C;;OAEG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgCrE;;OAEG;YACW,UAAU;IAyCxB;;OAEG;IACH,OAAO,CAAC,cAAc;IAqBtB;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAK5E;;OAEG;IACH,SAAS,CAAC,qBAAqB,IAAI,IAAI;IAOvC;;OAEG;IACH,QAAQ,CAAC,WAAW,IAAI,OAAO;IAE/B;;OAEG;IACH,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIhF;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIjF;;OAEG;IACH,OAAO,IAAI,IAAI;CAOhB"}
1
+ {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/shared/MessageHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAGlD,8BAAsB,cAAc;IAClC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAY;IAC/C,SAAS,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAY;IAClE,SAAS,CAAC,YAAY,EAAE,YAAY,CAAqB;IACzD,SAAS,CAAC,eAAe,EAAE,eAAe,CAAA;IAC1C,SAAS,CAAC,WAAW,EAAE,WAAW,CAAoB;IACtD,SAAS,CAAC,OAAO,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,KAAK,EAAE,OAAO,CAAA;IACxB,SAAS,CAAC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;gBAE5D,cAAc,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAwB,EAAE,KAAK,GAAE,OAAe;IAM/F;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI;IAO9F;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAO5C;;;OAGG;IACH,SAAS,CAAC,sBAAsB,CAC9B,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,GAAG,GACT,OAAO,CAAC,GAAG,CAAC;IAsCf;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAElF;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,MAAM;IAE5C;;OAEG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgCrE;;OAEG;YACW,UAAU;IAgDxB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAK5E;;OAEG;IACH,SAAS,CAAC,qBAAqB,IAAI,IAAI;IAOvC;;OAEG;IACH,QAAQ,CAAC,WAAW,IAAI,OAAO;IAE/B;;OAEG;IACH,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIhF;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAIjF;;OAEG;IACH,OAAO,IAAI,IAAI;CAOhB"}
@@ -4,7 +4,6 @@
4
4
  import { EventEmitter } from '../shared/EventEmitter';
5
5
  import { OriginValidator } from '../shared/OriginValidator';
6
6
  import { IdGenerator } from '../shared/IdGenerator';
7
- import { errorFactory } from '../errors/errorFactory';
8
7
  import { DEFAULT_TIMEOUT } from '../constants';
9
8
  export class MessageHandler {
10
9
  constructor(allowedOrigins, timeout = DEFAULT_TIMEOUT, debug = false) {
@@ -37,19 +36,32 @@ export class MessageHandler {
37
36
  }
38
37
  /**
39
38
  * Call a handler on the other side with timeout
39
+ * Never rejects - errors are resolved as {error: string}
40
40
  */
41
41
  callHandlerWithTimeout(targetOrigin, handlerName, data) {
42
- return new Promise((resolve, reject) => {
42
+ return new Promise((resolve) => {
43
43
  const messageId = this.idGenerator.generate();
44
44
  const timeoutHandle = setTimeout(() => {
45
45
  this.pendingRequests.delete(messageId);
46
- reject(errorFactory.timeout(this.timeout));
46
+ resolve({ error: `Timeout: no response after ${this.timeout}ms` });
47
47
  }, this.timeout);
48
48
  this.pendingRequests.set(messageId, {
49
49
  id: messageId,
50
50
  timeout: timeoutHandle,
51
- resolve,
52
- reject
51
+ resolve: (data) => {
52
+ // Wrap successful responses
53
+ if (data !== undefined && !data.error) {
54
+ resolve(data);
55
+ }
56
+ else {
57
+ resolve(data);
58
+ }
59
+ },
60
+ reject: (error) => {
61
+ // Never reject - resolve errors instead
62
+ const errorMessage = error instanceof Error ? error.message : String(error);
63
+ resolve({ error: errorMessage });
64
+ }
53
65
  });
54
66
  const message = {
55
67
  type: 'call',
@@ -109,16 +121,20 @@ export class MessageHandler {
109
121
  return;
110
122
  }
111
123
  try {
112
- // Execute handler
124
+ let callbackCalled = false;
125
+ // Execute handler with callback function
113
126
  const result = await Promise.resolve(handler(data, (response) => {
114
- this.sendMessage(origin, {
115
- type: 'response',
116
- id,
117
- data: response
118
- });
127
+ if (!callbackCalled) {
128
+ callbackCalled = true;
129
+ this.sendMessage(origin, {
130
+ type: 'response',
131
+ id,
132
+ data: response
133
+ });
134
+ }
119
135
  }));
120
- // If handler returns a value directly (not using callback)
121
- if (result !== undefined) {
136
+ // If handler returns a value and callback wasn't called, send the return value
137
+ if (!callbackCalled && result !== undefined) {
122
138
  this.sendMessage(origin, {
123
139
  type: 'response',
124
140
  id,
@@ -137,6 +153,7 @@ export class MessageHandler {
137
153
  }
138
154
  /**
139
155
  * Handle incoming response message
156
+ * Never rejects - always resolves with data or error object
140
157
  */
141
158
  handleResponse(message) {
142
159
  const { id, data, error } = message;
@@ -150,7 +167,8 @@ export class MessageHandler {
150
167
  clearTimeout(pending.timeout);
151
168
  this.pendingRequests.delete(id);
152
169
  if (error) {
153
- pending.reject(new Error(error));
170
+ // Resolve with error object instead of rejecting
171
+ pending.resolve({ error });
154
172
  }
155
173
  else {
156
174
  pending.resolve(data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kookapp/web-bridge",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "PostMessage-based bridge for iframe communication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",