@protoc-gen-go-wasmjs/runtime 0.0.28 → 0.0.30

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.
@@ -1,41 +1,56 @@
1
1
  /**
2
2
  * Browser Service Manager
3
- * Handles FIFO processing of browser service calls from WASM
4
- * This is a shared component used by all WASM clients that interact with browser services
3
+ *
4
+ * Manages browser service implementations that can be called from WASM.
5
+ *
6
+ * There are two calling patterns:
7
+ *
8
+ * 1. Synchronous: WASM calls __browserServices.ServiceName.methodName(request)
9
+ * and receives the result directly. The JS function must return a value (not a Promise).
10
+ *
11
+ * 2. Asynchronous: WASM calls __wasmCallBrowserMethodAsync(service, method, request, callbackId)
12
+ * and JS calls __wasmDeliverBrowserResponse(callbackId, response, error) when done.
13
+ * This is for browser APIs that return Promises (fetch, IndexedDB, etc.)
5
14
  */
6
15
  declare class BrowserServiceManager {
7
- private processing;
8
16
  private serviceImplementations;
9
- private wasmModule;
17
+ private initialized;
10
18
  constructor();
11
19
  /**
12
- * Register a browser service implementation
20
+ * Register a browser service implementation.
21
+ * The service object should have methods matching the proto service definition.
22
+ *
23
+ * Methods can be either:
24
+ * - Synchronous: Return a value directly (for sync browser APIs)
25
+ * - Asynchronous: Return a Promise (for async browser APIs like fetch)
13
26
  */
14
27
  registerService(name: string, implementation: any): void;
15
28
  /**
16
- * Set the WASM module reference
29
+ * Initialize the browser service bridge.
30
+ * This sets up the global functions that WASM uses to call browser services.
17
31
  */
18
- setWasmModule(wasmModule: any): void;
32
+ initialize(): void;
19
33
  /**
20
- * Start processing browser service calls
34
+ * Update the __browserServices global object with current registrations.
35
+ * This is called automatically when services are registered.
21
36
  */
22
- startProcessing(): Promise<void>;
37
+ private updateBrowserServices;
23
38
  /**
24
- * Process a single browser service call asynchronously
39
+ * Get all method names from an object, including prototype chain.
25
40
  */
26
- private processCall;
41
+ private getAllMethodNames;
27
42
  /**
28
- * Stop processing browser service calls
43
+ * Deliver an async response back to WASM.
29
44
  */
30
- stopProcessing(): void;
45
+ private deliverResponse;
31
46
  /**
32
- * Get the next browser call from WASM
47
+ * Check if a service is registered.
33
48
  */
34
- private getNextBrowserCall;
49
+ hasService(name: string): boolean;
35
50
  /**
36
- * Deliver a response back to WASM (called internally)
51
+ * Get the number of registered services.
37
52
  */
38
- private deliverBrowserResponse;
53
+ getServiceCount(): number;
39
54
  }
40
55
 
41
56
  export { BrowserServiceManager };
@@ -1,41 +1,56 @@
1
1
  /**
2
2
  * Browser Service Manager
3
- * Handles FIFO processing of browser service calls from WASM
4
- * This is a shared component used by all WASM clients that interact with browser services
3
+ *
4
+ * Manages browser service implementations that can be called from WASM.
5
+ *
6
+ * There are two calling patterns:
7
+ *
8
+ * 1. Synchronous: WASM calls __browserServices.ServiceName.methodName(request)
9
+ * and receives the result directly. The JS function must return a value (not a Promise).
10
+ *
11
+ * 2. Asynchronous: WASM calls __wasmCallBrowserMethodAsync(service, method, request, callbackId)
12
+ * and JS calls __wasmDeliverBrowserResponse(callbackId, response, error) when done.
13
+ * This is for browser APIs that return Promises (fetch, IndexedDB, etc.)
5
14
  */
6
15
  declare class BrowserServiceManager {
7
- private processing;
8
16
  private serviceImplementations;
9
- private wasmModule;
17
+ private initialized;
10
18
  constructor();
11
19
  /**
12
- * Register a browser service implementation
20
+ * Register a browser service implementation.
21
+ * The service object should have methods matching the proto service definition.
22
+ *
23
+ * Methods can be either:
24
+ * - Synchronous: Return a value directly (for sync browser APIs)
25
+ * - Asynchronous: Return a Promise (for async browser APIs like fetch)
13
26
  */
14
27
  registerService(name: string, implementation: any): void;
15
28
  /**
16
- * Set the WASM module reference
29
+ * Initialize the browser service bridge.
30
+ * This sets up the global functions that WASM uses to call browser services.
17
31
  */
18
- setWasmModule(wasmModule: any): void;
32
+ initialize(): void;
19
33
  /**
20
- * Start processing browser service calls
34
+ * Update the __browserServices global object with current registrations.
35
+ * This is called automatically when services are registered.
21
36
  */
22
- startProcessing(): Promise<void>;
37
+ private updateBrowserServices;
23
38
  /**
24
- * Process a single browser service call asynchronously
39
+ * Get all method names from an object, including prototype chain.
25
40
  */
26
- private processCall;
41
+ private getAllMethodNames;
27
42
  /**
28
- * Stop processing browser service calls
43
+ * Deliver an async response back to WASM.
29
44
  */
30
- stopProcessing(): void;
45
+ private deliverResponse;
31
46
  /**
32
- * Get the next browser call from WASM
47
+ * Check if a service is registered.
33
48
  */
34
- private getNextBrowserCall;
49
+ hasService(name: string): boolean;
35
50
  /**
36
- * Deliver a response back to WASM (called internally)
51
+ * Get the number of registered services.
37
52
  */
38
- private deliverBrowserResponse;
53
+ getServiceCount(): number;
39
54
  }
40
55
 
41
56
  export { BrowserServiceManager };
@@ -3,83 +3,122 @@
3
3
  // src/browser/service-manager.ts
4
4
  var BrowserServiceManager = class {
5
5
  constructor() {
6
- this.processing = false;
7
6
  this.serviceImplementations = /* @__PURE__ */ new Map();
7
+ this.initialized = false;
8
8
  }
9
9
  /**
10
- * Register a browser service implementation
10
+ * Register a browser service implementation.
11
+ * The service object should have methods matching the proto service definition.
12
+ *
13
+ * Methods can be either:
14
+ * - Synchronous: Return a value directly (for sync browser APIs)
15
+ * - Asynchronous: Return a Promise (for async browser APIs like fetch)
11
16
  */
12
17
  registerService(name, implementation) {
13
18
  this.serviceImplementations.set(name, implementation);
19
+ this.updateBrowserServices();
14
20
  }
15
21
  /**
16
- * Set the WASM module reference
22
+ * Initialize the browser service bridge.
23
+ * This sets up the global functions that WASM uses to call browser services.
17
24
  */
18
- setWasmModule(wasmModule) {
19
- this.wasmModule = wasmModule;
25
+ initialize() {
26
+ if (this.initialized) return;
27
+ this.initialized = true;
28
+ window.__wasmCallBrowserMethodAsync = async (service, method, request, callbackId) => {
29
+ try {
30
+ const impl = this.serviceImplementations.get(service);
31
+ if (!impl) {
32
+ this.deliverResponse(callbackId, null, `Service '${service}' not registered`);
33
+ return;
34
+ }
35
+ const methodName = method.charAt(0).toLowerCase() + method.slice(1);
36
+ const methodFunc = impl[methodName];
37
+ if (!methodFunc) {
38
+ this.deliverResponse(callbackId, null, `Method '${methodName}' not found on service '${service}'`);
39
+ return;
40
+ }
41
+ const requestObj = JSON.parse(request);
42
+ const response = await Promise.resolve(methodFunc.call(impl, requestObj));
43
+ this.deliverResponse(callbackId, JSON.stringify(response), null);
44
+ } catch (error) {
45
+ this.deliverResponse(callbackId, null, error.message || String(error));
46
+ }
47
+ };
48
+ this.updateBrowserServices();
20
49
  }
21
50
  /**
22
- * Start processing browser service calls
51
+ * Update the __browserServices global object with current registrations.
52
+ * This is called automatically when services are registered.
23
53
  */
24
- async startProcessing() {
25
- if (this.processing) return;
26
- this.processing = true;
27
- while (this.processing) {
28
- const call = this.getNextBrowserCall();
29
- if (!call) {
30
- await new Promise((resolve) => setTimeout(resolve, 10));
31
- continue;
54
+ updateBrowserServices() {
55
+ const browserServices = {};
56
+ for (const [name, impl] of this.serviceImplementations) {
57
+ const serviceProxy = {};
58
+ const methodNames = this.getAllMethodNames(impl);
59
+ for (const methodName of methodNames) {
60
+ serviceProxy[methodName] = (request) => {
61
+ try {
62
+ const requestObj = JSON.parse(request);
63
+ const result = impl[methodName](requestObj);
64
+ if (result instanceof Promise) {
65
+ console.warn(
66
+ `Method ${name}.${methodName} returned a Promise but was called synchronously. Mark this method as async in the proto definition.`
67
+ );
68
+ return JSON.stringify({ error: "Method is async but called synchronously" });
69
+ }
70
+ return JSON.stringify(result);
71
+ } catch (error) {
72
+ return JSON.stringify({ error: error.message || String(error) });
73
+ }
74
+ };
32
75
  }
33
- this.processCall(call);
76
+ browserServices[name] = serviceProxy;
34
77
  }
78
+ window.__browserServices = browserServices;
35
79
  }
36
80
  /**
37
- * Process a single browser service call asynchronously
81
+ * Get all method names from an object, including prototype chain.
38
82
  */
39
- async processCall(call) {
40
- try {
41
- const service = this.serviceImplementations.get(call.service);
42
- if (!service) {
43
- throw new Error(`No implementation registered for service: ${call.service}`);
83
+ getAllMethodNames(obj) {
84
+ const methods = /* @__PURE__ */ new Set();
85
+ for (const name of Object.keys(obj)) {
86
+ if (typeof obj[name] === "function") {
87
+ methods.add(name);
44
88
  }
45
- const methodName = call.method.charAt(0).toLowerCase() + call.method.slice(1);
46
- const method = service[methodName];
47
- if (!method) {
48
- throw new Error(`Method ${methodName} not found on service ${call.service}`);
89
+ }
90
+ let proto = Object.getPrototypeOf(obj);
91
+ while (proto && proto !== Object.prototype) {
92
+ for (const name of Object.getOwnPropertyNames(proto)) {
93
+ if (name !== "constructor" && typeof obj[name] === "function") {
94
+ methods.add(name);
95
+ }
49
96
  }
50
- const request = JSON.parse(call.request);
51
- const response = await Promise.resolve(method.call(service, request));
52
- console.log(`DEBUG: Browser service response for ${call.service}.${call.method}:`, response);
53
- const jsonResponse = JSON.stringify(response);
54
- console.log(`DEBUG: JSON stringified response:`, jsonResponse);
55
- this.deliverBrowserResponse(call.id, jsonResponse, null);
56
- } catch (error) {
57
- this.deliverBrowserResponse(call.id, null, error.message || String(error));
97
+ proto = Object.getPrototypeOf(proto);
58
98
  }
99
+ return Array.from(methods);
59
100
  }
60
101
  /**
61
- * Stop processing browser service calls
102
+ * Deliver an async response back to WASM.
62
103
  */
63
- stopProcessing() {
64
- this.processing = false;
104
+ deliverResponse(callbackId, response, error) {
105
+ if (typeof window.__wasmDeliverBrowserResponse === "function") {
106
+ window.__wasmDeliverBrowserResponse(callbackId, response, error);
107
+ } else {
108
+ console.error("__wasmDeliverBrowserResponse not available - WASM not initialized?");
109
+ }
65
110
  }
66
111
  /**
67
- * Get the next browser call from WASM
112
+ * Check if a service is registered.
68
113
  */
69
- getNextBrowserCall() {
70
- if (typeof window.__wasmGetNextBrowserCall === "function") {
71
- return window.__wasmGetNextBrowserCall();
72
- }
73
- return null;
114
+ hasService(name) {
115
+ return this.serviceImplementations.has(name);
74
116
  }
75
117
  /**
76
- * Deliver a response back to WASM (called internally)
118
+ * Get the number of registered services.
77
119
  */
78
- deliverBrowserResponse(callId, response, error) {
79
- if (!window.__wasmDeliverBrowserResponse) {
80
- return false;
81
- }
82
- return window.__wasmDeliverBrowserResponse(callId, response, error);
120
+ getServiceCount() {
121
+ return this.serviceImplementations.size;
83
122
  }
84
123
  };
85
124
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/browser/service-manager.ts"],"names":[],"mappings":";;;AAmBO,IAAM,wBAAN,MAA4B;AAAA,EAK/B,WAAA,GAAc;AAJd,IAAA,IAAA,CAAQ,UAAA,GAAa,KAAA;AACrB,IAAA,IAAA,CAAQ,sBAAA,uBAA6B,GAAA,EAAiB;AAAA,EAMtD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAA,CAAgB,MAAc,cAAA,EAA2B;AACrD,IAAA,IAAA,CAAK,sBAAA,CAAuB,GAAA,CAAI,IAAA,EAAM,cAAc,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAAA,EAAuB;AACjC,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAA,GAAiC;AACnC,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAGlB,IAAA,OAAO,KAAK,UAAA,EAAY;AACpB,MAAA,MAAM,IAAA,GAAO,KAAK,kBAAA,EAAmB;AACrC,MAAA,IAAI,CAAC,IAAA,EAAM;AAEP,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,OAAA,KAAW,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACpD,QAAA;AAAA,MACJ;AAGA,MAAA,IAAA,CAAK,YAAY,IAAI,CAAA;AAAA,IACzB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAAY,IAAA,EAA0B;AAChD,IAAA,IAAI;AAEA,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,sBAAA,CAAuB,GAAA,CAAI,KAAK,OAAO,CAAA;AAC5D,MAAA,IAAI,CAAC,OAAA,EAAS;AACV,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0CAAA,EAA6C,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AAAA,MAC/E;AAGA,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,GAAI,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA;AAC5E,MAAA,MAAM,MAAA,GAAS,QAAQ,UAAU,CAAA;AACjC,MAAA,IAAI,CAAC,MAAA,EAAQ;AACT,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,OAAA,EAAU,UAAU,CAAA,sBAAA,EAAyB,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AAAA,MAC/E;AAGA,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA;AAGvC,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAA,CAAQ,OAAO,IAAA,CAAK,OAAA,EAAS,OAAO,CAAC,CAAA;AAEpE,MAAA,OAAA,CAAQ,GAAA,CAAI,uCAAuC,IAAA,CAAK,OAAO,IAAI,IAAA,CAAK,MAAM,KAAK,QAAQ,CAAA;AAC3F,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA;AAC5C,MAAA,OAAA,CAAQ,GAAA,CAAI,qCAAqC,YAAY,CAAA;AAG7D,MAAA,IAAA,CAAK,sBAAA,CAAuB,IAAA,CAAK,EAAA,EAAI,YAAA,EAAc,IAAI,CAAA;AAAA,IAC3D,SAAS,KAAA,EAAY;AAEjB,MAAA,IAAA,CAAK,sBAAA,CAAuB,KAAK,EAAA,EAAI,IAAA,EAAM,MAAM,OAAA,IAAW,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAC7E;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,cAAA,GAAuB;AACnB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAA,GAA0B;AAE9B,IAAA,IAAI,OAAQ,MAAA,CAAe,wBAAA,KAA6B,UAAA,EAAY;AAChE,MAAA,OAAQ,OAAe,wBAAA,EAAyB;AAAA,IACpD;AACA,IAAA,OAAO,IAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAA,CAAuB,MAAA,EAAgB,QAAA,EAAyB,KAAA,EAA+B;AACnG,IAAA,IAAI,CAAE,OAAe,4BAAA,EAA8B;AAC/C,MAAA,OAAO,KAAA;AAAA,IACX;AACA,IAAA,OAAQ,MAAA,CAAe,4BAAA,CAA6B,MAAA,EAAQ,QAAA,EAAU,KAAK,CAAA;AAAA,EAC/E;AACJ","file":"index.js","sourcesContent":["// Copyright 2025 Sri Panyam\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * Browser Service Manager\n * Handles FIFO processing of browser service calls from WASM\n * This is a shared component used by all WASM clients that interact with browser services\n */\nexport class BrowserServiceManager {\n private processing = false;\n private serviceImplementations = new Map<string, any>();\n private wasmModule: any;\n\n constructor() {\n // WASM will set up the global functions __wasmGetNextBrowserCall and __wasmDeliverBrowserResponse\n // We'll just use them when they're available\n }\n\n /**\n * Register a browser service implementation\n */\n registerService(name: string, implementation: any): void {\n this.serviceImplementations.set(name, implementation);\n }\n\n /**\n * Set the WASM module reference\n */\n setWasmModule(wasmModule: any): void {\n this.wasmModule = wasmModule;\n }\n\n /**\n * Start processing browser service calls\n */\n async startProcessing(): Promise<void> {\n if (this.processing) return;\n this.processing = true;\n\n // Process calls in a loop\n while (this.processing) {\n const call = this.getNextBrowserCall();\n if (!call) {\n // No pending calls, wait a bit\n await new Promise(resolve => setTimeout(resolve, 10));\n continue;\n }\n\n // Process each call asynchronously without blocking the loop\n this.processCall(call);\n }\n }\n\n /**\n * Process a single browser service call asynchronously\n */\n private async processCall(call: any): Promise<void> {\n try {\n // Get the service implementation\n const service = this.serviceImplementations.get(call.service);\n if (!service) {\n throw new Error(`No implementation registered for service: ${call.service}`);\n }\n\n // Get the method\n const methodName = call.method.charAt(0).toLowerCase() + call.method.slice(1);\n const method = service[methodName];\n if (!method) {\n throw new Error(`Method ${methodName} not found on service ${call.service}`);\n }\n\n // Parse request\n const request = JSON.parse(call.request);\n\n // Call the method (auto-await if async)\n const response = await Promise.resolve(method.call(service, request));\n \n console.log(`DEBUG: Browser service response for ${call.service}.${call.method}:`, response);\n const jsonResponse = JSON.stringify(response);\n console.log(`DEBUG: JSON stringified response:`, jsonResponse);\n\n // Deliver response\n this.deliverBrowserResponse(call.id, jsonResponse, null);\n } catch (error: any) {\n // Deliver error\n this.deliverBrowserResponse(call.id, null, error.message || String(error));\n }\n }\n\n /**\n * Stop processing browser service calls\n */\n stopProcessing(): void {\n this.processing = false;\n }\n\n /**\n * Get the next browser call from WASM\n */\n private getNextBrowserCall(): any {\n // The __wasmGetNextBrowserCall function should be provided by WASM\n if (typeof (window as any).__wasmGetNextBrowserCall === 'function') {\n return (window as any).__wasmGetNextBrowserCall();\n }\n return null;\n }\n\n /**\n * Deliver a response back to WASM (called internally)\n */\n private deliverBrowserResponse(callId: string, response: string | null, error: string | null): boolean {\n if (!(window as any).__wasmDeliverBrowserResponse) {\n return false;\n }\n return (window as any).__wasmDeliverBrowserResponse(callId, response, error);\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/browser/service-manager.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2025 Sri Panyam\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * Browser Service Manager\n *\n * Manages browser service implementations that can be called from WASM.\n *\n * There are two calling patterns:\n *\n * 1. Synchronous: WASM calls __browserServices.ServiceName.methodName(request)\n * and receives the result directly. The JS function must return a value (not a Promise).\n *\n * 2. Asynchronous: WASM calls __wasmCallBrowserMethodAsync(service, method, request, callbackId)\n * and JS calls __wasmDeliverBrowserResponse(callbackId, response, error) when done.\n * This is for browser APIs that return Promises (fetch, IndexedDB, etc.)\n */\nexport class BrowserServiceManager {\n private serviceImplementations = new Map<string, any>();\n private initialized = false;\n\n constructor() {}\n\n /**\n * Register a browser service implementation.\n * The service object should have methods matching the proto service definition.\n *\n * Methods can be either:\n * - Synchronous: Return a value directly (for sync browser APIs)\n * - Asynchronous: Return a Promise (for async browser APIs like fetch)\n */\n registerService(name: string, implementation: any): void {\n this.serviceImplementations.set(name, implementation);\n this.updateBrowserServices();\n }\n\n /**\n * Initialize the browser service bridge.\n * This sets up the global functions that WASM uses to call browser services.\n */\n initialize(): void {\n if (this.initialized) return;\n this.initialized = true;\n\n // Set up the async method handler\n // WASM calls this for async browser methods (ones that return Promises)\n (window as any).__wasmCallBrowserMethodAsync = async (\n service: string,\n method: string,\n request: string,\n callbackId: string\n ) => {\n try {\n const impl = this.serviceImplementations.get(service);\n if (!impl) {\n this.deliverResponse(callbackId, null, `Service '${service}' not registered`);\n return;\n }\n\n // Convert method name to camelCase (e.g., \"getLocalStorage\" -> \"getLocalStorage\")\n const methodName = method.charAt(0).toLowerCase() + method.slice(1);\n const methodFunc = impl[methodName];\n if (!methodFunc) {\n this.deliverResponse(callbackId, null, `Method '${methodName}' not found on service '${service}'`);\n return;\n }\n\n // Parse request and call the method\n const requestObj = JSON.parse(request);\n const response = await Promise.resolve(methodFunc.call(impl, requestObj));\n\n // Deliver the response back to WASM\n this.deliverResponse(callbackId, JSON.stringify(response), null);\n } catch (error: any) {\n this.deliverResponse(callbackId, null, error.message || String(error));\n }\n };\n\n // Update the browser services object\n this.updateBrowserServices();\n }\n\n /**\n * Update the __browserServices global object with current registrations.\n * This is called automatically when services are registered.\n */\n private updateBrowserServices(): void {\n const browserServices: Record<string, any> = {};\n\n for (const [name, impl] of this.serviceImplementations) {\n const serviceProxy: Record<string, any> = {};\n\n // Get all methods from the implementation\n // Check both own properties and prototype chain\n const methodNames = this.getAllMethodNames(impl);\n\n for (const methodName of methodNames) {\n // Create a wrapper that handles sync calls\n serviceProxy[methodName] = (request: string): string => {\n try {\n const requestObj = JSON.parse(request);\n const result = impl[methodName](requestObj);\n\n // For sync methods, result should not be a Promise\n // If it is, this is a programming error - async methods should use\n // __wasmCallBrowserMethodAsync instead\n if (result instanceof Promise) {\n console.warn(\n `Method ${name}.${methodName} returned a Promise but was called synchronously. ` +\n `Mark this method as async in the proto definition.`\n );\n return JSON.stringify({ error: 'Method is async but called synchronously' });\n }\n\n return JSON.stringify(result);\n } catch (error: any) {\n return JSON.stringify({ error: error.message || String(error) });\n }\n };\n }\n\n browserServices[name] = serviceProxy;\n }\n\n (window as any).__browserServices = browserServices;\n }\n\n /**\n * Get all method names from an object, including prototype chain.\n */\n private getAllMethodNames(obj: any): string[] {\n const methods = new Set<string>();\n\n // Get own property names (for plain objects)\n for (const name of Object.keys(obj)) {\n if (typeof obj[name] === 'function') {\n methods.add(name);\n }\n }\n\n // Also check prototype chain (for class instances)\n let proto = Object.getPrototypeOf(obj);\n while (proto && proto !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name !== 'constructor' && typeof obj[name] === 'function') {\n methods.add(name);\n }\n }\n proto = Object.getPrototypeOf(proto);\n }\n\n return Array.from(methods);\n }\n\n /**\n * Deliver an async response back to WASM.\n */\n private deliverResponse(callbackId: string, response: string | null, error: string | null): void {\n if (typeof (window as any).__wasmDeliverBrowserResponse === 'function') {\n (window as any).__wasmDeliverBrowserResponse(callbackId, response, error);\n } else {\n console.error('__wasmDeliverBrowserResponse not available - WASM not initialized?');\n }\n }\n\n /**\n * Check if a service is registered.\n */\n hasService(name: string): boolean {\n return this.serviceImplementations.has(name);\n }\n\n /**\n * Get the number of registered services.\n */\n getServiceCount(): number {\n return this.serviceImplementations.size;\n }\n}\n"]}
@@ -1,83 +1,122 @@
1
1
  // src/browser/service-manager.ts
2
2
  var BrowserServiceManager = class {
3
3
  constructor() {
4
- this.processing = false;
5
4
  this.serviceImplementations = /* @__PURE__ */ new Map();
5
+ this.initialized = false;
6
6
  }
7
7
  /**
8
- * Register a browser service implementation
8
+ * Register a browser service implementation.
9
+ * The service object should have methods matching the proto service definition.
10
+ *
11
+ * Methods can be either:
12
+ * - Synchronous: Return a value directly (for sync browser APIs)
13
+ * - Asynchronous: Return a Promise (for async browser APIs like fetch)
9
14
  */
10
15
  registerService(name, implementation) {
11
16
  this.serviceImplementations.set(name, implementation);
17
+ this.updateBrowserServices();
12
18
  }
13
19
  /**
14
- * Set the WASM module reference
20
+ * Initialize the browser service bridge.
21
+ * This sets up the global functions that WASM uses to call browser services.
15
22
  */
16
- setWasmModule(wasmModule) {
17
- this.wasmModule = wasmModule;
23
+ initialize() {
24
+ if (this.initialized) return;
25
+ this.initialized = true;
26
+ window.__wasmCallBrowserMethodAsync = async (service, method, request, callbackId) => {
27
+ try {
28
+ const impl = this.serviceImplementations.get(service);
29
+ if (!impl) {
30
+ this.deliverResponse(callbackId, null, `Service '${service}' not registered`);
31
+ return;
32
+ }
33
+ const methodName = method.charAt(0).toLowerCase() + method.slice(1);
34
+ const methodFunc = impl[methodName];
35
+ if (!methodFunc) {
36
+ this.deliverResponse(callbackId, null, `Method '${methodName}' not found on service '${service}'`);
37
+ return;
38
+ }
39
+ const requestObj = JSON.parse(request);
40
+ const response = await Promise.resolve(methodFunc.call(impl, requestObj));
41
+ this.deliverResponse(callbackId, JSON.stringify(response), null);
42
+ } catch (error) {
43
+ this.deliverResponse(callbackId, null, error.message || String(error));
44
+ }
45
+ };
46
+ this.updateBrowserServices();
18
47
  }
19
48
  /**
20
- * Start processing browser service calls
49
+ * Update the __browserServices global object with current registrations.
50
+ * This is called automatically when services are registered.
21
51
  */
22
- async startProcessing() {
23
- if (this.processing) return;
24
- this.processing = true;
25
- while (this.processing) {
26
- const call = this.getNextBrowserCall();
27
- if (!call) {
28
- await new Promise((resolve) => setTimeout(resolve, 10));
29
- continue;
52
+ updateBrowserServices() {
53
+ const browserServices = {};
54
+ for (const [name, impl] of this.serviceImplementations) {
55
+ const serviceProxy = {};
56
+ const methodNames = this.getAllMethodNames(impl);
57
+ for (const methodName of methodNames) {
58
+ serviceProxy[methodName] = (request) => {
59
+ try {
60
+ const requestObj = JSON.parse(request);
61
+ const result = impl[methodName](requestObj);
62
+ if (result instanceof Promise) {
63
+ console.warn(
64
+ `Method ${name}.${methodName} returned a Promise but was called synchronously. Mark this method as async in the proto definition.`
65
+ );
66
+ return JSON.stringify({ error: "Method is async but called synchronously" });
67
+ }
68
+ return JSON.stringify(result);
69
+ } catch (error) {
70
+ return JSON.stringify({ error: error.message || String(error) });
71
+ }
72
+ };
30
73
  }
31
- this.processCall(call);
74
+ browserServices[name] = serviceProxy;
32
75
  }
76
+ window.__browserServices = browserServices;
33
77
  }
34
78
  /**
35
- * Process a single browser service call asynchronously
79
+ * Get all method names from an object, including prototype chain.
36
80
  */
37
- async processCall(call) {
38
- try {
39
- const service = this.serviceImplementations.get(call.service);
40
- if (!service) {
41
- throw new Error(`No implementation registered for service: ${call.service}`);
81
+ getAllMethodNames(obj) {
82
+ const methods = /* @__PURE__ */ new Set();
83
+ for (const name of Object.keys(obj)) {
84
+ if (typeof obj[name] === "function") {
85
+ methods.add(name);
42
86
  }
43
- const methodName = call.method.charAt(0).toLowerCase() + call.method.slice(1);
44
- const method = service[methodName];
45
- if (!method) {
46
- throw new Error(`Method ${methodName} not found on service ${call.service}`);
87
+ }
88
+ let proto = Object.getPrototypeOf(obj);
89
+ while (proto && proto !== Object.prototype) {
90
+ for (const name of Object.getOwnPropertyNames(proto)) {
91
+ if (name !== "constructor" && typeof obj[name] === "function") {
92
+ methods.add(name);
93
+ }
47
94
  }
48
- const request = JSON.parse(call.request);
49
- const response = await Promise.resolve(method.call(service, request));
50
- console.log(`DEBUG: Browser service response for ${call.service}.${call.method}:`, response);
51
- const jsonResponse = JSON.stringify(response);
52
- console.log(`DEBUG: JSON stringified response:`, jsonResponse);
53
- this.deliverBrowserResponse(call.id, jsonResponse, null);
54
- } catch (error) {
55
- this.deliverBrowserResponse(call.id, null, error.message || String(error));
95
+ proto = Object.getPrototypeOf(proto);
56
96
  }
97
+ return Array.from(methods);
57
98
  }
58
99
  /**
59
- * Stop processing browser service calls
100
+ * Deliver an async response back to WASM.
60
101
  */
61
- stopProcessing() {
62
- this.processing = false;
102
+ deliverResponse(callbackId, response, error) {
103
+ if (typeof window.__wasmDeliverBrowserResponse === "function") {
104
+ window.__wasmDeliverBrowserResponse(callbackId, response, error);
105
+ } else {
106
+ console.error("__wasmDeliverBrowserResponse not available - WASM not initialized?");
107
+ }
63
108
  }
64
109
  /**
65
- * Get the next browser call from WASM
110
+ * Check if a service is registered.
66
111
  */
67
- getNextBrowserCall() {
68
- if (typeof window.__wasmGetNextBrowserCall === "function") {
69
- return window.__wasmGetNextBrowserCall();
70
- }
71
- return null;
112
+ hasService(name) {
113
+ return this.serviceImplementations.has(name);
72
114
  }
73
115
  /**
74
- * Deliver a response back to WASM (called internally)
116
+ * Get the number of registered services.
75
117
  */
76
- deliverBrowserResponse(callId, response, error) {
77
- if (!window.__wasmDeliverBrowserResponse) {
78
- return false;
79
- }
80
- return window.__wasmDeliverBrowserResponse(callId, response, error);
118
+ getServiceCount() {
119
+ return this.serviceImplementations.size;
81
120
  }
82
121
  };
83
122
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/browser/service-manager.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// Copyright 2025 Sri Panyam\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * Browser Service Manager\n * Handles FIFO processing of browser service calls from WASM\n * This is a shared component used by all WASM clients that interact with browser services\n */\nexport class BrowserServiceManager {\n private processing = false;\n private serviceImplementations = new Map<string, any>();\n private wasmModule: any;\n\n constructor() {\n // WASM will set up the global functions __wasmGetNextBrowserCall and __wasmDeliverBrowserResponse\n // We'll just use them when they're available\n }\n\n /**\n * Register a browser service implementation\n */\n registerService(name: string, implementation: any): void {\n this.serviceImplementations.set(name, implementation);\n }\n\n /**\n * Set the WASM module reference\n */\n setWasmModule(wasmModule: any): void {\n this.wasmModule = wasmModule;\n }\n\n /**\n * Start processing browser service calls\n */\n async startProcessing(): Promise<void> {\n if (this.processing) return;\n this.processing = true;\n\n // Process calls in a loop\n while (this.processing) {\n const call = this.getNextBrowserCall();\n if (!call) {\n // No pending calls, wait a bit\n await new Promise(resolve => setTimeout(resolve, 10));\n continue;\n }\n\n // Process each call asynchronously without blocking the loop\n this.processCall(call);\n }\n }\n\n /**\n * Process a single browser service call asynchronously\n */\n private async processCall(call: any): Promise<void> {\n try {\n // Get the service implementation\n const service = this.serviceImplementations.get(call.service);\n if (!service) {\n throw new Error(`No implementation registered for service: ${call.service}`);\n }\n\n // Get the method\n const methodName = call.method.charAt(0).toLowerCase() + call.method.slice(1);\n const method = service[methodName];\n if (!method) {\n throw new Error(`Method ${methodName} not found on service ${call.service}`);\n }\n\n // Parse request\n const request = JSON.parse(call.request);\n\n // Call the method (auto-await if async)\n const response = await Promise.resolve(method.call(service, request));\n \n console.log(`DEBUG: Browser service response for ${call.service}.${call.method}:`, response);\n const jsonResponse = JSON.stringify(response);\n console.log(`DEBUG: JSON stringified response:`, jsonResponse);\n\n // Deliver response\n this.deliverBrowserResponse(call.id, jsonResponse, null);\n } catch (error: any) {\n // Deliver error\n this.deliverBrowserResponse(call.id, null, error.message || String(error));\n }\n }\n\n /**\n * Stop processing browser service calls\n */\n stopProcessing(): void {\n this.processing = false;\n }\n\n /**\n * Get the next browser call from WASM\n */\n private getNextBrowserCall(): any {\n // The __wasmGetNextBrowserCall function should be provided by WASM\n if (typeof (window as any).__wasmGetNextBrowserCall === 'function') {\n return (window as any).__wasmGetNextBrowserCall();\n }\n return null;\n }\n\n /**\n * Deliver a response back to WASM (called internally)\n */\n private deliverBrowserResponse(callId: string, response: string | null, error: string | null): boolean {\n if (!(window as any).__wasmDeliverBrowserResponse) {\n return false;\n }\n return (window as any).__wasmDeliverBrowserResponse(callId, response, error);\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/browser/service-manager.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// Copyright 2025 Sri Panyam\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * Browser Service Manager\n *\n * Manages browser service implementations that can be called from WASM.\n *\n * There are two calling patterns:\n *\n * 1. Synchronous: WASM calls __browserServices.ServiceName.methodName(request)\n * and receives the result directly. The JS function must return a value (not a Promise).\n *\n * 2. Asynchronous: WASM calls __wasmCallBrowserMethodAsync(service, method, request, callbackId)\n * and JS calls __wasmDeliverBrowserResponse(callbackId, response, error) when done.\n * This is for browser APIs that return Promises (fetch, IndexedDB, etc.)\n */\nexport class BrowserServiceManager {\n private serviceImplementations = new Map<string, any>();\n private initialized = false;\n\n constructor() {}\n\n /**\n * Register a browser service implementation.\n * The service object should have methods matching the proto service definition.\n *\n * Methods can be either:\n * - Synchronous: Return a value directly (for sync browser APIs)\n * - Asynchronous: Return a Promise (for async browser APIs like fetch)\n */\n registerService(name: string, implementation: any): void {\n this.serviceImplementations.set(name, implementation);\n this.updateBrowserServices();\n }\n\n /**\n * Initialize the browser service bridge.\n * This sets up the global functions that WASM uses to call browser services.\n */\n initialize(): void {\n if (this.initialized) return;\n this.initialized = true;\n\n // Set up the async method handler\n // WASM calls this for async browser methods (ones that return Promises)\n (window as any).__wasmCallBrowserMethodAsync = async (\n service: string,\n method: string,\n request: string,\n callbackId: string\n ) => {\n try {\n const impl = this.serviceImplementations.get(service);\n if (!impl) {\n this.deliverResponse(callbackId, null, `Service '${service}' not registered`);\n return;\n }\n\n // Convert method name to camelCase (e.g., \"getLocalStorage\" -> \"getLocalStorage\")\n const methodName = method.charAt(0).toLowerCase() + method.slice(1);\n const methodFunc = impl[methodName];\n if (!methodFunc) {\n this.deliverResponse(callbackId, null, `Method '${methodName}' not found on service '${service}'`);\n return;\n }\n\n // Parse request and call the method\n const requestObj = JSON.parse(request);\n const response = await Promise.resolve(methodFunc.call(impl, requestObj));\n\n // Deliver the response back to WASM\n this.deliverResponse(callbackId, JSON.stringify(response), null);\n } catch (error: any) {\n this.deliverResponse(callbackId, null, error.message || String(error));\n }\n };\n\n // Update the browser services object\n this.updateBrowserServices();\n }\n\n /**\n * Update the __browserServices global object with current registrations.\n * This is called automatically when services are registered.\n */\n private updateBrowserServices(): void {\n const browserServices: Record<string, any> = {};\n\n for (const [name, impl] of this.serviceImplementations) {\n const serviceProxy: Record<string, any> = {};\n\n // Get all methods from the implementation\n // Check both own properties and prototype chain\n const methodNames = this.getAllMethodNames(impl);\n\n for (const methodName of methodNames) {\n // Create a wrapper that handles sync calls\n serviceProxy[methodName] = (request: string): string => {\n try {\n const requestObj = JSON.parse(request);\n const result = impl[methodName](requestObj);\n\n // For sync methods, result should not be a Promise\n // If it is, this is a programming error - async methods should use\n // __wasmCallBrowserMethodAsync instead\n if (result instanceof Promise) {\n console.warn(\n `Method ${name}.${methodName} returned a Promise but was called synchronously. ` +\n `Mark this method as async in the proto definition.`\n );\n return JSON.stringify({ error: 'Method is async but called synchronously' });\n }\n\n return JSON.stringify(result);\n } catch (error: any) {\n return JSON.stringify({ error: error.message || String(error) });\n }\n };\n }\n\n browserServices[name] = serviceProxy;\n }\n\n (window as any).__browserServices = browserServices;\n }\n\n /**\n * Get all method names from an object, including prototype chain.\n */\n private getAllMethodNames(obj: any): string[] {\n const methods = new Set<string>();\n\n // Get own property names (for plain objects)\n for (const name of Object.keys(obj)) {\n if (typeof obj[name] === 'function') {\n methods.add(name);\n }\n }\n\n // Also check prototype chain (for class instances)\n let proto = Object.getPrototypeOf(obj);\n while (proto && proto !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name !== 'constructor' && typeof obj[name] === 'function') {\n methods.add(name);\n }\n }\n proto = Object.getPrototypeOf(proto);\n }\n\n return Array.from(methods);\n }\n\n /**\n * Deliver an async response back to WASM.\n */\n private deliverResponse(callbackId: string, response: string | null, error: string | null): void {\n if (typeof (window as any).__wasmDeliverBrowserResponse === 'function') {\n (window as any).__wasmDeliverBrowserResponse(callbackId, response, error);\n } else {\n console.error('__wasmDeliverBrowserResponse not available - WASM not initialized?');\n }\n }\n\n /**\n * Check if a service is registered.\n */\n hasService(name: string): boolean {\n return this.serviceImplementations.has(name);\n }\n\n /**\n * Get the number of registered services.\n */\n getServiceCount(): number {\n return this.serviceImplementations.size;\n }\n}\n"]}