@wishcore/wish-rpc 0.6.14 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +71 -0
- package/dist/{src/index.js → index.js} +2 -0
- package/dist/method-registry.d.ts +60 -0
- package/dist/method-registry.js +135 -0
- package/dist/rpc-client.d.ts +59 -0
- package/dist/rpc-client.js +176 -0
- package/dist/rpc-protocol.d.ts +35 -0
- package/dist/rpc-protocol.js +43 -0
- package/dist/rpc-server.d.ts +171 -0
- package/dist/rpc-server.js +523 -0
- package/package.json +9 -4
- package/dist/src/index.d.ts +0 -28
- package/dist/src/index.js.map +0 -1
- package/dist/src/rpc-client.d.ts +0 -16
- package/dist/src/rpc-client.js +0 -109
- package/dist/src/rpc-client.js.map +0 -1
- package/dist/src/rpc-server.d.ts +0 -67
- package/dist/src/rpc-server.js +0 -443
- package/dist/src/rpc-server.js.map +0 -1
- package/dist/test/stream-clean.spec.d.ts +0 -1
- package/dist/test/stream-clean.spec.js +0 -146
- package/dist/test/stream-clean.spec.js.map +0 -1
- package/dist/test/stream.spec.d.ts +0 -1
- package/dist/test/stream.spec.js +0 -113
- package/dist/test/stream.spec.js.map +0 -1
- package/dist/test/test-client.d.ts +0 -1
- package/dist/test/test-client.js +0 -88
- package/dist/test/test-client.js.map +0 -1
- package/dist/test/test-rpc-acl.d.ts +0 -1
- package/dist/test/test-rpc-acl.js +0 -103
- package/dist/test/test-rpc-acl.js.map +0 -1
- package/dist/test/test-rpc.spec.d.ts +0 -1
- package/dist/test/test-rpc.spec.js +0 -143
- package/dist/test/test-rpc.spec.js.map +0 -1
- package/dist/test/test-session.spec.d.ts +0 -1
- package/dist/test/test-session.spec.js +0 -46
- package/dist/test/test-session.spec.js.map +0 -1
- package/dist/test/test-stream.d.ts +0 -1
- package/dist/test/test-stream.js +0 -253
- package/dist/test/test-stream.js.map +0 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface MethodMap {
|
|
2
|
+
[endpoint: string]: any;
|
|
3
|
+
}
|
|
4
|
+
export interface EndpointConfig {
|
|
5
|
+
doc?: string;
|
|
6
|
+
args?: string;
|
|
7
|
+
validate?: (args: any[], context: any) => any;
|
|
8
|
+
validateResult?: (result: any) => any;
|
|
9
|
+
fullname?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
export interface BaseEndpointConfig {
|
|
13
|
+
doc?: string;
|
|
14
|
+
args?: string;
|
|
15
|
+
fullname?: string;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
export interface EndpointHandler {
|
|
19
|
+
(req: RpcRequest, res: RpcResponse, context?: any): any;
|
|
20
|
+
}
|
|
21
|
+
export interface RpcRequest {
|
|
22
|
+
args: any[];
|
|
23
|
+
id?: number;
|
|
24
|
+
op?: string;
|
|
25
|
+
context?: any;
|
|
26
|
+
end?: any;
|
|
27
|
+
send?: any;
|
|
28
|
+
}
|
|
29
|
+
export interface RpcResponse {
|
|
30
|
+
send(data?: any): void;
|
|
31
|
+
emit(data?: any): void;
|
|
32
|
+
error(data?: any): void;
|
|
33
|
+
close(data?: any): void;
|
|
34
|
+
}
|
|
35
|
+
export interface RequestMessage {
|
|
36
|
+
op: string;
|
|
37
|
+
args?: any[];
|
|
38
|
+
id?: number;
|
|
39
|
+
push?: number;
|
|
40
|
+
sig?: number;
|
|
41
|
+
data?: any;
|
|
42
|
+
end?: number;
|
|
43
|
+
stream?: any;
|
|
44
|
+
}
|
|
45
|
+
export interface ResponseMessage {
|
|
46
|
+
ack?: number;
|
|
47
|
+
err?: number;
|
|
48
|
+
sig?: number;
|
|
49
|
+
end?: number;
|
|
50
|
+
push?: number;
|
|
51
|
+
data?: any;
|
|
52
|
+
fin?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface RequestContext {
|
|
55
|
+
id?: number;
|
|
56
|
+
op?: string;
|
|
57
|
+
args?: any[];
|
|
58
|
+
context?: any;
|
|
59
|
+
end?: any;
|
|
60
|
+
send?: any;
|
|
61
|
+
data?: any;
|
|
62
|
+
}
|
|
63
|
+
export interface RequestMap {
|
|
64
|
+
[clientId: string]: {
|
|
65
|
+
[requestId: string]: RequestContext;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export * from './rpc-client';
|
|
69
|
+
export * from './rpc-server';
|
|
70
|
+
export * from './rpc-protocol';
|
|
71
|
+
export * from './method-registry';
|
|
@@ -12,4 +12,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
__exportStar(require("./rpc-client"), exports);
|
|
14
14
|
__exportStar(require("./rpc-server"), exports);
|
|
15
|
+
__exportStar(require("./rpc-protocol"), exports);
|
|
16
|
+
__exportStar(require("./method-registry"), exports);
|
|
15
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EndpointConfig, EndpointHandler, MethodMap, BaseEndpointConfig } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* Manages RPC method registration and retrieval
|
|
4
|
+
*/
|
|
5
|
+
export declare class MethodRegistry<T extends BaseEndpointConfig = EndpointConfig> {
|
|
6
|
+
private modules;
|
|
7
|
+
private methods;
|
|
8
|
+
/**
|
|
9
|
+
* Register a new endpoint with modern syntax
|
|
10
|
+
*/
|
|
11
|
+
registerEndpoint(name: string, config: T, handler: EndpointHandler): void;
|
|
12
|
+
/**
|
|
13
|
+
* Register methods using legacy underscore-prefixed structure
|
|
14
|
+
*/
|
|
15
|
+
registerLegacyMethods(methodMap: MethodMap): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get all registered modules
|
|
18
|
+
*/
|
|
19
|
+
getModules(): Record<string, T>;
|
|
20
|
+
/**
|
|
21
|
+
* Get all registered methods
|
|
22
|
+
*/
|
|
23
|
+
getMethods(): Record<string, EndpointHandler>;
|
|
24
|
+
/**
|
|
25
|
+
* Get a specific method by name
|
|
26
|
+
*/
|
|
27
|
+
getMethod(name: string): EndpointHandler | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Get a specific module config by name
|
|
30
|
+
*/
|
|
31
|
+
getModule(name: string): T | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a method exists
|
|
34
|
+
*/
|
|
35
|
+
hasMethod(name: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get method listing excluding internal fields
|
|
38
|
+
*/
|
|
39
|
+
getMethodListing(): Record<string, Partial<T>>;
|
|
40
|
+
/**
|
|
41
|
+
* Recursively processes method map with underscore-prefixed descriptors
|
|
42
|
+
*/
|
|
43
|
+
private processMethodMap;
|
|
44
|
+
/**
|
|
45
|
+
* Registers a single method using legacy format
|
|
46
|
+
*/
|
|
47
|
+
private registerLegacyMethod;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a copy of config object excluding specified fields
|
|
50
|
+
*/
|
|
51
|
+
private copyConfig;
|
|
52
|
+
/**
|
|
53
|
+
* Checks if a key represents module metadata
|
|
54
|
+
*/
|
|
55
|
+
private isModuleMeta;
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a key is a descriptor (starts with underscore)
|
|
58
|
+
*/
|
|
59
|
+
private isDescriptor;
|
|
60
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MethodRegistry = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Manages RPC method registration and retrieval
|
|
6
|
+
*/
|
|
7
|
+
class MethodRegistry {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.modules = {};
|
|
10
|
+
this.methods = {};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Register a new endpoint with modern syntax
|
|
14
|
+
*/
|
|
15
|
+
registerEndpoint(name, config, handler) {
|
|
16
|
+
if (!name || typeof name !== 'string') {
|
|
17
|
+
throw new Error('Endpoint name must be a non-empty string');
|
|
18
|
+
}
|
|
19
|
+
if (typeof handler !== 'function') {
|
|
20
|
+
throw new Error('Endpoint handler must be a function');
|
|
21
|
+
}
|
|
22
|
+
this.modules[name] = Object.assign(Object.assign({}, config), { fullname: name });
|
|
23
|
+
this.methods[name] = handler;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Register methods using legacy underscore-prefixed structure
|
|
27
|
+
*/
|
|
28
|
+
registerLegacyMethods(methodMap) {
|
|
29
|
+
this.processMethodMap('', methodMap);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get all registered modules
|
|
33
|
+
*/
|
|
34
|
+
getModules() {
|
|
35
|
+
return Object.assign({}, this.modules);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get all registered methods
|
|
39
|
+
*/
|
|
40
|
+
getMethods() {
|
|
41
|
+
return Object.assign({}, this.methods);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific method by name
|
|
45
|
+
*/
|
|
46
|
+
getMethod(name) {
|
|
47
|
+
return this.methods[name];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get a specific module config by name
|
|
51
|
+
*/
|
|
52
|
+
getModule(name) {
|
|
53
|
+
return this.modules[name];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a method exists
|
|
57
|
+
*/
|
|
58
|
+
hasMethod(name) {
|
|
59
|
+
return name in this.methods;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get method listing excluding internal fields
|
|
63
|
+
*/
|
|
64
|
+
getMethodListing() {
|
|
65
|
+
const result = {};
|
|
66
|
+
const excludeFields = new Set(['fullname']);
|
|
67
|
+
for (const methodName in this.modules) {
|
|
68
|
+
const config = this.modules[methodName];
|
|
69
|
+
result[methodName] = this.copyConfig(config, excludeFields);
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Recursively processes method map with underscore-prefixed descriptors
|
|
75
|
+
*/
|
|
76
|
+
processMethodMap(path, methodMap) {
|
|
77
|
+
const prefix = path ? `${path}.` : '';
|
|
78
|
+
for (const key in methodMap) {
|
|
79
|
+
if (this.isModuleMeta(key) || this.isDescriptor(key)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const descriptor = methodMap[`_${key}`];
|
|
83
|
+
if (!descriptor) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const item = methodMap[key];
|
|
87
|
+
if (typeof item === 'function') {
|
|
88
|
+
this.registerLegacyMethod(prefix, key, descriptor, item);
|
|
89
|
+
}
|
|
90
|
+
else if (typeof item === 'object' && item !== null) {
|
|
91
|
+
this.processMethodMap(`${prefix}${key}`, item);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Registers a single method using legacy format
|
|
97
|
+
*/
|
|
98
|
+
registerLegacyMethod(prefix, name, descriptor, handler) {
|
|
99
|
+
const fullName = prefix + (descriptor.name || name);
|
|
100
|
+
const config = Object.assign(Object.assign({}, descriptor), { fullname: fullName });
|
|
101
|
+
try {
|
|
102
|
+
this.modules[fullName] = config;
|
|
103
|
+
this.methods[fullName] = handler;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error(`Failed to register method ${fullName}:`, error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Creates a copy of config object excluding specified fields
|
|
111
|
+
*/
|
|
112
|
+
copyConfig(source, excludeFields) {
|
|
113
|
+
const result = {};
|
|
114
|
+
for (const key in source) {
|
|
115
|
+
if (!excludeFields.has(key) && Object.prototype.hasOwnProperty.call(source, key)) {
|
|
116
|
+
result[key] = source[key];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Checks if a key represents module metadata
|
|
123
|
+
*/
|
|
124
|
+
isModuleMeta(key) {
|
|
125
|
+
return key === '_';
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Checks if a key is a descriptor (starts with underscore)
|
|
129
|
+
*/
|
|
130
|
+
isDescriptor(key) {
|
|
131
|
+
return key.startsWith('_');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.MethodRegistry = MethodRegistry;
|
|
135
|
+
//# sourceMappingURL=method-registry.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ResponseMessage } from './index';
|
|
2
|
+
export declare class Client {
|
|
3
|
+
private write;
|
|
4
|
+
private requests;
|
|
5
|
+
private id;
|
|
6
|
+
private mtu;
|
|
7
|
+
constructor(write: (msg: ResponseMessage) => void, opts?: {
|
|
8
|
+
mtu: number;
|
|
9
|
+
});
|
|
10
|
+
destroy(): void;
|
|
11
|
+
messageReceived(msg: any): void;
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Use request() without callback instead - it now returns a Promise
|
|
14
|
+
*/
|
|
15
|
+
requestAsync(op: string, args: any[]): Promise<any>;
|
|
16
|
+
/**
|
|
17
|
+
* Make an RPC request.
|
|
18
|
+
*
|
|
19
|
+
* Without callback: Returns a Promise that resolves with the response.
|
|
20
|
+
* With callback: Returns request ID for streaming/signal subscriptions.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Promise style (recommended for one-shot requests)
|
|
24
|
+
* const result = await client.request('method', [arg1, arg2]);
|
|
25
|
+
*
|
|
26
|
+
* // Callback style (for streaming responses or signals)
|
|
27
|
+
* client.request('signals', [], (err, data, end) => {
|
|
28
|
+
* if (data) console.log('Signal:', data);
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
request(op: string, args: any[]): Promise<any>;
|
|
32
|
+
request(op: string, args: any[], cb: (err: any, data: any, end?: boolean) => any): number;
|
|
33
|
+
private requestWithCallback;
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to signals from the server.
|
|
36
|
+
*
|
|
37
|
+
* @param event - Signal event name to filter (e.g., 'document.changed'), or null for all signals
|
|
38
|
+
* @param callback - Called when a matching signal is received
|
|
39
|
+
* @param endpoint - RPC endpoint to subscribe to (default: 'signals')
|
|
40
|
+
* @returns Unsubscribe function
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Subscribe to specific signal
|
|
44
|
+
* const unsub = client.subscribe('document.changed', (data) => {
|
|
45
|
+
* console.log('Document changed:', data);
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Subscribe to all signals
|
|
49
|
+
* const unsub = client.subscribe(null, (event, data) => {
|
|
50
|
+
* console.log('Signal:', event, data);
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Unsubscribe when done
|
|
54
|
+
* unsub();
|
|
55
|
+
*/
|
|
56
|
+
subscribe(event: string | null, callback: (eventOrData: any, data?: any) => void, endpoint?: string): () => void;
|
|
57
|
+
send(id: number, data: any): void;
|
|
58
|
+
end(id: number): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Client = void 0;
|
|
13
|
+
class Client {
|
|
14
|
+
constructor(write, opts) {
|
|
15
|
+
this.write = write;
|
|
16
|
+
this.requests = {};
|
|
17
|
+
this.id = 0;
|
|
18
|
+
this.mtu = 65535;
|
|
19
|
+
if (opts && opts.mtu) {
|
|
20
|
+
this.mtu = opts.mtu;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
destroy() {
|
|
24
|
+
// Reject all pending requests with disconnect error
|
|
25
|
+
if (this.requests) {
|
|
26
|
+
for (const id of Object.keys(this.requests)) {
|
|
27
|
+
const request = this.requests[id];
|
|
28
|
+
if (request && typeof request.cb === 'function' && !request.canceled) {
|
|
29
|
+
request.cb.call(request.context, { code: 'DISCONNECTED', msg: 'Connection closed' }, null, true);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
this.requests = null;
|
|
34
|
+
this.write = null;
|
|
35
|
+
this.id = 0;
|
|
36
|
+
}
|
|
37
|
+
messageReceived(msg) {
|
|
38
|
+
//console.log("RpcClient received message", msg);
|
|
39
|
+
const end = !!(msg.err || msg.ack || msg.fin);
|
|
40
|
+
const id = msg.ack || msg.err || msg.sig || msg.fin;
|
|
41
|
+
const request = this.requests[id];
|
|
42
|
+
let retval;
|
|
43
|
+
if (request && typeof request.cb === 'function') {
|
|
44
|
+
let err;
|
|
45
|
+
if (msg.fin) {
|
|
46
|
+
// This request closed gracefully
|
|
47
|
+
request.cb.call(request.context, null, null, true);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
if (request.canceled) {
|
|
51
|
+
console.log('This request is canceled. Not calling the callback.');
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// all is good, call the callback function
|
|
55
|
+
err = msg.err ? msg.data : null;
|
|
56
|
+
retval = request.cb.call(request.context, err, msg.data, end);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (end) {
|
|
61
|
+
//console.log("deleting this request", id);
|
|
62
|
+
delete this.requests[id];
|
|
63
|
+
}
|
|
64
|
+
return retval;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* @deprecated Use request() without callback instead - it now returns a Promise
|
|
68
|
+
*/
|
|
69
|
+
requestAsync(op, args) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
return this.request(op, args);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
request(op, args, cb) {
|
|
75
|
+
// Promise mode when no callback provided
|
|
76
|
+
if (!cb) {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
this.requestWithCallback(op, args, (err, data) => {
|
|
79
|
+
if (err) {
|
|
80
|
+
reject(data);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
resolve(data);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Callback mode
|
|
89
|
+
return this.requestWithCallback(op, args, cb);
|
|
90
|
+
}
|
|
91
|
+
requestWithCallback(op, args, cb) {
|
|
92
|
+
this.id++;
|
|
93
|
+
const msg = { op };
|
|
94
|
+
if (Array.isArray(args)) {
|
|
95
|
+
msg.args = args;
|
|
96
|
+
}
|
|
97
|
+
msg.id = this.id;
|
|
98
|
+
this.requests[msg.id] = {
|
|
99
|
+
cb,
|
|
100
|
+
context: {
|
|
101
|
+
id: msg.id,
|
|
102
|
+
cancel: () => {
|
|
103
|
+
setTimeout(((id) => {
|
|
104
|
+
return () => {
|
|
105
|
+
if (this.requests[id]) {
|
|
106
|
+
console.log('rpc-client.js: timeout, the request has not been removed while it was canceled', id, this.requests[id]);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
})(msg.id), 1500);
|
|
110
|
+
this.requests[msg.id].canceled = true;
|
|
111
|
+
this.write({ end: msg.id });
|
|
112
|
+
},
|
|
113
|
+
emit: (data) => {
|
|
114
|
+
return this.write({ sig: msg.id, data: data });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
this.write(msg);
|
|
119
|
+
return msg.id;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Subscribe to signals from the server.
|
|
123
|
+
*
|
|
124
|
+
* @param event - Signal event name to filter (e.g., 'document.changed'), or null for all signals
|
|
125
|
+
* @param callback - Called when a matching signal is received
|
|
126
|
+
* @param endpoint - RPC endpoint to subscribe to (default: 'signals')
|
|
127
|
+
* @returns Unsubscribe function
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Subscribe to specific signal
|
|
131
|
+
* const unsub = client.subscribe('document.changed', (data) => {
|
|
132
|
+
* console.log('Document changed:', data);
|
|
133
|
+
* });
|
|
134
|
+
*
|
|
135
|
+
* // Subscribe to all signals
|
|
136
|
+
* const unsub = client.subscribe(null, (event, data) => {
|
|
137
|
+
* console.log('Signal:', event, data);
|
|
138
|
+
* });
|
|
139
|
+
*
|
|
140
|
+
* // Unsubscribe when done
|
|
141
|
+
* unsub();
|
|
142
|
+
*/
|
|
143
|
+
subscribe(event, callback, endpoint = 'signals') {
|
|
144
|
+
const requestId = this.request(endpoint, [], (err, signalData, end) => {
|
|
145
|
+
if (err || end)
|
|
146
|
+
return;
|
|
147
|
+
if (!signalData || !Array.isArray(signalData))
|
|
148
|
+
return;
|
|
149
|
+
const [signalEvent, ...args] = signalData;
|
|
150
|
+
if (event === null) {
|
|
151
|
+
// Pass all signals: callback(event, data)
|
|
152
|
+
callback(signalEvent, args.length === 1 ? args[0] : args);
|
|
153
|
+
}
|
|
154
|
+
else if (signalEvent === event) {
|
|
155
|
+
// Pass only matching signals: callback(data)
|
|
156
|
+
callback(args.length === 1 ? args[0] : args);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
// Return unsubscribe function
|
|
160
|
+
return () => {
|
|
161
|
+
if (this.requests && this.requests[requestId]) {
|
|
162
|
+
this.requests[requestId].canceled = true;
|
|
163
|
+
this.write({ end: requestId });
|
|
164
|
+
delete this.requests[requestId];
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
send(id, data) {
|
|
169
|
+
this.write({ push: id, data });
|
|
170
|
+
}
|
|
171
|
+
end(id) {
|
|
172
|
+
this.write({ end: id });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.Client = Client;
|
|
176
|
+
//# sourceMappingURL=rpc-client.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Server } from './rpc-server';
|
|
2
|
+
import { EndpointConfig, EndpointHandler } from './index';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for P2P protocol handlers.
|
|
5
|
+
*
|
|
6
|
+
* Subclass to define endpoints and peer lifecycle:
|
|
7
|
+
*
|
|
8
|
+
* ```typescript
|
|
9
|
+
* class NotesProtocol extends Protocol {
|
|
10
|
+
* constructor() {
|
|
11
|
+
* super();
|
|
12
|
+
* this.endpoint('list', { doc: 'List notes' }, async (req, res) => {
|
|
13
|
+
* res.send(myNotes);
|
|
14
|
+
* });
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* online(peer: Peer, client: Client) {
|
|
18
|
+
* console.log('Peer online:', peer.toString());
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Generic parameters allow typed peer/client when re-exported from wish-sdk:
|
|
24
|
+
* - P = Peer type (defaults to any for wish-rpc standalone use)
|
|
25
|
+
* - C = Client type (defaults to any for wish-rpc standalone use)
|
|
26
|
+
*/
|
|
27
|
+
export declare class Protocol<P = any, C = any> {
|
|
28
|
+
server: Server<EndpointConfig>;
|
|
29
|
+
/** Register an RPC endpoint */
|
|
30
|
+
endpoint(name: string, config: EndpointConfig, handler: EndpointHandler): void;
|
|
31
|
+
/** Called when a peer comes online */
|
|
32
|
+
online(peer: P, client: C): void;
|
|
33
|
+
/** Called when a peer goes offline */
|
|
34
|
+
offline(peer: P): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Protocol = void 0;
|
|
4
|
+
const rpc_server_1 = require("./rpc-server");
|
|
5
|
+
/**
|
|
6
|
+
* Base class for P2P protocol handlers.
|
|
7
|
+
*
|
|
8
|
+
* Subclass to define endpoints and peer lifecycle:
|
|
9
|
+
*
|
|
10
|
+
* ```typescript
|
|
11
|
+
* class NotesProtocol extends Protocol {
|
|
12
|
+
* constructor() {
|
|
13
|
+
* super();
|
|
14
|
+
* this.endpoint('list', { doc: 'List notes' }, async (req, res) => {
|
|
15
|
+
* res.send(myNotes);
|
|
16
|
+
* });
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* online(peer: Peer, client: Client) {
|
|
20
|
+
* console.log('Peer online:', peer.toString());
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Generic parameters allow typed peer/client when re-exported from wish-sdk:
|
|
26
|
+
* - P = Peer type (defaults to any for wish-rpc standalone use)
|
|
27
|
+
* - C = Client type (defaults to any for wish-rpc standalone use)
|
|
28
|
+
*/
|
|
29
|
+
class Protocol {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.server = new rpc_server_1.Server();
|
|
32
|
+
}
|
|
33
|
+
/** Register an RPC endpoint */
|
|
34
|
+
endpoint(name, config, handler) {
|
|
35
|
+
this.server.endpoint(name, config, handler);
|
|
36
|
+
}
|
|
37
|
+
/** Called when a peer comes online */
|
|
38
|
+
online(peer, client) { }
|
|
39
|
+
/** Called when a peer goes offline */
|
|
40
|
+
offline(peer) { }
|
|
41
|
+
}
|
|
42
|
+
exports.Protocol = Protocol;
|
|
43
|
+
//# sourceMappingURL=rpc-protocol.js.map
|