@wishcore/wish-rpc 0.6.13 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,37 @@
1
1
  export interface MethodMap {
2
2
  [endpoint: string]: any;
3
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
+ }
4
35
  export interface RequestMessage {
5
36
  op: string;
6
37
  args?: any[];
@@ -18,11 +49,22 @@ export interface ResponseMessage {
18
49
  end?: number;
19
50
  push?: number;
20
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;
21
62
  }
22
63
  export interface RequestMap {
23
64
  [clientId: string]: {
24
- [requestId: string]: any;
65
+ [requestId: string]: RequestContext;
25
66
  };
26
67
  }
27
68
  export * from './rpc-client';
28
69
  export * from './rpc-server';
70
+ export * from './method-registry';
package/dist/src/index.js CHANGED
@@ -12,4 +12,5 @@ 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("./method-registry"), exports);
15
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AA+BA,+CAA6B;AAC7B,+CAA6B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AA+EA,+CAA6B;AAC7B,+CAA6B;AAC7B,oDAAkC"}
@@ -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 @@
1
+ {"version":3,"file":"method-registry.js","sourceRoot":"","sources":["../../src/method-registry.ts"],"names":[],"mappings":"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"}
@@ -1,39 +1,91 @@
1
+ import { MethodMap, RequestMessage, EndpointConfig, EndpointHandler, BaseEndpointConfig } from './index';
1
2
  /**
2
- * The idea is to be able to easily register methods/functions to be called
3
- * remotely, to enable access control and return data directly, asynchronously or
4
- * opening a stream which delivers data for long running tasks or large data
5
- * transfers.
3
+ * RPC Server for registering and handling remote procedure calls.
6
4
  *
7
- * When registerCore is run with the wish core instance, it reads all objects
8
- * that have a property named "_". It scans those properties for properties
9
- * which have a descriptor property with the same name prepended with "_".
5
+ * Supports both modern endpoint registration and legacy underscore-prefixed methods.
6
+ * Provides real-time communication with support for streaming, events, and async operations.
10
7
  *
11
- * Sub modules are not implemented yet, but an example should look like the
12
- * relay, module below.
8
+ * Type-safe endpoint registration with custom configuration:
9
+ * ```typescript
10
+ * interface ReasonEndpoint extends BaseEndpointConfig {
11
+ * validateArgs?: (args: any[], context: any) => boolean;
12
+ * permissions?: string[];
13
+ * rateLimit?: number;
14
+ * }
13
15
  *
14
- * Ex.
15
- * rpc.insertMethods({
16
- * _doThis: {
17
- * doc: "This function does this, and emits progress each second and end.",
18
- * },
19
- * doThis: function(req,res) {
20
- * ...
21
- * },
22
- * _relay: {
23
- * doc: "A bunch of relay related functions"
24
- * },
25
- * relay: {
26
- * _test: {},
27
- * test: function() {},
28
- * _list: {},
29
- * list: function() {}
30
- * }
31
- * });
16
+ * const server = new Server<ReasonEndpoint>();
17
+ * server.endpoint('user.get', {
18
+ * doc: 'Get user by ID',
19
+ * args: 'userId: string',
20
+ * validateArgs: (args, context) => validateUserArgs(args),
21
+ * permissions: ['user', 'admin'],
22
+ * rateLimit: 100
23
+ * }, async (req, res) => {
24
+ * const user = await getUserById(req.args[0]);
25
+ * res.send(user);
26
+ * });
27
+ * ```
28
+ *
29
+ * Default usage (backwards compatible):
30
+ * ```typescript
31
+ * const server = new Server();
32
+ * server.endpoint('user.get', {
33
+ * doc: 'Get user by ID',
34
+ * args: 'userId: string',
35
+ * validate: (args, context) => validateUserArgs(args)
36
+ * }, async (req, res) => {
37
+ * const user = await getUserById(req.args[0]);
38
+ * res.send(user);
39
+ * });
40
+ * ```
41
+ *
42
+ * Legacy method registration (backwards compatibility):
43
+ * ```typescript
44
+ * server.insertMethods({
45
+ * _getUser: {
46
+ * doc: 'Get user by ID',
47
+ * args: 'userId: string'
48
+ * },
49
+ * getUser: async (req, res) => {
50
+ * const user = await getUserById(req.args[0]);
51
+ * res.send(user);
52
+ * }
53
+ * });
54
+ * ```
55
+ *
56
+ * Accessing metadata for custom validation and introspection:
57
+ * ```typescript
58
+ * // Get method metadata and check for custom validation properties
59
+ * const meta = server.getMethodMeta('user.get');
60
+ * if (meta?.validateArgs) {
61
+ * const isValid = meta.validateArgs(args, context);
62
+ * }
63
+ * if (meta?.schema) {
64
+ * const result = meta.schema.safeParse(args); // e.g., Zod
65
+ * }
66
+ * if (meta?.validator) {
67
+ * meta.validator.validate(args); // e.g., Joi
68
+ * }
69
+ *
70
+ * // Generate documentation from all methods
71
+ * const allMeta = server.getAllMethodMeta();
72
+ * for (const [name, config] of Object.entries(allMeta)) {
73
+ * console.log(`${name}: ${config.doc || 'No documentation'}`);
74
+ * if (config.args) console.log(` Args: ${config.args}`);
75
+ * }
76
+ * ```
77
+ *
78
+ * Features:
79
+ * - Type-safe endpoint registration with TypeScript
80
+ * - Streaming support for large data transfers
81
+ * - Real-time events and signals
82
+ * - Request validation and error handling
83
+ * - Session management and cleanup
84
+ * - Method discovery and documentation
85
+ * - Metadata access for external validation and introspection
32
86
  */
33
- import { MethodMap, RequestMessage } from './index';
34
- export declare class Server {
35
- private modules;
36
- private methods;
87
+ export declare class Server<T extends BaseEndpointConfig = EndpointConfig> {
88
+ private registry;
37
89
  private requests;
38
90
  private clientIdCounter;
39
91
  /** internal request id counter */
@@ -42,14 +94,60 @@ export declare class Server {
42
94
  private invokeId;
43
95
  constructor(methods?: MethodMap);
44
96
  destroy(): void;
45
- insertMethods(o: any): void;
46
- addMethods(path: string, o: MethodMap): void;
47
- listMethods(args: any, context: any, cb: any): void;
97
+ /**
98
+ * Legacy method for backwards compatibility
99
+ * Registers methods using the old underscore-prefixed structure
100
+ */
101
+ insertMethods(methodMap: MethodMap): void;
102
+ /**
103
+ * Lists all available methods and their configurations
104
+ */
105
+ listMethods(args: any[], context: any, callback: (error: any, result?: any) => void): void;
106
+ /**
107
+ * Cleanup when a client goes offline
108
+ */
48
109
  clientOffline(clientId: number): void;
110
+ /**
111
+ * Close all active connections and cleanup
112
+ */
49
113
  closeAll(): void;
50
- parse(msg: RequestMessage, respond: any, context: any, clientId: number): void;
114
+ /**
115
+ * Safely cleanup a specific request
116
+ */
117
+ private cleanupRequest;
118
+ /**
119
+ * Parse and handle incoming RPC messages
120
+ */
121
+ parse(msg: RequestMessage, respond: (data: any) => void, context: any, clientId: number): void;
51
122
  emit(op: string, data: any): void;
52
- invokeRaw(msg: RequestMessage, respond: any, context: any, clientId: any): void;
123
+ /**
124
+ * Process raw RPC invocation
125
+ */
126
+ invokeRaw(msg: RequestMessage, respond: (data: any) => void, context: any, clientId: number): void;
127
+ /**
128
+ * Handle push (streaming data) messages
129
+ */
130
+ private handlePushMessage;
131
+ /**
132
+ * Handle signal (streaming) messages
133
+ */
134
+ private handleSignalMessage;
135
+ /**
136
+ * Create response handlers for signal messages
137
+ */
138
+ private createSignalResponseHandlers;
139
+ /**
140
+ * Handle event messages (no response expected)
141
+ */
142
+ private handleEventMessage;
143
+ /**
144
+ * Handle regular RPC requests
145
+ */
146
+ private handleRegularRequest;
147
+ /**
148
+ * Create response handlers for regular requests
149
+ */
150
+ private createRegularResponseHandlers;
53
151
  close(clientId: number): void;
54
152
  open(): number;
55
153
  /**
@@ -58,10 +156,16 @@ export declare class Server {
58
156
  * @param config - Configuration object with doc, args, validate, etc.
59
157
  * @param handler - The endpoint handler function
60
158
  */
61
- endpoint(name: string, config: {
62
- doc?: string;
63
- args?: string;
64
- validate?: (args: any[], context: any) => any;
65
- validateResult?: (result: any) => any;
66
- }, handler: (req: any, res: any, context?: any) => any): void;
159
+ endpoint(name: string, config: T, handler: EndpointHandler): void;
160
+ /**
161
+ * Get metadata for a specific method (including all custom properties)
162
+ * @param name - Method name
163
+ * @returns The complete method configuration or undefined if not found
164
+ */
165
+ getMethodMeta(name: string): T | undefined;
166
+ /**
167
+ * Get metadata for all registered methods
168
+ * @returns Object containing all method configurations
169
+ */
170
+ getAllMethodMeta(): Record<string, T>;
67
171
  }