@event-chat/rpc 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 快乐的小萌新
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Rslib project
2
+
3
+ ## Setup
4
+
5
+ Install the dependencies:
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ ## Get started
12
+
13
+ Build the library:
14
+
15
+ ```bash
16
+ npm run build
17
+ ```
18
+
19
+ Build the library in watch mode:
20
+
21
+ ```bash
22
+ npm run dev
23
+ ```
@@ -0,0 +1,42 @@
1
+ import RPCFactory, { IframeSerializeOptions } from './RPCFactory';
2
+ declare class RPCAction {
3
+ private _target;
4
+ private _brodcastListeners;
5
+ private _handlers;
6
+ private _heartbeatTimer;
7
+ private _isConnected;
8
+ private _lastHeartbeat;
9
+ private _options;
10
+ private _pending;
11
+ private _boundMessageHandler;
12
+ constructor(_target: RPCFactory, options?: RPCOptionsType);
13
+ destroy(): void;
14
+ broadcast<T>(options?: Omit<RequestOptions<T>, 'retry'>): void;
15
+ config(options: Omit<RPCOptionsType, 'onConnect' | 'onDisconnect'>): void;
16
+ on<T extends ActionFunType>(type: PropertyKey, handler: T): void;
17
+ onBrodcast(listener: BrodcastItem): void;
18
+ request<T = unknown>(type: PropertyKey, options?: RequestOptions<T>): Promise<unknown>;
19
+ private _abort;
20
+ private _createRequestId;
21
+ private _isOriginAllowed;
22
+ private _messageHandler;
23
+ private _startHeartbeat;
24
+ }
25
+ export default RPCAction;
26
+ export type RPCOptionsType = {
27
+ allowedOrigins?: string[];
28
+ channel?: string;
29
+ heartbeatInterval?: number;
30
+ heartbeatTimeout?: number;
31
+ retryTimeout?: number;
32
+ retryTimes?: number;
33
+ onConnect?: () => void;
34
+ onDisconnect?: () => void;
35
+ };
36
+ export type ActionFunType = (payload?: any) => any;
37
+ export type BrodcastItem = (value: unknown, origin?: string) => void;
38
+ export type RequestOptions<T = unknown> = IframeSerializeOptions & {
39
+ payload?: T;
40
+ retry?: number;
41
+ };
42
+ //# sourceMappingURL=RPCAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RPCAction.d.ts","sourceRoot":"","sources":["../src/RPCAction.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,EAAE,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAejE,cAAM,SAAS;IAWX,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,oBAAoB,CAAkC;gBAGpD,OAAO,EAAE,UAAU,EAC3B,OAAO,CAAC,EAAE,cAAc;IAc1B,OAAO;IAUP,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;IASvD,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,cAAc,CAAC;IAIlE,EAAE,CAAC,CAAC,SAAS,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IAIzD,UAAU,CAAC,QAAQ,EAAE,YAAY;IAIjC,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;IA6CnE,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,eAAe;IAuEvB,OAAO,CAAC,eAAe;CAsBxB;AAED,eAAe,SAAS,CAAA;AAExB,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;CAC1B,CAAA;AAID,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,GAAG,CAAA;AAElD,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;AAEpE,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,OAAO,IAAI,sBAAsB,GAAG;IACjE,OAAO,CAAC,EAAE,CAAC,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA"}
@@ -0,0 +1,189 @@
1
+ import { isKey } from "./utils.js";
2
+ const RPC_SIGN = 'RPCActionSign';
3
+ const defaultOptions = {
4
+ allowedOrigins: [],
5
+ heartbeatInterval: 3000,
6
+ heartbeatTimeout: 8000,
7
+ retryTimeout: 5000,
8
+ retryTimes: 2
9
+ };
10
+ const getOrigin = (url)=>new URL(url, window.location.origin).origin;
11
+ class RPCAction {
12
+ _target;
13
+ _brodcastListeners = [];
14
+ _handlers = {};
15
+ _heartbeatTimer = null;
16
+ _isConnected = false;
17
+ _lastHeartbeat = Date.now();
18
+ _options = {};
19
+ _pending = new Map();
20
+ _boundMessageHandler = this._messageHandler.bind(this);
21
+ constructor(_target, options){
22
+ this._target = _target;
23
+ this._options = {
24
+ ...defaultOptions,
25
+ ...options,
26
+ allowedOrigins: options?.allowedOrigins?.map((item)=>'*' === item ? item : getOrigin(item))
27
+ };
28
+ this._target.onmessage(this._boundMessageHandler);
29
+ this._startHeartbeat();
30
+ }
31
+ destroy() {
32
+ clearInterval(this._heartbeatTimer ?? void 0);
33
+ this._target.onremove(this._boundMessageHandler);
34
+ this._abort();
35
+ this._brodcastListeners = [];
36
+ this._handlers = {};
37
+ this._heartbeatTimer = null;
38
+ }
39
+ broadcast(options) {
40
+ const { payload, ...ops } = options ?? {};
41
+ const { channel } = this._options;
42
+ this._target.postMessage({
43
+ __RPC__: RPC_SIGN,
44
+ broadcast: true,
45
+ kind: 'request',
46
+ channel,
47
+ payload
48
+ }, {
49
+ ...ops,
50
+ targetOrigin: ops.targetOrigin ?? window.location.origin
51
+ });
52
+ }
53
+ config(options) {
54
+ this._options = {
55
+ ...this._options,
56
+ ...options
57
+ };
58
+ }
59
+ on(type, handler) {
60
+ this._handlers[type] = handler;
61
+ }
62
+ onBrodcast(listener) {
63
+ this._brodcastListeners.push(listener);
64
+ }
65
+ request(type, options) {
66
+ const { payload, retry = 0, ...ops } = options ?? {};
67
+ const { channel, retryTimeout = defaultOptions.retryTimeout, retryTimes = defaultOptions.retryTimes } = this._options;
68
+ return new Promise((resolve, reject)=>{
69
+ if (!this._isConnected) return void reject(new Error(`[RPC] 连接未建立,无法请求:${type.toString()}`));
70
+ const requestId = this._createRequestId();
71
+ const timer = window.setTimeout(()=>{
72
+ this._pending.delete(requestId);
73
+ if (retry < retryTimes) resolve(this.request(type, {
74
+ ...options,
75
+ retry: retry + 1
76
+ }));
77
+ else reject(new Error(`[RPC] 请求超时:${type.toString()}`));
78
+ }, retryTimeout);
79
+ this._pending.set(requestId, {
80
+ resolve: (res)=>{
81
+ clearTimeout(timer);
82
+ resolve(res);
83
+ },
84
+ reject: (err)=>{
85
+ const message = err instanceof Error ? err.message : '[RPC] 处理消息时发生错误';
86
+ clearTimeout(timer);
87
+ reject(new Error(message));
88
+ },
89
+ timer
90
+ });
91
+ this._target.postMessage({
92
+ __RPC__: RPC_SIGN,
93
+ kind: 'request',
94
+ channel,
95
+ payload,
96
+ requestId,
97
+ type
98
+ }, {
99
+ ...ops,
100
+ targetOrigin: ops.targetOrigin ?? window.location.origin
101
+ });
102
+ });
103
+ }
104
+ _abort() {
105
+ this._isConnected = false;
106
+ this._options?.onDisconnect?.();
107
+ this._pending.forEach(({ reject, timer })=>{
108
+ clearTimeout(timer);
109
+ reject(new Error('[RPC] 连接已断开,请求已取消'));
110
+ });
111
+ this._pending.clear();
112
+ }
113
+ _createRequestId() {
114
+ const requestId = Math.random().toString(36).slice(2, 10);
115
+ return this._pending.has(requestId) ? this._createRequestId() : requestId;
116
+ }
117
+ _isOriginAllowed(origin) {
118
+ return this._options?.allowedOrigins?.some((item)=>'*' === item || getOrigin(item) === getOrigin(origin)) ?? false;
119
+ }
120
+ _messageHandler(event) {
121
+ const { data, origin, source } = event;
122
+ const { __RPC__, broadcast, channel, error, heartbeat, kind, payload, requestId, type } = data ?? {};
123
+ if (__RPC__ !== RPC_SIGN) return;
124
+ if (this._options?.channel !== channel) return;
125
+ if (!this._target.is(source)) return;
126
+ if (this._target.getType() === window.toString() && !this._isOriginAllowed(origin)) return;
127
+ if (heartbeat) {
128
+ this._lastHeartbeat = Date.now();
129
+ if (!this._isConnected) {
130
+ this._isConnected = true;
131
+ this._options?.onConnect?.();
132
+ }
133
+ return;
134
+ }
135
+ if (broadcast) return void this._brodcastListeners.forEach((listener)=>listener(payload, origin));
136
+ const pending = requestId ? this._pending.get(requestId) : void 0;
137
+ if (requestId && pending) {
138
+ const { resolve, reject } = pending;
139
+ this._pending.delete(requestId);
140
+ if (void 0 !== error) reject(new Error(error));
141
+ else resolve(payload);
142
+ return;
143
+ }
144
+ if ('request' !== kind) return;
145
+ const handler = type && isKey(type, this._handlers) ? this._handlers[type] : void 0;
146
+ if (handler) Promise.resolve().then(()=>handler(payload)).then((result)=>{
147
+ this._target.postMessage({
148
+ __RPC__: RPC_SIGN,
149
+ kind: 'response',
150
+ payload: result,
151
+ channel,
152
+ requestId,
153
+ type
154
+ }, {
155
+ targetOrigin: origin
156
+ });
157
+ }).catch((err)=>{
158
+ const message = err instanceof Error ? err.message : '[RPC] 处理消息时发生错误';
159
+ this._target.postMessage({
160
+ __RPC__: RPC_SIGN,
161
+ error: message,
162
+ kind: 'response',
163
+ payload: '',
164
+ channel,
165
+ requestId
166
+ }, {
167
+ targetOrigin: origin
168
+ });
169
+ });
170
+ }
171
+ _startHeartbeat() {
172
+ const { channel, heartbeatInterval = defaultOptions.heartbeatInterval, heartbeatTimeout = defaultOptions.heartbeatTimeout } = this._options;
173
+ const intervalLoops = ()=>{
174
+ this._target.postMessage({
175
+ __RPC__: RPC_SIGN,
176
+ heartbeat: true,
177
+ kind: 'request',
178
+ channel
179
+ });
180
+ if (!this._isConnected) return;
181
+ if (Date.now() - this._lastHeartbeat > heartbeatTimeout) this._abort();
182
+ };
183
+ intervalLoops();
184
+ clearInterval(this._heartbeatTimer ?? void 0);
185
+ this._heartbeatTimer = window.setInterval(intervalLoops, heartbeatInterval);
186
+ }
187
+ }
188
+ const src_RPCAction = RPCAction;
189
+ export { src_RPCAction as default };
@@ -0,0 +1,21 @@
1
+ import RPCAction, { ActionFunType, BrodcastItem, RPCOptionsType, RequestOptions } from './RPCAction';
2
+ import RPCFactory, { FactoryOptions, TargetType } from './RPCFactory';
3
+ declare const factoryKey: readonly ["getType", "upset"];
4
+ declare function RPCDecorator<EVENT extends ActionRecord, CONSUME extends ActionRecord>(target: TargetType | null, context?: DecoratorContext<EVENT, CONSUME>): Readonly<Omit<RPCAction, "on" | "request"> & Pick<RPCFactory, (typeof factoryKey)[number]> & {
5
+ request: <K extends keyof CONSUME>(...args: Parameters<CONSUME[K]> extends [] ? [keyname: K, reqops?: RequestOptionsByAction<CONSUME[K]>] : [keyname: K, reqops: RequestOptionsByAction<CONSUME[K]>]) => Promise<ReturnType<CONSUME[K]>>;
6
+ }>;
7
+ export default RPCDecorator;
8
+ export interface DecoratorContext<EVENT extends ActionRecord, CONSUME extends ActionRecord> {
9
+ brodcast?: Record<string, BrodcastItem>;
10
+ config?: RPCOptionsType;
11
+ consume?: CONSUME;
12
+ event?: EVENT;
13
+ options?: FactoryOptions;
14
+ }
15
+ export type ActionRecord = Record<string, ActionFunType>;
16
+ type RequestOptionsByAction<F extends ActionFunType> = Parameters<F> extends [] ? Omit<RequestOptions, 'payload'> & {
17
+ payload?: never;
18
+ } : RequestOptions<Parameters<F>[0]> & {
19
+ payload: Parameters<F>[0];
20
+ };
21
+ //# sourceMappingURL=RPCDecorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RPCDecorator.d.ts","sourceRoot":"","sources":["../src/RPCDecorator.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACpG,OAAO,UAAU,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAErE,QAAA,MAAM,UAAU,+BAAgC,CAAA;AAUhD,iBAAS,YAAY,CAAC,KAAK,SAAS,YAAY,EAAE,OAAO,SAAS,YAAY,EAC5E,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,OAAO,CAAC,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,GAwDrC,QAAQ,CACX,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,CAAC,GAC/B,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,UAAU,EAAE,MAAM,CAAC,CAAC,GAAG;IAC9C,OAAO,GArDI,CAAC,SAAS,MAAM,OAAO,WAC7B,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,GACtC,CAAC,OAAO,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GACzD,CAAC,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAGjB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CA+C/C;CACxB,CACJ,CACF;AAED,eAAe,YAAY,CAAA;AAE3B,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,YAAY,EAAE,OAAO,SAAS,YAAY;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;AAExD,KAAK,sBAAsB,CAAC,CAAC,SAAS,aAAa,IACjD,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE,GACpB,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,KAAK,CAAA;CAAE,GACrD,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;CAAE,CAAA"}
@@ -0,0 +1,57 @@
1
+ import RPCAction from "./RPCAction.js";
2
+ import RPCFactory from "./RPCFactory.js";
3
+ const factoryKey = [
4
+ 'getType',
5
+ 'upset'
6
+ ];
7
+ function isAction(action, key) {
8
+ return key in action;
9
+ }
10
+ function isFactory(action, key) {
11
+ return null !== action && key in action && factoryKey.map(String).includes(key);
12
+ }
13
+ function RPCDecorator(target, context) {
14
+ const { brodcast, config, event, options } = context ?? {};
15
+ const factory = target ? new RPCFactory(target, options) : null;
16
+ const action = factory ? new RPCAction(factory, config) : null;
17
+ const request = (...args)=>{
18
+ const [keyname, reqops] = args;
19
+ return action?.request(keyname, reqops);
20
+ };
21
+ Object.entries(event ?? {}).forEach(([keyname, handle])=>action?.on(keyname, handle));
22
+ Object.values(brodcast ?? {}).forEach((handle)=>action?.onBrodcast(handle));
23
+ return new Proxy({}, {
24
+ get (_, key) {
25
+ const keyname = key.toString();
26
+ if (!action || 'on' === keyname || !(keyname in action) && !isFactory(factory, keyname)) return;
27
+ switch(key){
28
+ case 'destroy':
29
+ return ()=>{
30
+ action.destroy();
31
+ factory?.destory();
32
+ };
33
+ case 'request':
34
+ return request;
35
+ default:
36
+ if (isAction(action, keyname)) {
37
+ const value = action[keyname];
38
+ return 'function' == typeof value ? value.bind(action) : value;
39
+ }
40
+ if (factory && isFactory(factory, keyname)) {
41
+ const value = factory[keyname];
42
+ return 'function' == typeof value ? value.bind(factory) : value;
43
+ }
44
+ throw new Error('outof decorator');
45
+ }
46
+ },
47
+ has (_, key) {
48
+ const keyname = key.toString();
49
+ return null !== action && 'on' !== keyname && (keyname in action || isFactory(factory, keyname));
50
+ },
51
+ set () {
52
+ throw new Error('decorator is readonly');
53
+ }
54
+ });
55
+ }
56
+ const src_RPCDecorator = RPCDecorator;
57
+ export { src_RPCDecorator as default };
@@ -0,0 +1,22 @@
1
+ declare class RPCFactory<T extends TargetType = TargetType> {
2
+ private _target;
3
+ private _options;
4
+ private _onconnect;
5
+ constructor(_target: T, _options?: FactoryOptions);
6
+ destory(): void;
7
+ getType(): string;
8
+ is(source: MessageEventSource | null): boolean;
9
+ onmessage(listener: (ev: MessageEvent) => unknown): void;
10
+ onremove(listener: (ev: MessageEvent) => unknown): void;
11
+ postMessage(message: unknown, options?: IframeSerializeOptions): void;
12
+ upset(options: FactoryOptions): void;
13
+ }
14
+ export default RPCFactory;
15
+ export interface FactoryOptions {
16
+ message?: boolean | AddEventListenerOptions;
17
+ }
18
+ export type IframeSerializeOptions = StructuredSerializeOptions & {
19
+ targetOrigin?: string;
20
+ };
21
+ export type TargetType = BroadcastChannel | WebSocket | Window | ServiceWorkerRegistration | ServiceWorkerGlobalScope | SharedWorker | SharedWorkerGlobalScope | Worker | DedicatedWorkerGlobalScope;
22
+ //# sourceMappingURL=RPCFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RPCFactory.d.ts","sourceRoot":"","sources":["../src/RPCFactory.ts"],"names":[],"mappings":"AAQA,cAAM,UAAU,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAI9C,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,UAAU,CAAwB;gBAGhC,OAAO,EAAE,CAAC,EACV,QAAQ,GAAE,cAAmB;IAqBvC,OAAO;IA4CP,OAAO;IAIP,EAAE,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAIpC,SAAS,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO;IAoFjD,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO;IA0DhD,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,sBAAsB;IAqE9D,KAAK,CAAC,OAAO,EAAE,cAAc;CAG9B;AAED,eAAe,UAAU,CAAA;AAEzB,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAAA;CAC5C;AAED,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,GAAG;IAChE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAID,MAAM,MAAM,UAAU,GAClB,gBAAgB,GAChB,SAAS,GACT,MAAM,GAEN,yBAAyB,GACzB,wBAAwB,GACxB,YAAY,GACZ,uBAAuB,GACvB,MAAM,GACN,0BAA0B,CAAA"}
@@ -0,0 +1,141 @@
1
+ import { TARGET_TYPE_STRINGS } from "./fields.js";
2
+ function isSafeBufferSource(data) {
3
+ return data instanceof ArrayBuffer || ArrayBuffer.isView(data) && data.buffer instanceof ArrayBuffer;
4
+ }
5
+ class RPCFactory {
6
+ _target;
7
+ _options;
8
+ _onconnect = {};
9
+ constructor(_target, _options = {}){
10
+ this._target = _target;
11
+ this._options = _options;
12
+ this._onconnect = {
13
+ WebSocketPort: new Promise((resolve)=>{
14
+ if (this._target instanceof WebSocket) {
15
+ this._target.onopen = ()=>{
16
+ resolve(true);
17
+ };
18
+ this._target.onclose = ()=>{
19
+ resolve(false);
20
+ };
21
+ } else resolve(false);
22
+ })
23
+ };
24
+ }
25
+ destory() {
26
+ if (this._target instanceof BroadcastChannel) this._target.close();
27
+ if (this._target instanceof WebSocket) {
28
+ this._target.onclose = null;
29
+ this._target.onopen = null;
30
+ if (this._target.readyState === WebSocket.OPEN) this._target.close();
31
+ }
32
+ this._target, ServiceWorkerContainer;
33
+ if (this._target instanceof SharedWorker) this._target.port.close();
34
+ if ('undefined' != typeof SharedWorkerGlobalScope && this._target instanceof SharedWorkerGlobalScope) {
35
+ this._onconnect.SharedWorkerGlobalPort?.then((messagePort)=>messagePort.close()).catch(()=>{});
36
+ if (this._onconnect.SharedWorkerGlobalScope) this._target.removeEventListener('connect', this._onconnect.SharedWorkerGlobalScope);
37
+ }
38
+ if (this._target instanceof Worker) this._target.terminate();
39
+ this._onconnect = {};
40
+ }
41
+ getType() {
42
+ return `${Object.prototype.toString.call(this._target)}`;
43
+ }
44
+ is(source) {
45
+ return this._target === source;
46
+ }
47
+ onmessage(listener) {
48
+ if (this._target instanceof BroadcastChannel) this._target.addEventListener('message', listener, this._options.message);
49
+ if (this._target instanceof WebSocket) this._target.onmessage = listener;
50
+ if (this.getType() === TARGET_TYPE_STRINGS.Window) window.addEventListener('message', listener, this._options.message);
51
+ if ('undefined' != typeof ServiceWorkerRegistration && this._target instanceof ServiceWorkerRegistration) navigator.serviceWorker.addEventListener('message', listener, this._options.message);
52
+ if ('undefined' != typeof ServiceWorkerGlobalScope && this._target instanceof ServiceWorkerGlobalScope) {
53
+ this._onconnect.ServiceWorkerGlobalScope = (event)=>{
54
+ const ports = event.ports.map((i)=>i);
55
+ const source = event.source instanceof Client ? null : event.source;
56
+ const messageEvent = new MessageEvent('serverWork', {
57
+ ...event,
58
+ ports,
59
+ source
60
+ });
61
+ listener(messageEvent);
62
+ };
63
+ this._target.addEventListener('message', this._onconnect.ServiceWorkerGlobalScope, this._options.message);
64
+ }
65
+ if (this._target instanceof SharedWorker) {
66
+ this._target.port.addEventListener('message', listener, this._options.message);
67
+ this._target.port.start();
68
+ }
69
+ if ('undefined' != typeof SharedWorkerGlobalScope && this._target instanceof SharedWorkerGlobalScope) {
70
+ const SharedWorker1 = this._target;
71
+ this._onconnect.SharedWorkerGlobalPort = new Promise((resolve)=>{
72
+ this._onconnect.SharedWorkerGlobalScope = (event)=>{
73
+ const port = event.ports[0];
74
+ resolve(port);
75
+ port.addEventListener('message', listener, this._options.message);
76
+ port.start();
77
+ };
78
+ SharedWorker1.addEventListener('connect', this._onconnect.SharedWorkerGlobalScope);
79
+ });
80
+ }
81
+ if (this._target instanceof Worker) this._target.addEventListener('message', listener, this._options.message);
82
+ if ('undefined' != typeof DedicatedWorkerGlobalScope && this._target instanceof DedicatedWorkerGlobalScope) this._target.addEventListener('message', listener, this._options.message);
83
+ }
84
+ onremove(listener) {
85
+ if (this._target instanceof BroadcastChannel) this._target.removeEventListener('message', listener, this._options.message);
86
+ if (this._target instanceof WebSocket) this._target.onmessage = null;
87
+ if (this.getType() === TARGET_TYPE_STRINGS.Window) window.removeEventListener('message', listener, this._options.message);
88
+ if ('undefined' != typeof ServiceWorkerRegistration && this._target instanceof ServiceWorkerRegistration) navigator.serviceWorker.removeEventListener('message', listener, this._options.message);
89
+ if ('undefined' != typeof ServiceWorkerGlobalScope && this._target instanceof ServiceWorkerGlobalScope && this._onconnect.ServiceWorkerGlobalScope) this._target.removeEventListener('message', this._onconnect.ServiceWorkerGlobalScope, this._options.message);
90
+ if (this._target instanceof SharedWorker) this._target.port.removeEventListener('message', listener, this._options.message);
91
+ if ('undefined' != typeof SharedWorkerGlobalScope && this._target instanceof SharedWorkerGlobalScope) this._onconnect.SharedWorkerGlobalPort?.then((messagePort)=>{
92
+ messagePort.removeEventListener('message', listener, this._options.message);
93
+ }).catch(()=>{});
94
+ if (this._target instanceof Worker) this._target.removeEventListener('message', listener, this._options.message);
95
+ if ('undefined' != typeof DedicatedWorkerGlobalScope && this._target instanceof DedicatedWorkerGlobalScope) this._target.removeEventListener('message', listener, this._options.message);
96
+ }
97
+ postMessage(message, options) {
98
+ const { transfer, targetOrigin = '*' } = options ?? {};
99
+ if (this._target instanceof BroadcastChannel) this._target.postMessage(message);
100
+ if (this._target instanceof WebSocket) {
101
+ const target = this._target;
102
+ this._onconnect.WebSocketPort?.then((open)=>{
103
+ if (open) target.send(isSafeBufferSource(message) || message instanceof Blob ? message : String(message));
104
+ }).catch(()=>{});
105
+ }
106
+ if (this.getType() === TARGET_TYPE_STRINGS.Window && 'postMessage' in this._target) this._target.postMessage(message, {
107
+ targetOrigin,
108
+ transfer
109
+ });
110
+ if ('undefined' != typeof ServiceWorkerRegistration && this._target instanceof ServiceWorkerRegistration) this._target.active?.postMessage(message, {
111
+ transfer
112
+ });
113
+ if ('undefined' != typeof ServiceWorkerGlobalScope && this._target instanceof ServiceWorkerGlobalScope) this._target.clients.matchAll().then((clients)=>{
114
+ clients.forEach((client)=>client.postMessage(message, {
115
+ transfer
116
+ }));
117
+ }).catch(()=>{});
118
+ if (this._target instanceof SharedWorker) this._target.port.postMessage(message, {
119
+ transfer
120
+ });
121
+ if ('undefined' != typeof SharedWorkerGlobalScope && this._target instanceof SharedWorkerGlobalScope) this._onconnect.SharedWorkerGlobalPort?.then((messagePort)=>{
122
+ messagePort.postMessage(message, {
123
+ transfer
124
+ });
125
+ }).catch(()=>{});
126
+ if (this._target instanceof Worker) this._target.postMessage(message, {
127
+ transfer
128
+ });
129
+ if ('undefined' != typeof DedicatedWorkerGlobalScope && this._target instanceof DedicatedWorkerGlobalScope) this._target.postMessage(message, {
130
+ transfer
131
+ });
132
+ }
133
+ upset(options) {
134
+ this._options = {
135
+ ...this._options,
136
+ ...options
137
+ };
138
+ }
139
+ }
140
+ const src_RPCFactory = RPCFactory;
141
+ export { src_RPCFactory as default };
@@ -0,0 +1,4 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ declare const RPCProvider: FC<PropsWithChildren>;
3
+ export default RPCProvider;
4
+ //# sourceMappingURL=RPCProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RPCProvider.d.ts","sourceRoot":"","sources":["../src/RPCProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAkC,MAAM,OAAO,CAAA;AAW7E,QAAA,MAAM,WAAW,EAAE,EAAE,CAAC,iBAAiB,CA4CtC,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef } from "react";
3
+ import { RPCInstanceContext, TARGET_TYPE_STRINGS } from "./fields.js";
4
+ import { objectValues } from "./utils.js";
5
+ const RPCProvider = ({ children })=>{
6
+ const list = useRef(new Map());
7
+ const brodcastScope = useCallback((data, options)=>{
8
+ const { exclude, include, typein, typeout } = options ?? {};
9
+ if (list.current.size > 0) {
10
+ const scope = objectValues(TARGET_TYPE_STRINGS).filter((item)=>!exclude?.includes(item)).filter((item)=>!include || include.includes(item)).map(String);
11
+ list.current.forEach((group, item)=>{
12
+ if (scope.includes(item.getType()) && !typeout?.includes(group) && (!typein || typein.includes(group))) item.broadcast(data);
13
+ });
14
+ }
15
+ }, []);
16
+ const mount = useCallback((data, name)=>{
17
+ const newMap = new Map(list.current);
18
+ if (void 0 !== name) newMap.set(data, name);
19
+ else newMap.delete(data);
20
+ list.current = newMap;
21
+ }, []);
22
+ useEffect(()=>()=>{
23
+ list.current = new Map();
24
+ }, []);
25
+ return /*#__PURE__*/ jsx(RPCInstanceContext.Provider, {
26
+ value: {
27
+ brodcastScope,
28
+ mount
29
+ },
30
+ children: children
31
+ });
32
+ };
33
+ const src_RPCProvider = RPCProvider;
34
+ export { src_RPCProvider as default };
@@ -0,0 +1,13 @@
1
+ import { ActionFunType, BrodcastItem } from './RPCAction';
2
+ declare function createCtx<CTX extends Record<string, unknown>, ACTIONS extends Record<string, ActionFunType>, BRODCATS extends Record<string, BrodcastItem>>(factory: (ctx: Partial<CTX>) => ACTIONS, brodcast?: (ctx: Partial<CTX>) => BRODCATS): Readonly<{
3
+ actions: ACTIONS;
4
+ brodcasts: BRODCATS | undefined;
5
+ provider: (next: Partial<CTX>) => () => void;
6
+ }>;
7
+ declare function createService<CTX extends Record<string, unknown>>(): <ACTIONS extends Record<string, ActionFunType>, BRODCATS extends Record<string, BrodcastItem>>(factory: (ctx: Partial<CTX>) => ACTIONS, brodcast?: (ctx: Partial<CTX>) => BRODCATS) => Readonly<{
8
+ actions: ACTIONS;
9
+ brodcasts: BRODCATS | undefined;
10
+ provider: (next: Partial<CTX>) => () => void;
11
+ }>;
12
+ export { createCtx, createService };
13
+ //# sourceMappingURL=createCtx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createCtx.d.ts","sourceRoot":"","sources":["../src/createCtx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEzD,iBAAS,SAAS,CAChB,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAC7C,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EAC7C,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ;;;qBAiB3D,OAAO,CAAC,GAAG,CAAC;GAYrC;AAED,iBAAS,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAEtD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EAC7C,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EAE7C,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EACvC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ;;;;GAE7C;AAED,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA"}
@@ -0,0 +1,33 @@
1
+ function createCtx(factory, brodcast) {
2
+ const ctx = {
3
+ current: null
4
+ };
5
+ const proxyCtx = new Proxy({}, {
6
+ get (_, key) {
7
+ return ctx.current ? Reflect.get(ctx.current, key) : void 0;
8
+ },
9
+ set () {
10
+ throw new Error('ctx is readonly');
11
+ }
12
+ });
13
+ const actions = factory(proxyCtx);
14
+ const brodcasts = brodcast?.(proxyCtx);
15
+ const provider = (next)=>{
16
+ ctx.current = {
17
+ ...ctx.current ?? {},
18
+ ...next
19
+ };
20
+ return ()=>{
21
+ ctx.current = null;
22
+ };
23
+ };
24
+ return Object.freeze({
25
+ actions,
26
+ brodcasts,
27
+ provider
28
+ });
29
+ }
30
+ function createService() {
31
+ return (factory, brodcast)=>createCtx(factory, brodcast);
32
+ }
33
+ export { createCtx, createService };
@@ -0,0 +1,27 @@
1
+ import RPCAction, { RequestOptions } from './RPCAction';
2
+ import RPCFactory from './RPCFactory';
3
+ import { ValueOf } from './utils';
4
+ export declare const RPCInstanceContext: import("react").Context<RPCInstanceContextIns>;
5
+ export declare const TARGET_TYPE_STRINGS: Readonly<{
6
+ BroadcastChannel: "[object BroadcastChannel]";
7
+ WebSocket: "[object WebSocket]";
8
+ Window: "[object Window]";
9
+ ServiceWorkerRegistration: "[object ServiceWorkerRegistration]";
10
+ ServiceWorkerGlobalScope: "[object ServiceWorkerGlobalScope]";
11
+ SharedWorker: "[object SharedWorker]";
12
+ SharedWorkerGlobalScope: "[object SharedWorkerGlobalScope]";
13
+ Worker: "[object Worker]";
14
+ DedicatedWorkerGlobalScope: "[object DedicatedWorkerGlobalScope]";
15
+ }>;
16
+ export interface RPCInstanceContextIns {
17
+ brodcastScope?: <T>(data: RequestOptions<T>, options?: ScopeProps) => void;
18
+ mount?: (item: RPCItem, name?: string) => void;
19
+ }
20
+ export type RPCItem = Pick<RPCAction, 'broadcast'> & Pick<RPCFactory, 'getType'>;
21
+ export type ScopeProps = {
22
+ exclude?: Array<ValueOf<typeof TARGET_TYPE_STRINGS>>;
23
+ include?: Array<ValueOf<typeof TARGET_TYPE_STRINGS>>;
24
+ typein?: string[];
25
+ typeout?: string[];
26
+ };
27
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,UAAU,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEjC,eAAO,MAAM,kBAAkB,gDAA2C,CAAA;AAC1E,eAAO,MAAM,mBAAmB;;;;;;;;;;EAU9B,CAAA;AAEF,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;IAC1E,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/C;AAED,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;AAEhF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAA;IACpD,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAA;IACpD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB,CAAA"}
package/dist/fields.js ADDED
@@ -0,0 +1,14 @@
1
+ import { createContext } from "react";
2
+ const RPCInstanceContext = createContext({});
3
+ const TARGET_TYPE_STRINGS = Object.freeze({
4
+ BroadcastChannel: '[object BroadcastChannel]',
5
+ WebSocket: '[object WebSocket]',
6
+ Window: '[object Window]',
7
+ ServiceWorkerRegistration: '[object ServiceWorkerRegistration]',
8
+ ServiceWorkerGlobalScope: '[object ServiceWorkerGlobalScope]',
9
+ SharedWorker: '[object SharedWorker]',
10
+ SharedWorkerGlobalScope: '[object SharedWorkerGlobalScope]',
11
+ Worker: '[object Worker]',
12
+ DedicatedWorkerGlobalScope: '[object DedicatedWorkerGlobalScope]'
13
+ });
14
+ export { RPCInstanceContext, TARGET_TYPE_STRINGS };
@@ -0,0 +1,34 @@
1
+ import { ActionRecord, DecoratorContext } from './RPCDecorator';
2
+ declare const useRPC: <EVENT extends ActionRecord, CONSUME extends ActionRecord>({ init, ...ops }: RPCHooksOptions<EVENT, CONSUME>) => Readonly<{
3
+ rpc: Readonly<Omit<Readonly<Omit<import("./RPCAction").default, "on" | "request"> & Pick<import("./RPCFactory").default<import("./RPCFactory").TargetType>, "getType" | "upset"> & {
4
+ request: <K extends keyof CONSUME>(...args: Parameters<CONSUME[K]> extends [] ? [keyname: K, reqops?: (Parameters<CONSUME[K]> extends [] ? Omit<import("./RPCAction").RequestOptions, "payload"> & {
5
+ payload?: never;
6
+ } : StructuredSerializeOptions & {
7
+ targetOrigin?: string;
8
+ } & {
9
+ payload?: Parameters<CONSUME[K]>[0] | undefined;
10
+ retry?: number;
11
+ } & {
12
+ payload: Parameters<CONSUME[K]>[0];
13
+ }) | undefined] : [keyname: K, reqops: Parameters<CONSUME[K]> extends [] ? Omit<import("./RPCAction").RequestOptions, "payload"> & {
14
+ payload?: never;
15
+ } : StructuredSerializeOptions & {
16
+ targetOrigin?: string;
17
+ } & {
18
+ payload?: Parameters<CONSUME[K]>[0] | undefined;
19
+ retry?: number;
20
+ } & {
21
+ payload: Parameters<CONSUME[K]>[0];
22
+ }]) => Promise<ReturnType<CONSUME[K]>>;
23
+ }>, "destroy">>;
24
+ mount: (name: string) => void;
25
+ connected: boolean;
26
+ brodcastScope: <T>(data: import("./RPCAction").RequestOptions<T>, options?: import("./fields").ScopeProps) => void;
27
+ }>;
28
+ export default useRPC;
29
+ interface RPCHooksOptions<EVENT extends ActionRecord, CONSUME extends ActionRecord> extends DecoratorContext<EVENT, CONSUME> {
30
+ init: () => TargetInit | Promise<TargetInit>;
31
+ name?: string;
32
+ }
33
+ type TargetInit = BroadcastChannel | ServiceWorkerRegistration | SharedWorker | WebSocket | Window | Worker | HTMLIFrameElement | null;
34
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAqB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAI7E,QAAA,MAAM,MAAM,GAAI,KAAK,SAAS,YAAY,EAAE,OAAO,SAAS,YAAY,EAAE,kBAGvE,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;kBAoDvB,MAAM;;;EAkDhB,CAAA;AAED,eAAe,MAAM,CAAA;AAErB,UAAU,eAAe,CACvB,KAAK,SAAS,YAAY,EAC1B,OAAO,SAAS,YAAY,CAC5B,SAAQ,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC;IACxC,IAAI,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAOD,KAAK,UAAU,GACX,gBAAgB,GAChB,yBAAyB,GACzB,YAAY,GACZ,SAAS,GACT,MAAM,GACN,MAAM,GACN,iBAAiB,GACjB,IAAI,CAAA"}
package/dist/hooks.js ADDED
@@ -0,0 +1,82 @@
1
+ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
2
+ import RPCDecorator from "./RPCDecorator.js";
3
+ import { RPCInstanceContext } from "./fields.js";
4
+ const disableKey = [
5
+ 'destroy'
6
+ ];
7
+ const useRPC = ({ init, ...ops })=>{
8
+ const { mount, brodcastScope = ()=>{} } = useContext(RPCInstanceContext);
9
+ const [connected, setConnected] = useState(false);
10
+ const initRef = useRef(init);
11
+ const opsRef = useRef(ops);
12
+ const decoratorRef = useRef(null);
13
+ const processRef = useRef(Promise.resolve(null));
14
+ const isRPCResult = useCallback((data, key)=>null !== data && key in data, []);
15
+ const rpcIns = useMemo(()=>new Proxy({}, {
16
+ get (_, key) {
17
+ const decorator = decoratorRef.current;
18
+ const keyname = key.toString();
19
+ if (decorator && !disableKey.map(String).includes(keyname) && isRPCResult(decorator, keyname)) return decorator[keyname];
20
+ throw new Error(`outof decorator: ${key.toString()}`);
21
+ },
22
+ has (_, key) {
23
+ const decorator = decoratorRef.current;
24
+ const keyname = key.toString();
25
+ return null !== decorator && !disableKey.map(String).includes(keyname) && isRPCResult(decorator, keyname);
26
+ },
27
+ set () {
28
+ throw new Error('decorator is readonly');
29
+ }
30
+ }), [
31
+ decoratorRef,
32
+ isRPCResult
33
+ ]);
34
+ const mounHandle = useCallback((name)=>{
35
+ if (decoratorRef.current) mount?.(decoratorRef.current, name);
36
+ }, [
37
+ mount
38
+ ]);
39
+ useEffect(()=>{
40
+ const { config, name = '', ...opConfig } = opsRef.current;
41
+ processRef.current = processRef.current.then(initRef.current).then((tar)=>{
42
+ const result = RPCDecorator(tar instanceof HTMLIFrameElement ? tar.contentWindow : tar, {
43
+ ...opConfig,
44
+ config: {
45
+ ...config,
46
+ onConnect () {
47
+ config?.onConnect?.();
48
+ setConnected(true);
49
+ },
50
+ onDisconnect () {
51
+ config?.onDisconnect?.();
52
+ setConnected(false);
53
+ }
54
+ }
55
+ });
56
+ decoratorRef.current = result;
57
+ mount?.(result, name);
58
+ return result;
59
+ });
60
+ return ()=>{
61
+ processRef.current = processRef.current.then((result)=>{
62
+ setConnected(false);
63
+ if (result) {
64
+ mount?.(result);
65
+ result.destroy();
66
+ }
67
+ decoratorRef.current = null;
68
+ return null;
69
+ });
70
+ };
71
+ }, [
72
+ mount
73
+ ]);
74
+ return Object.freeze({
75
+ rpc: rpcIns,
76
+ mount: mounHandle,
77
+ connected,
78
+ brodcastScope
79
+ });
80
+ };
81
+ const hooks = useRPC;
82
+ export { hooks as default };
@@ -0,0 +1,6 @@
1
+ export * from './createCtx';
2
+ export { TARGET_TYPE_STRINGS } from './fields';
3
+ export { default as useRPC } from './hooks';
4
+ export { default as RPCDecorator } from './RPCDecorator';
5
+ export { default as RPCProvider } from './RPCProvider';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { TARGET_TYPE_STRINGS } from "./fields.js";
2
+ import hooks from "./hooks.js";
3
+ import RPCDecorator from "./RPCDecorator.js";
4
+ import RPCProvider from "./RPCProvider.js";
5
+ export * from "./createCtx.js";
6
+ export { RPCDecorator, RPCProvider, TARGET_TYPE_STRINGS, hooks as useRPC };
@@ -0,0 +1,5 @@
1
+ export declare const isKey: <T extends Record<string, unknown>>(key: unknown, data: T) => key is keyof T;
2
+ export declare const isPropertyKey: (value: unknown) => value is PropertyKey;
3
+ export declare const objectValues: <T extends object, V = ValueOf<T>>(obj: T) => V[];
4
+ export type ValueOf<T> = T[keyof T];
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,OAAO,EAAE,MAAM,CAAC,KAAG,GAAG,IAAI,MAAM,CAC3D,CAAA;AAEnC,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WACD,CAAA;AAEvD,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAA2B,CAAC,EAAE,CAAA;AAEnG,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
package/dist/utils.js ADDED
@@ -0,0 +1,8 @@
1
+ const isKey = (key, data)=>isPropertyKey(key) && key in data;
2
+ const isPropertyKey = (value)=>[
3
+ 'number',
4
+ 'string',
5
+ 'symbol'
6
+ ].includes(typeof value);
7
+ const objectValues = (obj)=>Object.values(obj);
8
+ export { isKey, isPropertyKey, objectValues };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@event-chat/rpc",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "keywords": [],
17
+ "license": "MIT",
18
+ "devDependencies": {
19
+ "@rslib/core": "^0.18.4",
20
+ "@rstest/core": "^0.7.1",
21
+ "@rstest/coverage-istanbul": "^0.1.3",
22
+ "@storybook/addon-docs": "^10.1.4",
23
+ "@storybook/addon-onboarding": "^10.1.4",
24
+ "@storybook/react": "^10.1.4",
25
+ "@testing-library/jest-dom": "^6.9.1",
26
+ "@testing-library/react": "^16.3.0",
27
+ "jsdom": "^26.1.0",
28
+ "storybook": "^10.1.4",
29
+ "storybook-addon-rslib": "^3.1.0",
30
+ "storybook-react-rsbuild": "^3.1.0",
31
+ "tsd": "^0.33.0",
32
+ "typescript-eslint": "^8.48.0"
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=18.0.0",
36
+ "react-dom": ">=18.0.0"
37
+ },
38
+ "scripts": {
39
+ "build": "rslib build",
40
+ "build:storybook": "storybook build",
41
+ "dev": "rslib build --watch",
42
+ "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx}\"",
43
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx}\"",
44
+ "lint": "eslint \"src/**/*.{ts,tsx,js,jsx}\"",
45
+ "lint:fix": "eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
46
+ "storybook": "storybook dev",
47
+ "test": "rstest",
48
+ "test:all": "tsd && rstest --coverage",
49
+ "test:coverage": "rstest --coverage",
50
+ "test:type": "tsd"
51
+ }
52
+ }