@aspectly/core 0.1.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.mjs ADDED
@@ -0,0 +1,412 @@
1
+ // src/BridgeCore.ts
2
+ var _BridgeCore = class _BridgeCore {
3
+ };
4
+ _BridgeCore.BRIDGE_EVENT_TYPE = "BridgeEvent";
5
+ _BridgeCore.isJSONObject = (str) => {
6
+ return str.startsWith("{") && str.endsWith("}");
7
+ };
8
+ /**
9
+ * Wraps an event in the bridge protocol format
10
+ */
11
+ _BridgeCore.wrapBridgeEvent = (event) => {
12
+ return JSON.stringify({
13
+ event,
14
+ type: _BridgeCore.BRIDGE_EVENT_TYPE
15
+ });
16
+ };
17
+ /**
18
+ * Creates a listener wrapper that parses incoming messages
19
+ */
20
+ _BridgeCore.wrapListener = (listener) => (data) => {
21
+ if (typeof data !== "string") {
22
+ return;
23
+ }
24
+ if (!data) {
25
+ return;
26
+ }
27
+ let processedData = data;
28
+ if (processedData.startsWith("'") && processedData.endsWith("'")) {
29
+ processedData = processedData.substring(1, processedData.length - 1);
30
+ }
31
+ if (!_BridgeCore.isJSONObject(processedData)) {
32
+ return;
33
+ }
34
+ try {
35
+ const eventData = JSON.parse(processedData);
36
+ if (!eventData || eventData.type !== _BridgeCore.BRIDGE_EVENT_TYPE) {
37
+ return;
38
+ }
39
+ listener(eventData.event);
40
+ } catch {
41
+ }
42
+ };
43
+ /**
44
+ * Creates a browser-specific message event listener
45
+ */
46
+ _BridgeCore.browserListener = (listener) => {
47
+ const triggerEvent = _BridgeCore.wrapListener(listener);
48
+ return (originalEvent) => {
49
+ if (!originalEvent?.data) {
50
+ return;
51
+ }
52
+ triggerEvent(originalEvent.data);
53
+ };
54
+ };
55
+ /**
56
+ * Creates a React Native WebView message listener
57
+ */
58
+ _BridgeCore.webViewListener = (listener) => {
59
+ const triggerEvent = _BridgeCore.wrapListener(listener);
60
+ return (originalEvent) => {
61
+ if (!originalEvent?.nativeEvent?.data) {
62
+ return;
63
+ }
64
+ triggerEvent(originalEvent.nativeEvent.data);
65
+ };
66
+ };
67
+ /**
68
+ * Sends an event to the parent context (WebView or iframe parent)
69
+ */
70
+ _BridgeCore.sendEvent = (event) => {
71
+ const bridgeEvent = _BridgeCore.wrapBridgeEvent(event);
72
+ if (typeof window === "undefined") {
73
+ console.warn("Window is undefined");
74
+ return;
75
+ }
76
+ const RNW = window.ReactNativeWebView;
77
+ if (typeof RNW?.postMessage === "function") {
78
+ RNW.postMessage(`'${bridgeEvent}'`);
79
+ return;
80
+ }
81
+ if (window.parent === window) {
82
+ return;
83
+ }
84
+ window.parent.postMessage(bridgeEvent, "*");
85
+ };
86
+ /**
87
+ * Subscribes to window message events
88
+ * @returns Cleanup function to unsubscribe
89
+ */
90
+ _BridgeCore.subscribe = (listener) => {
91
+ const browserListener = _BridgeCore.browserListener(listener);
92
+ if (typeof window === "undefined" || !window.addEventListener) {
93
+ return () => {
94
+ };
95
+ }
96
+ window.addEventListener("message", browserListener);
97
+ return () => window.removeEventListener("message", browserListener);
98
+ };
99
+ var BridgeCore = _BridgeCore;
100
+
101
+ // src/types.ts
102
+ var BridgeEventType = /* @__PURE__ */ ((BridgeEventType2) => {
103
+ BridgeEventType2["Request"] = "Request";
104
+ BridgeEventType2["Result"] = "Result";
105
+ BridgeEventType2["Init"] = "Init";
106
+ BridgeEventType2["InitResult"] = "InitResult";
107
+ return BridgeEventType2;
108
+ })(BridgeEventType || {});
109
+ var BridgeResultType = /* @__PURE__ */ ((BridgeResultType2) => {
110
+ BridgeResultType2["Success"] = "Success";
111
+ BridgeResultType2["Error"] = "Error";
112
+ return BridgeResultType2;
113
+ })(BridgeResultType || {});
114
+ var BridgeErrorType = /* @__PURE__ */ ((BridgeErrorType2) => {
115
+ BridgeErrorType2["METHOD_EXECUTION_TIMEOUT"] = "METHOD_EXECUTION_TIMEOUT";
116
+ BridgeErrorType2["UNSUPPORTED_METHOD"] = "UNSUPPORTED_METHOD";
117
+ BridgeErrorType2["REJECTED"] = "REJECTED";
118
+ BridgeErrorType2["BRIDGE_NOT_AVAILABLE"] = "BRIDGE_NOT_AVAILABLE";
119
+ return BridgeErrorType2;
120
+ })(BridgeErrorType || {});
121
+
122
+ // src/BridgeInternal.ts
123
+ var internalEvent = (type, data) => ({
124
+ type,
125
+ data
126
+ });
127
+ var internalResultEvent = (data) => internalEvent("Result" /* Result */, data);
128
+ var DEFAULT_TIMEOUT = 1e5;
129
+ var BridgeInternal = class {
130
+ constructor(sendEvent, options) {
131
+ this.requests = [];
132
+ this.handlers = {};
133
+ this.available = false;
134
+ this.supportedMethods = [];
135
+ this.listeners = [];
136
+ /**
137
+ * Subscribe to all result events
138
+ */
139
+ this.subscribe = (listener) => {
140
+ return this.listeners.push(listener);
141
+ };
142
+ /**
143
+ * Unsubscribe from result events
144
+ */
145
+ this.unsubscribe = (listener) => {
146
+ this.listeners = this.listeners.filter(
147
+ (oldListener) => oldListener !== listener
148
+ );
149
+ };
150
+ this.checkDiff = (a, b) => {
151
+ return a.filter((x) => !b.includes(x)).length > 0 || b.filter((x) => !a.includes(x)).length > 0;
152
+ };
153
+ /**
154
+ * Initialize the bridge with handlers
155
+ * @param handlers Map of method names to handler functions
156
+ * @returns Promise that resolves when the other side acknowledges
157
+ */
158
+ this.init = (handlers = {}) => {
159
+ const oldMethods = Object.keys(this.handlers);
160
+ const newMethods = Object.keys(handlers);
161
+ this.handlers = handlers;
162
+ if (!this.checkDiff(oldMethods, newMethods)) {
163
+ return Promise.resolve(true);
164
+ }
165
+ return new Promise((resolve, reject) => {
166
+ this.initPromise = { resolve, reject };
167
+ this.sendEvent(
168
+ internalEvent("Init" /* Init */, {
169
+ methods: newMethods
170
+ })
171
+ );
172
+ });
173
+ };
174
+ /**
175
+ * Handle incoming bridge events
176
+ */
177
+ this.handleCoreEvent = (event) => {
178
+ const { type, data } = event;
179
+ switch (type) {
180
+ case "Init" /* Init */:
181
+ this.handleInit(data);
182
+ break;
183
+ case "InitResult" /* InitResult */:
184
+ this.handleInitResult(data);
185
+ break;
186
+ case "Request" /* Request */:
187
+ this.handleRequest(data);
188
+ break;
189
+ case "Result" /* Result */:
190
+ this.handleResult(data);
191
+ break;
192
+ }
193
+ };
194
+ /**
195
+ * Handle incoming requests and execute the appropriate handler
196
+ */
197
+ this.handleRequest = (request) => {
198
+ const { method, params, request_id } = request;
199
+ new Promise((resolve, reject) => {
200
+ let timeout = false;
201
+ if (!Object.prototype.hasOwnProperty.call(this.handlers, method)) {
202
+ reject({
203
+ error_type: "UNSUPPORTED_METHOD" /* UNSUPPORTED_METHOD */,
204
+ error: new Error(`Handler for \xAB${method}\xBB is not registered`)
205
+ });
206
+ return;
207
+ }
208
+ const timer = setTimeout(() => {
209
+ timeout = true;
210
+ reject({
211
+ error_type: "METHOD_EXECUTION_TIMEOUT" /* METHOD_EXECUTION_TIMEOUT */,
212
+ error: new Error("Execution timeout exceeded")
213
+ });
214
+ }, this.timeout);
215
+ const handler = this.handlers[method];
216
+ if (!handler) {
217
+ reject({
218
+ error_type: "UNSUPPORTED_METHOD" /* UNSUPPORTED_METHOD */,
219
+ error: new Error(`Handler for \xAB${method}\xBB is undefined`)
220
+ });
221
+ return;
222
+ }
223
+ handler(params).then((result) => {
224
+ if (timeout) {
225
+ return;
226
+ }
227
+ clearTimeout(timer);
228
+ resolve(result);
229
+ }).catch((error) => {
230
+ if (timeout) {
231
+ return;
232
+ }
233
+ clearTimeout(timer);
234
+ reject({
235
+ error_type: "REJECTED" /* REJECTED */,
236
+ error
237
+ });
238
+ });
239
+ }).then((data) => {
240
+ this.sendEvent(
241
+ internalResultEvent({
242
+ type: "Success" /* Success */,
243
+ data,
244
+ method,
245
+ request_id
246
+ })
247
+ );
248
+ }).catch(
249
+ ({
250
+ error_type,
251
+ error
252
+ }) => {
253
+ this.sendEvent(
254
+ internalResultEvent({
255
+ type: "Error" /* Error */,
256
+ request_id,
257
+ method,
258
+ data: {
259
+ error_message: error.message,
260
+ error_type
261
+ }
262
+ })
263
+ );
264
+ }
265
+ );
266
+ };
267
+ this.handleResult = (result) => {
268
+ this.handleRequestResult(result);
269
+ this.listeners.forEach((listener) => listener(result));
270
+ };
271
+ this.handleRequestResult = (result) => {
272
+ if (!result || !Object.prototype.hasOwnProperty.call(result, "request_id")) {
273
+ return;
274
+ }
275
+ if (!Object.prototype.hasOwnProperty.call(result, "type")) {
276
+ console.warn("unknown result", result);
277
+ return;
278
+ }
279
+ const { request_id, data, type } = result;
280
+ const request = this.requests[Number(request_id)];
281
+ if (!request) {
282
+ return;
283
+ }
284
+ if (type === "Success" /* Success */) {
285
+ request.resolve(data);
286
+ return;
287
+ }
288
+ if (type === "Error" /* Error */) {
289
+ request.reject(data);
290
+ }
291
+ };
292
+ this.handleInit = (data) => {
293
+ this.available = true;
294
+ this.supportedMethods = data.methods;
295
+ this.sendEvent(internalEvent("InitResult" /* InitResult */, true));
296
+ };
297
+ this.handleInitResult = (success) => {
298
+ if (success) {
299
+ this.initPromise?.resolve(true);
300
+ } else {
301
+ this.initPromise?.reject();
302
+ }
303
+ };
304
+ /**
305
+ * Send a request to the other side
306
+ * @param method Method name to invoke
307
+ * @param params Parameters to pass to the method
308
+ * @returns Promise that resolves with the result
309
+ */
310
+ this.send = (method, params) => new Promise((resolve, reject) => {
311
+ const request_id = (this.requests.push({ resolve, reject }) - 1).toString();
312
+ if (!this.isAvailable()) {
313
+ this.handleCoreEvent(
314
+ internalResultEvent({
315
+ type: "Error" /* Error */,
316
+ request_id,
317
+ method,
318
+ data: {
319
+ error_message: "Bridge is not available",
320
+ error_type: "BRIDGE_NOT_AVAILABLE" /* BRIDGE_NOT_AVAILABLE */
321
+ }
322
+ })
323
+ );
324
+ return;
325
+ }
326
+ this.sendEvent(
327
+ internalEvent("Request" /* Request */, {
328
+ method,
329
+ params,
330
+ request_id
331
+ })
332
+ );
333
+ });
334
+ /**
335
+ * Check if a method is supported by the other side
336
+ */
337
+ this.supports = (method) => this.supportedMethods.includes(method);
338
+ /**
339
+ * Check if the bridge is available (initialized)
340
+ */
341
+ this.isAvailable = () => this.available;
342
+ this.sendEvent = sendEvent;
343
+ this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
344
+ }
345
+ };
346
+
347
+ // src/BridgeBase.ts
348
+ var BridgeBase = class {
349
+ constructor(bridge) {
350
+ /**
351
+ * Check if a method is supported by the other side
352
+ * @param method Method name to check
353
+ */
354
+ this.supports = (method) => this.bridge.supports(method);
355
+ /**
356
+ * Check if the bridge is available (initialized)
357
+ */
358
+ this.isAvailable = () => this.bridge.isAvailable();
359
+ /**
360
+ * Send a request to invoke a method on the other side
361
+ * @param method Method name to invoke
362
+ * @param params Parameters to pass
363
+ * @returns Promise resolving with the result
364
+ */
365
+ this.send = (method, params = {}) => {
366
+ return this.bridge.send(method, params);
367
+ };
368
+ /**
369
+ * Subscribe to all result events
370
+ * @param listener Callback for result events
371
+ * @returns Subscription index
372
+ */
373
+ this.subscribe = (listener) => {
374
+ return this.bridge.subscribe(listener);
375
+ };
376
+ /**
377
+ * Unsubscribe from result events
378
+ * @param listener The listener to remove
379
+ */
380
+ this.unsubscribe = (listener) => {
381
+ return this.bridge.unsubscribe(listener);
382
+ };
383
+ /**
384
+ * Initialize the bridge with handlers
385
+ * @param handlers Map of method names to handler functions
386
+ * @returns Promise resolving when initialization is complete
387
+ */
388
+ this.init = (handlers) => this.bridge.init(handlers);
389
+ this.bridge = bridge;
390
+ }
391
+ };
392
+
393
+ // src/AspectlyBridge.ts
394
+ var AspectlyBridge = class extends BridgeBase {
395
+ constructor(options) {
396
+ const bridge = new BridgeInternal(BridgeCore.sendEvent, options);
397
+ super(bridge);
398
+ /**
399
+ * Cleanup bridge subscriptions
400
+ */
401
+ this.destroy = () => {
402
+ this.cleanupSubscription();
403
+ };
404
+ this.cleanupSubscription = BridgeCore.subscribe(
405
+ bridge.handleCoreEvent
406
+ );
407
+ }
408
+ };
409
+
410
+ export { AspectlyBridge, BridgeBase, BridgeCore, BridgeErrorType, BridgeEventType, BridgeInternal, BridgeResultType };
411
+ //# sourceMappingURL=index.mjs.map
412
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/BridgeCore.ts","../src/types.ts","../src/BridgeInternal.ts","../src/BridgeBase.ts","../src/AspectlyBridge.ts"],"names":["BridgeEventType","BridgeResultType","BridgeErrorType"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Low-level bridge core handling platform-specific message passing\n */\n\nexport type Event = unknown;\n\ninterface BridgeCoreEvent {\n type: string;\n event: Event;\n}\n\ninterface WebViewMessage {\n nativeEvent?: {\n data?: string;\n };\n}\n\nexport type BridgeCoreListener = (event: Event) => void;\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: {\n postMessage: (message: string) => void;\n };\n }\n}\n\n/**\n * BridgeCore handles the low-level platform detection and message serialization.\n * It provides static methods for wrapping events, creating listeners, and sending messages.\n */\nexport class BridgeCore {\n private static BRIDGE_EVENT_TYPE = 'BridgeEvent';\n\n private static isJSONObject = (str: string): boolean => {\n return str.startsWith('{') && str.endsWith('}');\n };\n\n /**\n * Wraps an event in the bridge protocol format\n */\n public static wrapBridgeEvent = (event: Event): string => {\n return JSON.stringify({\n event,\n type: BridgeCore.BRIDGE_EVENT_TYPE,\n });\n };\n\n /**\n * Creates a listener wrapper that parses incoming messages\n */\n static wrapListener =\n (listener: BridgeCoreListener) =>\n (data?: string): void => {\n if (typeof data !== 'string') {\n return;\n }\n if (!data) {\n return;\n }\n let processedData = data;\n // iOS wraps JSON with additional quotes\n if (processedData.startsWith(\"'\") && processedData.endsWith(\"'\")) {\n processedData = processedData.substring(1, processedData.length - 1);\n }\n if (!BridgeCore.isJSONObject(processedData)) {\n return;\n }\n try {\n const eventData: BridgeCoreEvent = JSON.parse(processedData);\n if (!eventData || eventData.type !== BridgeCore.BRIDGE_EVENT_TYPE) {\n return;\n }\n listener(eventData.event);\n } catch {\n // Ignore parse errors\n }\n };\n\n /**\n * Creates a browser-specific message event listener\n */\n static browserListener = (listener: BridgeCoreListener) => {\n const triggerEvent = BridgeCore.wrapListener(listener);\n return (originalEvent: MessageEvent): void => {\n if (!originalEvent?.data) {\n return;\n }\n triggerEvent(originalEvent.data);\n };\n };\n\n /**\n * Creates a React Native WebView message listener\n */\n static webViewListener = (listener: BridgeCoreListener) => {\n const triggerEvent = BridgeCore.wrapListener(listener);\n return (originalEvent: WebViewMessage): void => {\n if (!originalEvent?.nativeEvent?.data) {\n return;\n }\n triggerEvent(originalEvent.nativeEvent.data);\n };\n };\n\n /**\n * Sends an event to the parent context (WebView or iframe parent)\n */\n static sendEvent = (event: Event): void => {\n const bridgeEvent = BridgeCore.wrapBridgeEvent(event);\n if (typeof window === 'undefined') {\n console.warn('Window is undefined');\n return;\n }\n const RNW = window.ReactNativeWebView;\n if (typeof RNW?.postMessage === 'function') {\n RNW.postMessage(`'${bridgeEvent}'`);\n return;\n }\n if (window.parent === window) {\n return;\n }\n window.parent.postMessage(bridgeEvent, '*');\n };\n\n /**\n * Subscribes to window message events\n * @returns Cleanup function to unsubscribe\n */\n static subscribe = (listener: BridgeCoreListener): VoidFunction => {\n const browserListener = BridgeCore.browserListener(listener);\n if (typeof window === 'undefined' || !window.addEventListener) {\n return () => {};\n }\n window.addEventListener('message', browserListener);\n return () => window.removeEventListener('message', browserListener);\n };\n}\n","/**\n * Event types used in bridge communication protocol\n */\nexport enum BridgeEventType {\n /** Request to invoke a method on the other side */\n Request = 'Request',\n /** Response to a request */\n Result = 'Result',\n /** Initialization handshake */\n Init = 'Init',\n /** Response to initialization */\n InitResult = 'InitResult',\n}\n\n/**\n * Result types for bridge responses\n */\nexport enum BridgeResultType {\n /** Successful response */\n Success = 'Success',\n /** Error response */\n Error = 'Error',\n}\n\n/**\n * Error types that can occur during bridge communication\n */\nexport enum BridgeErrorType {\n /** Handler took longer than timeout (default: 100s) */\n METHOD_EXECUTION_TIMEOUT = 'METHOD_EXECUTION_TIMEOUT',\n /** Method is not registered on the receiving side */\n UNSUPPORTED_METHOD = 'UNSUPPORTED_METHOD',\n /** Handler threw an error */\n REJECTED = 'REJECTED',\n /** Bridge is not initialized or unavailable */\n BRIDGE_NOT_AVAILABLE = 'BRIDGE_NOT_AVAILABLE',\n}\n\n/**\n * Error data returned when a bridge call fails\n */\nexport interface BridgeResultError {\n error_type: BridgeErrorType;\n error_message?: string;\n}\n\n/**\n * Successful result data (any object)\n */\nexport type BridgeResultSuccess = object;\n\n/**\n * Union type for result data\n */\nexport type BridgeResultData =\n | BridgeResultError\n | undefined\n | BridgeResultSuccess;\n\n/**\n * Result event sent back after processing a request\n */\nexport interface BridgeResultEvent {\n type?: BridgeResultType;\n method?: string;\n request_id?: string;\n data?: BridgeResultData;\n}\n\n/**\n * Request event sent to invoke a method\n */\nexport interface BridgeRequestEvent {\n method: string;\n params: object;\n request_id?: string;\n}\n\n/**\n * Initialization event with available methods\n */\nexport interface BridgeInitEvent {\n methods: string[];\n}\n\n/**\n * Initialization result (success/failure)\n */\nexport type BridgeInitResultEvent = boolean;\n\n/**\n * Union of all possible bridge data payloads\n */\nexport type BridgeData =\n | BridgeResultEvent\n | BridgeInitEvent\n | BridgeInitResultEvent\n | BridgeRequestEvent;\n\n/**\n * Complete bridge event with type and data\n */\nexport interface BridgeEvent {\n type: BridgeEventType;\n data: BridgeData;\n}\n\n/**\n * Handler function for processing incoming requests\n */\nexport type BridgeHandler<TParams = object, TResult = unknown> = (\n params: TParams\n) => Promise<TResult>;\n\n/**\n * Map of method names to their handlers\n */\nexport interface BridgeHandlers {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: BridgeHandler<any, any>;\n}\n\n/**\n * Listener function for bridge result events\n */\nexport type BridgeListener = (result: BridgeResultEvent) => void;\n\n/**\n * Options for bridge initialization\n */\nexport interface BridgeOptions {\n /** Timeout for method execution in milliseconds (default: 100000) */\n timeout?: number;\n}\n","import type {\n BridgeData,\n BridgeEvent,\n BridgeHandlers,\n BridgeInitEvent,\n BridgeInitResultEvent,\n BridgeListener,\n BridgeOptions,\n BridgeRequestEvent,\n BridgeResultData,\n BridgeResultError,\n BridgeResultEvent,\n} from './types';\nimport {\n BridgeErrorType,\n BridgeEventType,\n BridgeResultType,\n} from './types';\n\n/**\n * Creates a bridge event with the specified type and data\n */\nexport const internalEvent = (\n type: BridgeEventType,\n data: BridgeData\n): BridgeEvent => ({\n type,\n data,\n});\n\n/**\n * Creates a result event\n */\nexport const internalResultEvent = (data: BridgeData): BridgeEvent =>\n internalEvent(BridgeEventType.Result, data);\n\ninterface InternalRequestPromise {\n reject: (error: BridgeResultError) => void;\n resolve: (result: BridgeResultData) => void;\n}\n\ninterface InitPromise {\n reject: () => void;\n resolve: (success: boolean) => void;\n}\n\ntype InternalEventSender = (event: BridgeEvent) => void;\n\nconst DEFAULT_TIMEOUT = 100000;\n\n/**\n * BridgeInternal handles the business logic of the bridge protocol.\n * It manages request/response lifecycle, handler registration, and event routing.\n */\nexport class BridgeInternal {\n private requests: InternalRequestPromise[] = [];\n private handlers: BridgeHandlers = {};\n private available = false;\n private supportedMethods: string[] = [];\n private listeners: BridgeListener[] = [];\n private initPromise?: InitPromise;\n private readonly sendEvent: InternalEventSender;\n private readonly timeout: number;\n\n constructor(sendEvent: InternalEventSender, options?: BridgeOptions) {\n this.sendEvent = sendEvent;\n this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;\n }\n\n /**\n * Subscribe to all result events\n */\n public subscribe = (listener: BridgeListener): number => {\n return this.listeners.push(listener);\n };\n\n /**\n * Unsubscribe from result events\n */\n public unsubscribe = (listener: BridgeListener): void => {\n this.listeners = this.listeners.filter(\n (oldListener) => oldListener !== listener\n );\n };\n\n private checkDiff = (a: string[], b: string[]): boolean => {\n return (\n a.filter((x) => !b.includes(x)).length > 0 ||\n b.filter((x) => !a.includes(x)).length > 0\n );\n };\n\n /**\n * Initialize the bridge with handlers\n * @param handlers Map of method names to handler functions\n * @returns Promise that resolves when the other side acknowledges\n */\n public init = (handlers: BridgeHandlers = {}): Promise<boolean> => {\n const oldMethods = Object.keys(this.handlers);\n const newMethods = Object.keys(handlers);\n this.handlers = handlers;\n if (!this.checkDiff(oldMethods, newMethods)) {\n return Promise.resolve(true);\n }\n return new Promise((resolve, reject) => {\n this.initPromise = { resolve, reject };\n this.sendEvent(\n internalEvent(BridgeEventType.Init, {\n methods: newMethods,\n })\n );\n });\n };\n\n /**\n * Handle incoming bridge events\n */\n public handleCoreEvent = (event: BridgeEvent): void => {\n const { type, data } = event;\n switch (type) {\n case BridgeEventType.Init:\n this.handleInit(data as BridgeInitEvent);\n break;\n case BridgeEventType.InitResult:\n this.handleInitResult(data as BridgeInitResultEvent);\n break;\n case BridgeEventType.Request:\n this.handleRequest(data as BridgeRequestEvent);\n break;\n case BridgeEventType.Result:\n this.handleResult(data as BridgeResultEvent);\n break;\n }\n };\n\n /**\n * Handle incoming requests and execute the appropriate handler\n */\n public handleRequest = (request: BridgeRequestEvent): void => {\n const { method, params, request_id } = request;\n new Promise<BridgeResultData>((resolve, reject) => {\n let timeout = false;\n if (!Object.prototype.hasOwnProperty.call(this.handlers, method)) {\n reject({\n error_type: BridgeErrorType.UNSUPPORTED_METHOD,\n error: new Error(`Handler for «${method}» is not registered`),\n });\n return;\n }\n const timer = setTimeout(() => {\n timeout = true;\n reject({\n error_type: BridgeErrorType.METHOD_EXECUTION_TIMEOUT,\n error: new Error('Execution timeout exceeded'),\n });\n }, this.timeout);\n const handler = this.handlers[method];\n if (!handler) {\n reject({\n error_type: BridgeErrorType.UNSUPPORTED_METHOD,\n error: new Error(`Handler for «${method}» is undefined`),\n });\n return;\n }\n handler(params)\n .then((result) => {\n if (timeout) {\n return;\n }\n clearTimeout(timer);\n resolve(result as BridgeResultData);\n })\n .catch((error: Error) => {\n if (timeout) {\n return;\n }\n clearTimeout(timer);\n reject({\n error_type: BridgeErrorType.REJECTED,\n error: error,\n });\n });\n })\n .then((data: BridgeResultData) => {\n this.sendEvent(\n internalResultEvent({\n type: BridgeResultType.Success,\n data,\n method,\n request_id,\n })\n );\n })\n .catch(\n ({\n error_type,\n error,\n }: {\n error_type: BridgeErrorType;\n error: Error;\n }) => {\n this.sendEvent(\n internalResultEvent({\n type: BridgeResultType.Error,\n request_id,\n method,\n data: {\n error_message: error.message,\n error_type,\n },\n })\n );\n }\n );\n };\n\n private handleResult = (result: BridgeResultEvent): void => {\n this.handleRequestResult(result);\n this.listeners.forEach((listener) => listener(result));\n };\n\n private handleRequestResult = (result: BridgeResultEvent): void => {\n if (!result || !Object.prototype.hasOwnProperty.call(result, 'request_id')) {\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(result, 'type')) {\n console.warn('unknown result', result);\n return;\n }\n const { request_id, data, type } = result;\n const request = this.requests[Number(request_id)];\n if (!request) {\n return;\n }\n if (type === BridgeResultType.Success) {\n request.resolve(data);\n return;\n }\n if (type === BridgeResultType.Error) {\n request.reject(data as BridgeResultError);\n }\n };\n\n private handleInit = (data: BridgeInitEvent): void => {\n this.available = true;\n this.supportedMethods = data.methods;\n this.sendEvent(internalEvent(BridgeEventType.InitResult, true));\n };\n\n private handleInitResult = (success: BridgeInitResultEvent): void => {\n if (success) {\n this.initPromise?.resolve(true);\n } else {\n this.initPromise?.reject();\n }\n };\n\n /**\n * Send a request to the other side\n * @param method Method name to invoke\n * @param params Parameters to pass to the method\n * @returns Promise that resolves with the result\n */\n public send = <TResult = unknown>(\n method: string,\n params: object\n ): Promise<TResult> =>\n new Promise((resolve, reject) => {\n const request_id = (\n this.requests.push({ resolve: resolve as (result: BridgeResultData) => void, reject }) - 1\n ).toString();\n if (!this.isAvailable()) {\n this.handleCoreEvent(\n internalResultEvent({\n type: BridgeResultType.Error,\n request_id,\n method,\n data: {\n error_message: 'Bridge is not available',\n error_type: BridgeErrorType.BRIDGE_NOT_AVAILABLE,\n },\n })\n );\n return;\n }\n this.sendEvent(\n internalEvent(BridgeEventType.Request, {\n method,\n params,\n request_id,\n })\n );\n });\n\n /**\n * Check if a method is supported by the other side\n */\n public supports = (method: string): boolean =>\n this.supportedMethods.includes(method);\n\n /**\n * Check if the bridge is available (initialized)\n */\n public isAvailable = (): boolean => this.available;\n}\n","import type { BridgeHandlers, BridgeListener } from './types';\nimport type { BridgeInternal } from './BridgeInternal';\n\n/**\n * BridgeBase provides the public API for bridge communication.\n * It wraps BridgeInternal and exposes a clean interface for consumers.\n */\nexport class BridgeBase {\n protected bridge: BridgeInternal;\n\n constructor(bridge: BridgeInternal) {\n this.bridge = bridge;\n }\n\n /**\n * Check if a method is supported by the other side\n * @param method Method name to check\n */\n public supports = (method: string): boolean => this.bridge.supports(method);\n\n /**\n * Check if the bridge is available (initialized)\n */\n public isAvailable = (): boolean => this.bridge.isAvailable();\n\n /**\n * Send a request to invoke a method on the other side\n * @param method Method name to invoke\n * @param params Parameters to pass\n * @returns Promise resolving with the result\n */\n public send = <TResult = unknown>(\n method: string,\n params: object = {}\n ): Promise<TResult> => {\n return this.bridge.send<TResult>(method, params);\n };\n\n /**\n * Subscribe to all result events\n * @param listener Callback for result events\n * @returns Subscription index\n */\n public subscribe = (listener: BridgeListener): number => {\n return this.bridge.subscribe(listener);\n };\n\n /**\n * Unsubscribe from result events\n * @param listener The listener to remove\n */\n public unsubscribe = (listener: BridgeListener): void => {\n return this.bridge.unsubscribe(listener);\n };\n\n /**\n * Initialize the bridge with handlers\n * @param handlers Map of method names to handler functions\n * @returns Promise resolving when initialization is complete\n */\n public init = (handlers?: BridgeHandlers): Promise<boolean> =>\n this.bridge.init(handlers);\n}\n","import { BridgeCore } from './BridgeCore';\nimport { BridgeInternal } from './BridgeInternal';\nimport { BridgeBase } from './BridgeBase';\nimport type { BridgeOptions } from './types';\n\n/**\n * AspectlyBridge is the main entry point for bridge communication.\n * Use this class when running inside a WebView or iframe that needs\n * to communicate with its parent container.\n *\n * @example\n * ```typescript\n * // Inside a WebView or iframe\n * const bridge = new AspectlyBridge();\n *\n * // Initialize with handlers\n * await bridge.init({\n * greet: async (params) => {\n * return { message: `Hello, ${params.name}!` };\n * }\n * });\n *\n * // Send messages to parent\n * const result = await bridge.send('someMethod', { data: 'value' });\n * ```\n */\nexport class AspectlyBridge extends BridgeBase {\n private cleanupSubscription: VoidFunction;\n\n constructor(options?: BridgeOptions) {\n const bridge = new BridgeInternal(BridgeCore.sendEvent, options);\n super(bridge);\n this.cleanupSubscription = BridgeCore.subscribe(\n bridge.handleCoreEvent as (event: unknown) => void\n );\n }\n\n /**\n * Cleanup bridge subscriptions\n */\n public destroy = (): void => {\n this.cleanupSubscription();\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@aspectly/core",
3
+ "version": "0.1.0",
4
+ "description": "Core bridge framework for cross-platform communication between WebViews, iframes, and web applications",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ },
19
+ "./browser": {
20
+ "import": {
21
+ "types": "./dist/browser.d.mts",
22
+ "default": "./dist/browser.mjs"
23
+ },
24
+ "require": {
25
+ "types": "./dist/browser.d.ts",
26
+ "default": "./dist/browser.js"
27
+ }
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src"
33
+ ],
34
+ "sideEffects": false,
35
+ "keywords": [
36
+ "bridge",
37
+ "communication",
38
+ "webview",
39
+ "iframe",
40
+ "postmessage",
41
+ "cross-platform",
42
+ "typescript"
43
+ ],
44
+ "author": "Zhan Isaakian <jeanisahkyan@gmail.com>",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/JeanIsahakyan/aspectly",
49
+ "directory": "packages/core"
50
+ },
51
+ "bugs": {
52
+ "url": "https://github.com/JeanIsahakyan/aspectly/issues"
53
+ },
54
+ "homepage": "https://github.com/JeanIsahakyan/aspectly/tree/main/packages/core#readme",
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "devDependencies": {
59
+ "tsup": "^8.0.1",
60
+ "typescript": "^5.3.2",
61
+ "rimraf": "^5.0.5"
62
+ },
63
+ "scripts": {
64
+ "build": "tsup",
65
+ "dev": "tsup --watch",
66
+ "typecheck": "tsc --noEmit",
67
+ "lint": "eslint src --ext .ts,.tsx",
68
+ "clean": "rimraf dist",
69
+ "test": "vitest run"
70
+ }
71
+ }
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { AspectlyBridge } from './AspectlyBridge';
3
+ import { BridgeCore } from './BridgeCore';
4
+
5
+ // Mock BridgeCore
6
+ vi.mock('./BridgeCore', () => ({
7
+ BridgeCore: {
8
+ sendEvent: vi.fn(),
9
+ subscribe: vi.fn().mockReturnValue(vi.fn()),
10
+ },
11
+ }));
12
+
13
+ describe('AspectlyBridge', () => {
14
+ beforeEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+
18
+ describe('constructor', () => {
19
+ it('should create a bridge and subscribe to events', () => {
20
+ const bridge = new AspectlyBridge();
21
+
22
+ expect(BridgeCore.subscribe).toHaveBeenCalledWith(expect.any(Function));
23
+ expect(bridge).toBeInstanceOf(AspectlyBridge);
24
+ });
25
+
26
+ it('should pass options to internal bridge', () => {
27
+ const bridge = new AspectlyBridge({ timeout: 5000 });
28
+
29
+ expect(bridge).toBeInstanceOf(AspectlyBridge);
30
+ });
31
+ });
32
+
33
+ describe('destroy', () => {
34
+ it('should call unsubscribe function', () => {
35
+ const unsubscribeMock = vi.fn();
36
+ (BridgeCore.subscribe as ReturnType<typeof vi.fn>).mockReturnValue(
37
+ unsubscribeMock
38
+ );
39
+
40
+ const bridge = new AspectlyBridge();
41
+ bridge.destroy();
42
+
43
+ expect(unsubscribeMock).toHaveBeenCalled();
44
+ });
45
+ });
46
+
47
+ describe('inherited methods', () => {
48
+ it('should have init method', () => {
49
+ const bridge = new AspectlyBridge();
50
+ expect(typeof bridge.init).toBe('function');
51
+ });
52
+
53
+ it('should have send method', () => {
54
+ const bridge = new AspectlyBridge();
55
+ expect(typeof bridge.send).toBe('function');
56
+ });
57
+
58
+ it('should have subscribe method', () => {
59
+ const bridge = new AspectlyBridge();
60
+ expect(typeof bridge.subscribe).toBe('function');
61
+ });
62
+
63
+ it('should have unsubscribe method', () => {
64
+ const bridge = new AspectlyBridge();
65
+ expect(typeof bridge.unsubscribe).toBe('function');
66
+ });
67
+
68
+ it('should have supports method', () => {
69
+ const bridge = new AspectlyBridge();
70
+ expect(typeof bridge.supports).toBe('function');
71
+ });
72
+
73
+ it('should have isAvailable method', () => {
74
+ const bridge = new AspectlyBridge();
75
+ expect(typeof bridge.isAvailable).toBe('function');
76
+ });
77
+ });
78
+ });
@@ -0,0 +1,44 @@
1
+ import { BridgeCore } from './BridgeCore';
2
+ import { BridgeInternal } from './BridgeInternal';
3
+ import { BridgeBase } from './BridgeBase';
4
+ import type { BridgeOptions } from './types';
5
+
6
+ /**
7
+ * AspectlyBridge is the main entry point for bridge communication.
8
+ * Use this class when running inside a WebView or iframe that needs
9
+ * to communicate with its parent container.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Inside a WebView or iframe
14
+ * const bridge = new AspectlyBridge();
15
+ *
16
+ * // Initialize with handlers
17
+ * await bridge.init({
18
+ * greet: async (params) => {
19
+ * return { message: `Hello, ${params.name}!` };
20
+ * }
21
+ * });
22
+ *
23
+ * // Send messages to parent
24
+ * const result = await bridge.send('someMethod', { data: 'value' });
25
+ * ```
26
+ */
27
+ export class AspectlyBridge extends BridgeBase {
28
+ private cleanupSubscription: VoidFunction;
29
+
30
+ constructor(options?: BridgeOptions) {
31
+ const bridge = new BridgeInternal(BridgeCore.sendEvent, options);
32
+ super(bridge);
33
+ this.cleanupSubscription = BridgeCore.subscribe(
34
+ bridge.handleCoreEvent as (event: unknown) => void
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Cleanup bridge subscriptions
40
+ */
41
+ public destroy = (): void => {
42
+ this.cleanupSubscription();
43
+ };
44
+ }