@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 +21 -0
- package/README.md +23 -0
- package/dist/RPCAction.d.ts +42 -0
- package/dist/RPCAction.d.ts.map +1 -0
- package/dist/RPCAction.js +189 -0
- package/dist/RPCDecorator.d.ts +21 -0
- package/dist/RPCDecorator.d.ts.map +1 -0
- package/dist/RPCDecorator.js +57 -0
- package/dist/RPCFactory.d.ts +22 -0
- package/dist/RPCFactory.d.ts.map +1 -0
- package/dist/RPCFactory.js +141 -0
- package/dist/RPCProvider.d.ts +4 -0
- package/dist/RPCProvider.d.ts.map +1 -0
- package/dist/RPCProvider.js +34 -0
- package/dist/createCtx.d.ts +13 -0
- package/dist/createCtx.d.ts.map +1 -0
- package/dist/createCtx.js +33 -0
- package/dist/fields.d.ts +27 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +14 -0
- package/dist/hooks.d.ts +34 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +82 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +8 -0
- package/package.json +52 -0
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 @@
|
|
|
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 };
|
package/dist/fields.d.ts
ADDED
|
@@ -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 };
|
package/dist/hooks.d.ts
ADDED
|
@@ -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 };
|
package/dist/index.d.ts
ADDED
|
@@ -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 };
|
package/dist/utils.d.ts
ADDED
|
@@ -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
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
|
+
}
|