@webview-bridge/web 1.0.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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # webview-bridge
2
+
3
+ webview-bridge package for web
4
+
5
+ For more information, check out https://github.com/gronxb/webview-bridge
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ MethodNotFoundError: () => MethodNotFoundError,
24
+ linkNativeMethod: () => linkNativeMethod,
25
+ registerWebMethod: () => registerWebMethod
26
+ });
27
+ module.exports = __toCommonJS(src_exports);
28
+
29
+ // src/error.ts
30
+ var MethodNotFoundError = class extends Error {
31
+ constructor(methodName) {
32
+ super(`Method ${methodName} is not defined`);
33
+ this.name = "MethodNotFoundError";
34
+ }
35
+ };
36
+
37
+ // ../../shared/util/src/createEvents.ts
38
+ var createEvents = () => ({
39
+ events: {},
40
+ emit(event, ...args) {
41
+ const callbacks = this.events[event] || [];
42
+ for (let i = 0, length = callbacks.length; i < length; i++) {
43
+ callbacks[i](...args);
44
+ }
45
+ },
46
+ on(event, cb) {
47
+ this.events[event]?.push(cb) || (this.events[event] = [cb]);
48
+ return () => {
49
+ this.events[event] = this.events[event]?.filter((i) => cb !== i);
50
+ };
51
+ }
52
+ });
53
+ var createResolver = (emitter2, method, eventId, evaluate) => {
54
+ return new Promise((resolve) => {
55
+ const unbind = emitter2.on(`${method}-${eventId}`, (data) => {
56
+ unbind();
57
+ resolve(data);
58
+ });
59
+ evaluate();
60
+ });
61
+ };
62
+
63
+ // ../../shared/util/src/createRandomId.tsx
64
+ var TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
65
+ var ID_LENGTH = 21;
66
+ var createRandomId = (size = ID_LENGTH) => {
67
+ const randomValues = Array.from(
68
+ { length: size },
69
+ () => TABLE[Math.floor(Math.random() * TABLE.length)]
70
+ );
71
+ return randomValues.join("");
72
+ };
73
+
74
+ // ../../shared/util/src/timeout.ts
75
+ var timeout = (ms) => {
76
+ return new Promise((_, reject) => {
77
+ setTimeout(() => {
78
+ reject(new Error("Timeout"));
79
+ }, ms);
80
+ });
81
+ };
82
+
83
+ // src/linkNativeMethod.ts
84
+ var emitter = createEvents();
85
+ var linkNativeMethod = (options = {
86
+ timeout: 2e3,
87
+ throwOnError: false
88
+ }) => {
89
+ const {
90
+ timeout: timeoutMs = 2e3,
91
+ throwOnError = false,
92
+ onFallback
93
+ } = options;
94
+ if (!window.ReactNativeWebView) {
95
+ console.warn("[WebViewBridge] Not in a WebView environment");
96
+ }
97
+ const bridgeMethods = window.__bridgeMethods__ ?? [];
98
+ if (!window.nativeEmitter) {
99
+ window.nativeEmitter = emitter;
100
+ }
101
+ const target = bridgeMethods.reduce((acc, method) => {
102
+ return {
103
+ ...acc,
104
+ [method]: (...args) => {
105
+ const eventId = createRandomId();
106
+ return Promise.race([
107
+ createResolver(emitter, method, eventId, () => {
108
+ window.ReactNativeWebView?.postMessage(
109
+ JSON.stringify({
110
+ type: "bridge",
111
+ body: {
112
+ method,
113
+ eventId,
114
+ args
115
+ }
116
+ })
117
+ );
118
+ }),
119
+ timeout(timeoutMs)
120
+ ]);
121
+ }
122
+ };
123
+ }, {});
124
+ return new Proxy(target, {
125
+ get: (target2, method) => {
126
+ if (method in target2) {
127
+ return target2[method];
128
+ }
129
+ window.ReactNativeWebView?.postMessage(
130
+ JSON.stringify({
131
+ type: "fallback",
132
+ body: {
133
+ method
134
+ }
135
+ })
136
+ );
137
+ onFallback?.(method);
138
+ if (throwOnError === true) {
139
+ return () => Promise.reject(new MethodNotFoundError(method));
140
+ } else if (Array.isArray(throwOnError) && throwOnError.includes(method)) {
141
+ return () => Promise.reject(new MethodNotFoundError(method));
142
+ } else {
143
+ console.warn(
144
+ `[WebViewBridge] ${method} is not defined, using fallback.`
145
+ );
146
+ }
147
+ return () => Promise.resolve();
148
+ }
149
+ });
150
+ };
151
+
152
+ // src/registerWebMethod.ts
153
+ var registerWebMethod = (bridge) => {
154
+ if (!window.ReactNativeWebView) {
155
+ console.warn("[WebViewBridge] Not in a WebView environment");
156
+ return bridge;
157
+ }
158
+ const bridgeEntries = Object.entries(bridge);
159
+ const bridgeNames = Object.keys(bridge);
160
+ const emitter2 = createEvents();
161
+ window.webEmitter = emitter2;
162
+ for (const [funcName, func] of bridgeEntries) {
163
+ const $func = async (eventId, args) => {
164
+ const value = await func(...args);
165
+ window.ReactNativeWebView?.postMessage(
166
+ JSON.stringify({
167
+ type: "webMethodResponse",
168
+ body: { funcName, eventId, value }
169
+ })
170
+ );
171
+ };
172
+ emitter2.on(funcName, $func);
173
+ }
174
+ const register = () => {
175
+ window.ReactNativeWebView?.postMessage(
176
+ JSON.stringify({
177
+ type: "registerWebMethod",
178
+ body: { bridgeNames }
179
+ })
180
+ );
181
+ window.removeEventListener("DOMContentLoaded", register);
182
+ };
183
+ if (!window.ReactNativeWebView) {
184
+ window.addEventListener("DOMContentLoaded", register);
185
+ return bridge;
186
+ }
187
+ register();
188
+ return bridge;
189
+ };
190
+ // Annotate the CommonJS export names for ESM import in node:
191
+ 0 && (module.exports = {
192
+ MethodNotFoundError,
193
+ linkNativeMethod,
194
+ registerWebMethod
195
+ });
@@ -0,0 +1,166 @@
1
+ // src/error.ts
2
+ var MethodNotFoundError = class extends Error {
3
+ constructor(methodName) {
4
+ super(`Method ${methodName} is not defined`);
5
+ this.name = "MethodNotFoundError";
6
+ }
7
+ };
8
+
9
+ // ../../shared/util/src/createEvents.ts
10
+ var createEvents = () => ({
11
+ events: {},
12
+ emit(event, ...args) {
13
+ const callbacks = this.events[event] || [];
14
+ for (let i = 0, length = callbacks.length; i < length; i++) {
15
+ callbacks[i](...args);
16
+ }
17
+ },
18
+ on(event, cb) {
19
+ this.events[event]?.push(cb) || (this.events[event] = [cb]);
20
+ return () => {
21
+ this.events[event] = this.events[event]?.filter((i) => cb !== i);
22
+ };
23
+ }
24
+ });
25
+ var createResolver = (emitter2, method, eventId, evaluate) => {
26
+ return new Promise((resolve) => {
27
+ const unbind = emitter2.on(`${method}-${eventId}`, (data) => {
28
+ unbind();
29
+ resolve(data);
30
+ });
31
+ evaluate();
32
+ });
33
+ };
34
+
35
+ // ../../shared/util/src/createRandomId.tsx
36
+ var TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
37
+ var ID_LENGTH = 21;
38
+ var createRandomId = (size = ID_LENGTH) => {
39
+ const randomValues = Array.from(
40
+ { length: size },
41
+ () => TABLE[Math.floor(Math.random() * TABLE.length)]
42
+ );
43
+ return randomValues.join("");
44
+ };
45
+
46
+ // ../../shared/util/src/timeout.ts
47
+ var timeout = (ms) => {
48
+ return new Promise((_, reject) => {
49
+ setTimeout(() => {
50
+ reject(new Error("Timeout"));
51
+ }, ms);
52
+ });
53
+ };
54
+
55
+ // src/linkNativeMethod.ts
56
+ var emitter = createEvents();
57
+ var linkNativeMethod = (options = {
58
+ timeout: 2e3,
59
+ throwOnError: false
60
+ }) => {
61
+ const {
62
+ timeout: timeoutMs = 2e3,
63
+ throwOnError = false,
64
+ onFallback
65
+ } = options;
66
+ if (!window.ReactNativeWebView) {
67
+ console.warn("[WebViewBridge] Not in a WebView environment");
68
+ }
69
+ const bridgeMethods = window.__bridgeMethods__ ?? [];
70
+ if (!window.nativeEmitter) {
71
+ window.nativeEmitter = emitter;
72
+ }
73
+ const target = bridgeMethods.reduce((acc, method) => {
74
+ return {
75
+ ...acc,
76
+ [method]: (...args) => {
77
+ const eventId = createRandomId();
78
+ return Promise.race([
79
+ createResolver(emitter, method, eventId, () => {
80
+ window.ReactNativeWebView?.postMessage(
81
+ JSON.stringify({
82
+ type: "bridge",
83
+ body: {
84
+ method,
85
+ eventId,
86
+ args
87
+ }
88
+ })
89
+ );
90
+ }),
91
+ timeout(timeoutMs)
92
+ ]);
93
+ }
94
+ };
95
+ }, {});
96
+ return new Proxy(target, {
97
+ get: (target2, method) => {
98
+ if (method in target2) {
99
+ return target2[method];
100
+ }
101
+ window.ReactNativeWebView?.postMessage(
102
+ JSON.stringify({
103
+ type: "fallback",
104
+ body: {
105
+ method
106
+ }
107
+ })
108
+ );
109
+ onFallback?.(method);
110
+ if (throwOnError === true) {
111
+ return () => Promise.reject(new MethodNotFoundError(method));
112
+ } else if (Array.isArray(throwOnError) && throwOnError.includes(method)) {
113
+ return () => Promise.reject(new MethodNotFoundError(method));
114
+ } else {
115
+ console.warn(
116
+ `[WebViewBridge] ${method} is not defined, using fallback.`
117
+ );
118
+ }
119
+ return () => Promise.resolve();
120
+ }
121
+ });
122
+ };
123
+
124
+ // src/registerWebMethod.ts
125
+ var registerWebMethod = (bridge) => {
126
+ if (!window.ReactNativeWebView) {
127
+ console.warn("[WebViewBridge] Not in a WebView environment");
128
+ return bridge;
129
+ }
130
+ const bridgeEntries = Object.entries(bridge);
131
+ const bridgeNames = Object.keys(bridge);
132
+ const emitter2 = createEvents();
133
+ window.webEmitter = emitter2;
134
+ for (const [funcName, func] of bridgeEntries) {
135
+ const $func = async (eventId, args) => {
136
+ const value = await func(...args);
137
+ window.ReactNativeWebView?.postMessage(
138
+ JSON.stringify({
139
+ type: "webMethodResponse",
140
+ body: { funcName, eventId, value }
141
+ })
142
+ );
143
+ };
144
+ emitter2.on(funcName, $func);
145
+ }
146
+ const register = () => {
147
+ window.ReactNativeWebView?.postMessage(
148
+ JSON.stringify({
149
+ type: "registerWebMethod",
150
+ body: { bridgeNames }
151
+ })
152
+ );
153
+ window.removeEventListener("DOMContentLoaded", register);
154
+ };
155
+ if (!window.ReactNativeWebView) {
156
+ window.addEventListener("DOMContentLoaded", register);
157
+ return bridge;
158
+ }
159
+ register();
160
+ return bridge;
161
+ };
162
+ export {
163
+ MethodNotFoundError,
164
+ linkNativeMethod,
165
+ registerWebMethod
166
+ };
@@ -0,0 +1,3 @@
1
+ export declare const createNativeMethod: <T = unknown>(options?: {
2
+ timeout: number;
3
+ }) => T;
@@ -0,0 +1,2 @@
1
+ import type { Bridge } from "./types";
2
+ export declare const createWebMethod: Bridge;
@@ -0,0 +1,3 @@
1
+ export declare class MethodNotFoundError extends Error {
2
+ constructor(methodName: string);
3
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./error";
2
+ export * from "./linkNativeMethod";
3
+ export * from "./registerWebMethod";
4
+ export type * from "./types";
@@ -0,0 +1,6 @@
1
+ export interface LinkNativeMethodOptions<T extends object> {
2
+ timeout?: number;
3
+ throwOnError?: boolean | (keyof T)[];
4
+ onFallback?: (method: keyof T) => void;
5
+ }
6
+ export declare const linkNativeMethod: <T extends object>(options?: LinkNativeMethodOptions<T>) => T;
@@ -0,0 +1,2 @@
1
+ import type { Bridge } from "./types";
2
+ export declare const registerWebMethod: Bridge;
@@ -0,0 +1,5 @@
1
+ export type Procedure = (...args: any[]) => any;
2
+ export type ProceduresObject<T extends Record<string, Procedure>> = {
3
+ [K in keyof T]: (...args: Parameters<T[K]>) => Promise<Awaited<ReturnType<T[K]>>>;
4
+ };
5
+ export type Bridge = <T extends Record<string, Procedure>>(procedures: T) => ProceduresObject<T>;
@@ -0,0 +1,16 @@
1
+ interface EventsMap {
2
+ [event: string]: any;
3
+ }
4
+ export interface DefaultEvents extends EventsMap {
5
+ [event: string]: (...args: any) => void;
6
+ }
7
+ export interface EventEmitter<Events extends EventsMap = DefaultEvents> {
8
+ emit<K extends keyof Events>(this: this, event: K, ...args: Parameters<Events[K]>): void;
9
+ events: Partial<{
10
+ [E in keyof Events]: Events[E][];
11
+ }>;
12
+ on<K extends keyof Events>(this: this, event: K, cb: Events[K]): () => void;
13
+ }
14
+ export declare const createEvents: <Events extends EventsMap = DefaultEvents>() => EventEmitter<Events>;
15
+ export declare const createResolver: (emitter: EventEmitter<DefaultEvents>, method: string, eventId: string, evaluate: () => void) => Promise<unknown>;
16
+ export {};
@@ -0,0 +1 @@
1
+ export declare const createRandomId: (size?: number) => string;
@@ -0,0 +1,4 @@
1
+ export * from "./createEvents";
2
+ export * from "./createRandomId";
3
+ export * from "./noop";
4
+ export * from "./timeout";
@@ -0,0 +1 @@
1
+ export declare const noop: () => void;
@@ -0,0 +1 @@
1
+ export declare const timeout: (ms: number) => Promise<unknown>;
@@ -0,0 +1,5 @@
1
+ export type Procedure = (...args: any[]) => any;
2
+ export type ProceduresObject<T extends Record<string, Procedure>> = {
3
+ [K in keyof T]: (...args: Parameters<T[K]>) => Promise<Awaited<ReturnType<T[K]>>>;
4
+ };
5
+ export type Bridge = <T extends Record<string, Procedure>>(procedures: T) => ProceduresObject<T>;
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@webview-bridge/web",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "Integration Web and React Native WebView",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/gronxb/webview-bridge.git"
12
+ },
13
+ "license": "MIT",
14
+ "files": [
15
+ "dist",
16
+ "package.json"
17
+ ],
18
+ "main": "/dist/commonjs/index.cjs",
19
+ "module": "/dist/module/index.mjs",
20
+ "types": "/dist/typescript/packages/web/src/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./dist/module/index.mjs",
24
+ "require": "./dist/commonjs/index.cjs",
25
+ "types": "./dist/typescript/packages/web/src/index.d.ts"
26
+ }
27
+ },
28
+ "devDependencies": {
29
+ "esbuild": "^0.19.4"
30
+ },
31
+ "scripts": {
32
+ "build": "node esbuild.config.js && tsc --emitDeclarationOnly",
33
+ "test:type": "tsc --noEmit"
34
+ }
35
+ }