@irpclib/irpc 1.0.0-beta.19 → 1.0.0-beta.21

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/call.d.ts CHANGED
@@ -1,44 +1,69 @@
1
- import { IRPCPayload } from "./types.js";
1
+ import { IRPCTransport } from "./transport.js";
2
+ import { IRPCCallConfig, IRPCData, IRPCPacketStream, IRPCPayload, IRPCStatus } from "./types.js";
3
+ import { IRPCReader } from "./reader.js";
2
4
 
3
5
  //#region src/call.d.ts
4
-
6
+ declare const DEFAULT_RETRY_MODE = "exponential";
7
+ declare const DEFAULT_RETRY_DELAY = 1000;
5
8
  /**
6
9
  * Represents an RPC call with promise-like behavior for handling asynchronous operations.
7
10
  * Each call has a unique identifier and manages its own resolution state.
8
11
  */
9
12
  declare class IRPCCall {
13
+ transport: IRPCTransport;
10
14
  payload: IRPCPayload;
11
- resolver: (value: unknown) => void;
12
- rejector: (reason?: Error) => void;
13
- timeout?: number | undefined;
15
+ options: IRPCCallConfig;
14
16
  /**
15
17
  * Unique identifier for this RPC call, generated using shortId().
16
18
  */
17
19
  id: string;
20
+ /**
21
+ * The status of the RPC call, indicating whether it is pending, resolved, or rejected.
22
+ */
23
+ status: IRPCStatus;
18
24
  /**
19
25
  * Flag indicating whether this call has been resolved or rejected.
20
26
  * Prevents multiple resolutions of the same call.
21
27
  */
22
28
  resolved: boolean;
29
+ /**
30
+ * The timestamp when the RPC call was started.
31
+ */
32
+ startedAt: number;
33
+ /**
34
+ * The timestamp when the RPC call was finished.
35
+ */
36
+ finishedAt?: number;
37
+ /**
38
+ * The value returned by the RPC call.
39
+ */
40
+ value?: unknown;
41
+ error?: Error;
42
+ private readonly timerId?;
43
+ private retries;
44
+ private retryReasons;
45
+ reader: IRPCReader<IRPCData>;
23
46
  /**
24
47
  * Creates a new IRPCCall instance.
48
+ * @param transport
25
49
  * @param payload - The RPC payload containing method and parameters
26
- * @param resolver - Function to resolve the associated promise with a value
27
- * @param rejector - Function to reject the associated promise with an error
50
+ * @param options - Options for the call, such as timeout, maxRetries, etc.
28
51
  */
29
- constructor(payload: IRPCPayload, resolver: (value: unknown) => void, rejector: (reason?: Error) => void, timeout?: number | undefined);
52
+ constructor(transport: IRPCTransport, payload: IRPCPayload, options: IRPCCallConfig);
53
+ enqueue(packet: IRPCPacketStream<IRPCData>): void;
30
54
  /**
31
55
  * Resolves the RPC call with the provided value.
32
56
  * If the call is already resolved, this method does nothing.
33
57
  * @param value - The value to resolve the promise with
34
58
  */
35
- resolve(value: unknown): void;
59
+ resolve(value: IRPCData): void;
36
60
  /**
37
61
  * Rejects the RPC call with the provided reason.
38
62
  * If the call is already resolved, this method does nothing.
39
63
  * @param reason - Optional error reason for rejecting the promise
64
+ * @param retriable - Flag indicating whether to retry the call
40
65
  */
41
- reject(reason?: Error): void;
66
+ reject(reason?: Error, retriable?: boolean): void;
42
67
  }
43
68
  //#endregion
44
- export { IRPCCall };
69
+ export { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall };
package/dist/call.js CHANGED
@@ -1,6 +1,11 @@
1
+ import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
2
+ import { ERROR_CODE, ERROR_MESSAGE } from "./error.js";
3
+ import { IRPCReader } from "./reader.js";
1
4
  import { uuid } from "./uuid.js";
2
5
 
3
6
  //#region src/call.ts
7
+ const DEFAULT_RETRY_MODE = "exponential";
8
+ const DEFAULT_RETRY_DELAY = 1e3;
4
9
  /**
5
10
  * Represents an RPC call with promise-like behavior for handling asynchronous operations.
6
11
  * Each call has a unique identifier and manages its own resolution state.
@@ -11,21 +16,61 @@ var IRPCCall = class {
11
16
  */
12
17
  id = uuid();
13
18
  /**
19
+ * The status of the RPC call, indicating whether it is pending, resolved, or rejected.
20
+ */
21
+ status = IRPC_STATUS.PENDING;
22
+ /**
14
23
  * Flag indicating whether this call has been resolved or rejected.
15
24
  * Prevents multiple resolutions of the same call.
16
25
  */
17
26
  resolved = false;
18
27
  /**
28
+ * The timestamp when the RPC call was started.
29
+ */
30
+ startedAt = Date.now();
31
+ /**
32
+ * The timestamp when the RPC call was finished.
33
+ */
34
+ finishedAt;
35
+ /**
36
+ * The value returned by the RPC call.
37
+ */
38
+ value;
39
+ error;
40
+ timerId;
41
+ retries = 0;
42
+ retryReasons = /* @__PURE__ */ new Set();
43
+ reader;
44
+ /**
19
45
  * Creates a new IRPCCall instance.
46
+ * @param transport
20
47
  * @param payload - The RPC payload containing method and parameters
21
- * @param resolver - Function to resolve the associated promise with a value
22
- * @param rejector - Function to reject the associated promise with an error
48
+ * @param options - Options for the call, such as timeout, maxRetries, etc.
23
49
  */
24
- constructor(payload, resolver, rejector, timeout) {
50
+ constructor(transport, payload, options) {
51
+ this.transport = transport;
25
52
  this.payload = payload;
26
- this.resolver = resolver;
27
- this.rejector = rejector;
28
- this.timeout = timeout;
53
+ this.options = options;
54
+ if (options.timeout) this.timerId = setTimeout(() => {
55
+ this.reader.push({
56
+ id: this.id,
57
+ name: this.payload.name,
58
+ type: IRPC_PACKET_TYPE.CLOSE,
59
+ status: IRPC_STATUS.ERROR,
60
+ error: {
61
+ code: ERROR_CODE.TIMEOUT,
62
+ message: ERROR_MESSAGE[ERROR_CODE.TIMEOUT]
63
+ },
64
+ createdAt: Date.now()
65
+ });
66
+ this.reject(new Error(ERROR_MESSAGE[ERROR_CODE.TIMEOUT]), false);
67
+ }, options.timeout);
68
+ this.reader = new IRPCReader(this.id);
69
+ }
70
+ enqueue(packet) {
71
+ this.reader.push(packet);
72
+ if (this.reader.status === IRPC_STATUS.SUCCESS) this.resolve(this.reader.data);
73
+ else if (this.reader.status === IRPC_STATUS.ERROR) this.reject(this.reader.error);
29
74
  }
30
75
  /**
31
76
  * Resolves the RPC call with the provided value.
@@ -34,20 +79,42 @@ var IRPCCall = class {
34
79
  */
35
80
  resolve(value) {
36
81
  if (this.resolved) return;
82
+ this.value = value;
83
+ this.status = IRPC_STATUS.SUCCESS;
37
84
  this.resolved = true;
38
- this.resolver(value);
85
+ this.finishedAt = Date.now();
86
+ clearTimeout(this.timerId);
39
87
  }
40
88
  /**
41
89
  * Rejects the RPC call with the provided reason.
42
90
  * If the call is already resolved, this method does nothing.
43
91
  * @param reason - Optional error reason for rejecting the promise
92
+ * @param retriable - Flag indicating whether to retry the call
44
93
  */
45
- reject(reason) {
94
+ reject(reason, retriable = true) {
46
95
  if (this.resolved) return;
47
- this.resolved = true;
48
- this.rejector(reason);
96
+ const { maxRetries, retryMode = DEFAULT_RETRY_MODE, retryDelay = DEFAULT_RETRY_DELAY } = this.options;
97
+ if (maxRetries && retriable) {
98
+ if (reason) this.retryReasons.add(reason);
99
+ if (this.retries >= maxRetries) {
100
+ console.error(ERROR_MESSAGE[ERROR_CODE.CALL_MAX_RETRIES_REACHED], this.retryReasons);
101
+ this.reject(reason, false);
102
+ return;
103
+ }
104
+ const delay = retryMode === "linear" ? retryDelay : retryDelay * 2 ** this.retries;
105
+ setTimeout(() => {
106
+ this.retries++;
107
+ this.transport.schedule(this);
108
+ }, delay);
109
+ } else {
110
+ this.error = reason;
111
+ this.status = IRPC_STATUS.ERROR;
112
+ this.resolved = true;
113
+ this.finishedAt = Date.now();
114
+ clearTimeout(this.timerId);
115
+ }
49
116
  }
50
117
  };
51
118
 
52
119
  //#endregion
53
- export { IRPCCall };
120
+ export { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall };
package/dist/enum.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ //#region src/enum.d.ts
2
+ declare const IRPC_PACKET_TYPE: {
3
+ readonly CALL: "call";
4
+ readonly EVENT: "event";
5
+ readonly CLOSE: "close";
6
+ readonly ANSWER: "answer";
7
+ readonly REQUEST: "request";
8
+ readonly RESPONSE: "response";
9
+ };
10
+ declare const IRPC_DATA_TYPE: {
11
+ readonly ARRAY: "array";
12
+ readonly OBJECT: "object";
13
+ readonly READABLE: "readable";
14
+ readonly WRITABLE: "writable";
15
+ readonly PRIMITIVE: "primitive";
16
+ };
17
+ declare const IRPC_EVENT_TYPE: {
18
+ readonly OBJECT_SET: "set";
19
+ readonly OBJECT_DELETE: "delete";
20
+ readonly ARRAY_PUSH: "push";
21
+ readonly ARRAY_POP: "pop";
22
+ readonly ARRAY_SHIFT: "shift";
23
+ readonly ARRAY_UNSHIFT: "unshift";
24
+ readonly ARRAY_SPLICE: "splice";
25
+ readonly ARRAY_REVERSE: "reverse";
26
+ readonly ARRAY_COPY_WITHIN: "copyWithin";
27
+ };
28
+ declare const IRPC_STATUS: {
29
+ readonly IDLE: "idle";
30
+ readonly ERROR: "error";
31
+ readonly PENDING: "pending";
32
+ readonly SUCCESS: "success";
33
+ };
34
+ //#endregion
35
+ export { IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS };
package/dist/enum.js ADDED
@@ -0,0 +1,36 @@
1
+ //#region src/enum.ts
2
+ const IRPC_PACKET_TYPE = {
3
+ CALL: "call",
4
+ EVENT: "event",
5
+ CLOSE: "close",
6
+ ANSWER: "answer",
7
+ REQUEST: "request",
8
+ RESPONSE: "response"
9
+ };
10
+ const IRPC_DATA_TYPE = {
11
+ ARRAY: "array",
12
+ OBJECT: "object",
13
+ READABLE: "readable",
14
+ WRITABLE: "writable",
15
+ PRIMITIVE: "primitive"
16
+ };
17
+ const IRPC_EVENT_TYPE = {
18
+ OBJECT_SET: "set",
19
+ OBJECT_DELETE: "delete",
20
+ ARRAY_PUSH: "push",
21
+ ARRAY_POP: "pop",
22
+ ARRAY_SHIFT: "shift",
23
+ ARRAY_UNSHIFT: "unshift",
24
+ ARRAY_SPLICE: "splice",
25
+ ARRAY_REVERSE: "reverse",
26
+ ARRAY_COPY_WITHIN: "copyWithin"
27
+ };
28
+ const IRPC_STATUS = {
29
+ IDLE: "idle",
30
+ ERROR: "error",
31
+ PENDING: "pending",
32
+ SUCCESS: "success"
33
+ };
34
+
35
+ //#endregion
36
+ export { IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS };
package/dist/error.d.ts CHANGED
@@ -22,6 +22,8 @@ declare const ERROR_CODE: {
22
22
  RESOLVER_NOT_IMPLEMENTED: string;
23
23
  RESOLVER_NOT_FOUND: string;
24
24
  RESOLVER_NOT_SUPPORTED: string;
25
+ STREAM_ERROR: string;
26
+ CALL_MAX_RETRIES_REACHED: string;
25
27
  };
26
28
  type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];
27
29
  declare const ERROR_MESSAGE: {
@@ -41,10 +43,13 @@ declare const ERROR_MESSAGE: {
41
43
  [ERROR_CODE.TRANSPORT_INVALID]: string;
42
44
  [ERROR_CODE.TRANSPORT_NOT_IMPLEMENTED]: string;
43
45
  [ERROR_CODE.STUB_INVALID]: string;
46
+ [ERROR_CODE.STREAM_ERROR]: string;
44
47
  [ERROR_CODE.STUB_NOT_IMPLEMENTED]: string;
45
48
  [ERROR_CODE.RESOLVER_MISSING]: string;
46
49
  [ERROR_CODE.RESOLVER_NOT_IMPLEMENTED]: string;
47
50
  [ERROR_CODE.RESOLVER_NOT_FOUND]: string;
51
+ [ERROR_CODE.RESOLVER_NOT_SUPPORTED]: string;
52
+ [ERROR_CODE.CALL_MAX_RETRIES_REACHED]: string;
48
53
  };
49
54
  //#endregion
50
55
  export { ERROR_CODE, ERROR_MESSAGE, ErrorCode };
package/dist/error.js CHANGED
@@ -21,7 +21,9 @@ const ERROR_CODE = {
21
21
  RESOLVER_MISSING: "resolver_missing",
22
22
  RESOLVER_NOT_IMPLEMENTED: "resolver_not_implemented",
23
23
  RESOLVER_NOT_FOUND: "resolver_not_found",
24
- RESOLVER_NOT_SUPPORTED: "resolver_not_supported"
24
+ RESOLVER_NOT_SUPPORTED: "resolver_not_supported",
25
+ STREAM_ERROR: "stream_error",
26
+ CALL_MAX_RETRIES_REACHED: "call_max_retries_reached"
25
27
  };
26
28
  const ERROR_MESSAGE = {
27
29
  [ERROR_CODE.UNKNOWN]: "IRPC: Unknown error",
@@ -40,10 +42,13 @@ const ERROR_MESSAGE = {
40
42
  [ERROR_CODE.TRANSPORT_INVALID]: "IRPC: Transport invalid error",
41
43
  [ERROR_CODE.TRANSPORT_NOT_IMPLEMENTED]: "IRPC: Transport not implemented error",
42
44
  [ERROR_CODE.STUB_INVALID]: "IRPC: Stub invalid error",
45
+ [ERROR_CODE.STREAM_ERROR]: "IRPC: Stream error",
43
46
  [ERROR_CODE.STUB_NOT_IMPLEMENTED]: "IRPC: Stub not implemented error",
44
47
  [ERROR_CODE.RESOLVER_MISSING]: "IRPC: Resolver missing error",
45
48
  [ERROR_CODE.RESOLVER_NOT_IMPLEMENTED]: "IRPC: Resolver not implemented error",
46
- [ERROR_CODE.RESOLVER_NOT_FOUND]: "IRPC: Resolver not found error"
49
+ [ERROR_CODE.RESOLVER_NOT_FOUND]: "IRPC: Resolver not found error",
50
+ [ERROR_CODE.RESOLVER_NOT_SUPPORTED]: "IRPC: Resolver not supported error",
51
+ [ERROR_CODE.CALL_MAX_RETRIES_REACHED]: "IRPC: Call max retries reached error"
47
52
  };
48
53
 
49
54
  //#endregion
package/dist/index.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import { IRPCCacheEntry, IRPCCacher } from "./cache.js";
2
+ import { IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
2
3
  import { ERROR_CODE, ERROR_MESSAGE, ErrorCode } from "./error.js";
3
4
  import { IRPCTransport } from "./transport.js";
4
- import { IRPCArraySchema, IRPCCache, IRPCCaches, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCError, IRPCHandler, IRPCInit, IRPCInputs, IRPCModule, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackageConfig, IRPCPackageInfo, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCRequest, IRPCResponse, IRPCSchema, IRPCSpec, IRPCSpecStore, IRPCStubStore, TransportConfig } from "./types.js";
5
- import { IRPCCall } from "./call.js";
5
+ import { IRPCArraySchema, IRPCCallConfig, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCDataType, IRPCDeclareInit, IRPCError, IRPCEventType, IRPCHandler, IRPCInit, IRPCInputs, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackageConfig, IRPCPackageInfo, IRPCPacketAnswer, IRPCPacketBase, IRPCPacketCall, IRPCPacketClose, IRPCPacketData, IRPCPacketEvent, IRPCPacketStream, IRPCPacketType, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCReadable, IRPCRequest, IRPCResponse, IRPCSchema, IRPCSpec, IRPCSpecStore, IRPCStatus, IRPCStubStore, TransportConfig } from "./types.js";
6
+ import { RemoteState, StreamConstructor, stream } from "./state.js";
7
+ import { IRPCReader } from "./reader.js";
8
+ import { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall } from "./call.js";
6
9
  import { createContext, getContext, setContext, setContextProvider, withContext } from "./context.js";
7
10
  import { IRPCPackage, createPackage } from "./module.js";
8
11
  import { IRPCResolver } from "./resolver.js";
9
- export { ERROR_CODE, ERROR_MESSAGE, ErrorCode, IRPCArraySchema, IRPCCache, IRPCCacheEntry, IRPCCacher, IRPCCaches, IRPCCall, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCError, IRPCHandler, IRPCInit, IRPCInputs, IRPCModule, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackage, IRPCPackageConfig, IRPCPackageInfo, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCRequest, IRPCResolver, IRPCResponse, IRPCSchema, IRPCSpec, IRPCSpecStore, IRPCStubStore, IRPCTransport, TransportConfig, createContext, createPackage, getContext, setContext, setContextProvider, withContext };
12
+ import { IRPCStream } from "./stream.js";
13
+ export { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, ERROR_CODE, ERROR_MESSAGE, ErrorCode, IRPCArraySchema, IRPCCacheEntry, IRPCCacher, IRPCCall, IRPCCallConfig, IRPCContext, IRPCContextProvider, IRPCData, IRPCDataSchema, IRPCDataType, IRPCDeclareInit, IRPCError, IRPCEventType, IRPCHandler, IRPCInit, IRPCInputs, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackage, IRPCPackageConfig, IRPCPackageInfo, IRPCPacketAnswer, IRPCPacketBase, IRPCPacketCall, IRPCPacketClose, IRPCPacketData, IRPCPacketEvent, IRPCPacketStream, IRPCPacketType, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCReadable, IRPCReader, IRPCRequest, IRPCResolver, IRPCResponse, IRPCSchema, IRPCSpec, IRPCSpecStore, IRPCStatus, IRPCStream, IRPCStubStore, IRPCTransport, IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS, RemoteState, StreamConstructor, TransportConfig, createContext, createPackage, getContext, setContext, setContextProvider, stream, withContext };
package/dist/index.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { IRPCCacher } from "./cache.js";
2
- import { IRPCCall } from "./call.js";
3
- import { createContext, getContext, setContext, setContextProvider, withContext } from "./context.js";
2
+ import { IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
4
3
  import { ERROR_CODE, ERROR_MESSAGE } from "./error.js";
4
+ import { RemoteState, stream } from "./state.js";
5
+ import { IRPCReader } from "./reader.js";
6
+ import { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall } from "./call.js";
7
+ import { createContext, getContext, setContext, setContextProvider, withContext } from "./context.js";
5
8
  import { IRPCTransport } from "./transport.js";
6
9
  import { IRPCPackage, createPackage } from "./module.js";
7
10
  import { IRPCResolver } from "./resolver.js";
11
+ import { IRPCStream } from "./stream.js";
8
12
 
9
- export { ERROR_CODE, ERROR_MESSAGE, IRPCCacher, IRPCCall, IRPCPackage, IRPCResolver, IRPCTransport, createContext, createPackage, getContext, setContext, setContextProvider, withContext };
13
+ export { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, ERROR_CODE, ERROR_MESSAGE, IRPCCacher, IRPCCall, IRPCPackage, IRPCReader, IRPCResolver, IRPCStream, IRPCTransport, IRPC_DATA_TYPE, IRPC_EVENT_TYPE, IRPC_PACKET_TYPE, IRPC_STATUS, RemoteState, createContext, createPackage, getContext, setContext, setContextProvider, stream, withContext };
package/dist/module.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { IRPCCacher } from "./cache.js";
2
2
  import { IRPCTransport } from "./transport.js";
3
- import { IRPCData, IRPCHandler, IRPCInit, IRPCInputs, IRPCOutput, IRPCPackageConfig, IRPCPackageInfo, IRPCRequest, IRPCSpec, IRPCSpecStore, IRPCStubStore } from "./types.js";
3
+ import { IRPCData, IRPCDeclareInit, IRPCHandler, IRPCInputs, IRPCOutput, IRPCPackageConfig, IRPCPackageInfo, IRPCRequest, IRPCSpec, IRPCSpecStore, IRPCStubStore } from "./types.js";
4
+ import { RemoteState } from "./state.js";
4
5
 
5
6
  //#region src/module.d.ts
6
7
 
@@ -46,18 +47,18 @@ declare class IRPCPackage {
46
47
  constructor(config?: Partial<IRPCPackageConfig>);
47
48
  /**
48
49
  * Declares a new IRPC specification and creates a corresponding stub function
49
- * @param init - The initialization object containing the IRPC specification
50
+ * @param options - The initialization object containing the IRPC specification
50
51
  * @returns A stub function that can be used to call the IRPC
51
52
  * @throws Error if an IRPC with the same name already exists
52
53
  */
53
- declare<F, I extends IRPCInputs = IRPCInputs, O extends IRPCOutput = IRPCOutput>(init: IRPCInit<I, O>): F;
54
+ declare<F, I extends IRPCInputs = IRPCInputs, O extends IRPCOutput = IRPCOutput>(options: IRPCDeclareInit<F, I, O>): F;
54
55
  /**
55
56
  * Resolves and executes an IRPC call based on a request object
56
57
  * @param req - The request containing the IRPC name and arguments
57
58
  * @returns The result of the IRPC execution
58
59
  * @throws Error if the IRPC doesn't exist or doesn't have an implementation
59
60
  */
60
- resolve(req: IRPCRequest): Promise<IRPCData>;
61
+ resolve(req: IRPCRequest): IRPCData | Promise<IRPCData> | RemoteState<IRPCData>;
61
62
  /**
62
63
  * Associates a handler function with a stub function
63
64
  * @param stub - The stub function created by declare()
package/dist/module.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { IRPCCacher } from "./cache.js";
2
2
  import { ERROR_CODE, ERROR_MESSAGE } from "./error.js";
3
+ import { RemoteState } from "./state.js";
3
4
  import { IRPCTransport } from "./transport.js";
4
5
 
5
6
  //#region src/module.ts
@@ -65,29 +66,40 @@ var IRPCPackage = class {
65
66
  }
66
67
  /**
67
68
  * Declares a new IRPC specification and creates a corresponding stub function
68
- * @param init - The initialization object containing the IRPC specification
69
+ * @param options - The initialization object containing the IRPC specification
69
70
  * @returns A stub function that can be used to call the IRPC
70
71
  * @throws Error if an IRPC with the same name already exists
71
72
  */
72
- declare(init) {
73
- if (this.specs.has(init.name)) throw new Error(`IRPC ${init.name} already exists.`);
74
- const spec = { ...init };
73
+ declare(options) {
74
+ if (this.specs.has(options.name)) throw new Error(`IRPC ${options.name} already exists.`);
75
+ const spec = { ...options };
76
+ const calls = /* @__PURE__ */ new Map();
75
77
  const caches = new IRPCCacher();
76
- const timeout = spec.timeout ?? this.config.timeout;
77
- const stub = (async (...args) => {
78
- if (typeof spec.handler === "function") return spec.handler(...args);
79
- if (!this.transport) throw new Error(ERROR_MESSAGE[ERROR_CODE.TRANSPORT_MISSING]);
80
- if (spec.maxAge) {
81
- const key = JSON.stringify(args);
82
- const cache = caches.get(key);
83
- if (cache) return cache.value;
84
- const data = await this.transport.call(spec, args, timeout);
85
- caches.set(key, data, spec.maxAge);
86
- return data;
87
- }
88
- return await this.transport.call(spec, args, timeout);
78
+ const stub = ((...args) => {
79
+ if (!this.transport && typeof spec.handler !== "function") return Promise.reject(new Error(ERROR_MESSAGE[ERROR_CODE.TRANSPORT_MISSING]));
80
+ const callKey = JSON.stringify(args);
81
+ const cached = caches.get(callKey);
82
+ if (cached) return cached.value;
83
+ if (spec.coalesce !== false && calls.has(callKey)) return calls.get(callKey);
84
+ const { timeout, maxRetries, retryDelay, retryMode } = {
85
+ ...this.config,
86
+ ...spec
87
+ };
88
+ const config = {
89
+ timeout,
90
+ maxRetries,
91
+ retryDelay,
92
+ retryMode
93
+ };
94
+ const call = typeof spec.handler === "function" ? spec.handler(...args) : this.transport.call(spec, args, config);
95
+ calls.set(callKey, call);
96
+ if (spec.maxAge) caches.set(callKey, call, spec.maxAge);
97
+ if (typeof spec.init === "function" && call instanceof RemoteState && typeof call.data === "undefined") call.data = spec.init();
98
+ if (call instanceof Promise) call.finally(() => calls.delete(callKey)).catch(() => {});
99
+ else calls.delete(callKey);
100
+ return call;
89
101
  });
90
- this.specs.set(init.name, spec);
102
+ this.specs.set(options.name, spec);
91
103
  this.stubs.set(stub, spec);
92
104
  this.cache.set(stub, caches);
93
105
  return stub;
@@ -98,11 +110,11 @@ var IRPCPackage = class {
98
110
  * @returns The result of the IRPC execution
99
111
  * @throws Error if the IRPC doesn't exist or doesn't have an implementation
100
112
  */
101
- async resolve(req) {
113
+ resolve(req) {
102
114
  const spec = this.specs.get(req.name);
103
115
  if (!spec) return Promise.reject(/* @__PURE__ */ new Error(`IRPC ${req.name} does not exist.`));
104
116
  if (typeof spec.handler !== "function") return Promise.reject(/* @__PURE__ */ new Error(`IRPC ${req.name} does not have an implementation.`));
105
- return await spec.handler(...req.args);
117
+ return spec.handler(...req.args);
106
118
  }
107
119
  /**
108
120
  * Associates a handler function with a stub function
@@ -0,0 +1,24 @@
1
+ import { IRPCData, IRPCPacketStream } from "./types.js";
2
+ import { RemoteState } from "./state.js";
3
+
4
+ //#region src/reader.d.ts
5
+
6
+ /**
7
+ * A client-side consumer that hydrates `RemoteState` instances from network stream packets.
8
+ *
9
+ * @template T - The type of data yielded by the stream.
10
+ */
11
+ declare class IRPCReader<T extends IRPCData> extends RemoteState<T> {
12
+ id: string;
13
+ packets: Set<IRPCPacketStream<T>>;
14
+ constructor(id: string, init?: T);
15
+ /**
16
+ * Pushes incoming network packets into this reader, evaluating payload data
17
+ * and subsequently updating the core state values locally.
18
+ *
19
+ * @param packet - The incoming unified Stream Packet structure (`ANSWER`, `EVENT`, or `CLOSE`).
20
+ */
21
+ push(packet: IRPCPacketStream<T>): void;
22
+ }
23
+ //#endregion
24
+ export { IRPCReader };
package/dist/reader.js ADDED
@@ -0,0 +1,37 @@
1
+ import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
2
+ import { RemoteState } from "./state.js";
3
+ import { replay } from "@anchorlib/core";
4
+
5
+ //#region src/reader.ts
6
+ /**
7
+ * A client-side consumer that hydrates `RemoteState` instances from network stream packets.
8
+ *
9
+ * @template T - The type of data yielded by the stream.
10
+ */
11
+ var IRPCReader = class extends RemoteState {
12
+ packets = /* @__PURE__ */ new Set();
13
+ constructor(id, init) {
14
+ super(init);
15
+ this.id = id;
16
+ }
17
+ /**
18
+ * Pushes incoming network packets into this reader, evaluating payload data
19
+ * and subsequently updating the core state values locally.
20
+ *
21
+ * @param packet - The incoming unified Stream Packet structure (`ANSWER`, `EVENT`, or `CLOSE`).
22
+ */
23
+ push(packet) {
24
+ packet.arrivedAt = Date.now();
25
+ this.packets.add(packet);
26
+ if (packet.type === IRPC_PACKET_TYPE.ANSWER) if (packet.status === IRPC_STATUS.ERROR) this.error = new Error(packet.error.message);
27
+ else this.data = packet.data;
28
+ else if (packet.type === IRPC_PACKET_TYPE.EVENT) replay(this.state, packet.data);
29
+ else if (packet.type === IRPC_PACKET_TYPE.CLOSE) {
30
+ if (packet.error) this.error = new Error(packet.error.message);
31
+ }
32
+ this.status = packet.status;
33
+ }
34
+ };
35
+
36
+ //#endregion
37
+ export { IRPCReader };
@@ -1,4 +1,4 @@
1
- import { IRPCDataSchema, IRPCError, IRPCInputs, IRPCOutput, IRPCRequest, IRPCResponse, IRPCSpec } from "./types.js";
1
+ import { IRPCDataSchema, IRPCInputs, IRPCOutput, IRPCRequest, IRPCResponse, IRPCSpec } from "./types.js";
2
2
  import { IRPCPackage } from "./module.js";
3
3
 
4
4
  //#region src/resolver.d.ts
@@ -45,17 +45,7 @@ declare class IRPCResolver {
45
45
  id,
46
46
  name,
47
47
  args
48
- }: IRPCRequest, schema?: IRPCOutput): Promise<{
49
- id: string;
50
- name: string;
51
- result: string | number | boolean | IRPCDataSchema | Record<string, unknown> | (string | number | boolean | Record<string, unknown> | null | undefined)[] | null | undefined;
52
- error?: undefined;
53
- } | {
54
- id: string;
55
- name: string;
56
- error: IRPCError;
57
- result?: undefined;
58
- }>;
48
+ }: IRPCRequest, schema?: IRPCOutput): Promise<IRPCResponse>;
59
49
  }
60
50
  //#endregion
61
51
  export { IRPCResolver };
package/dist/resolver.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ERROR_CODE } from "./error.js";
2
+ import { RemoteState } from "./state.js";
2
3
 
3
4
  //#region src/resolver.ts
4
5
  /**
@@ -69,11 +70,28 @@ var IRPCResolver = class {
69
70
  */
70
71
  async forward({ id, name, args }, schema) {
71
72
  try {
72
- const output = parseOutput(await this.module.resolve({
73
+ const result = this.module.resolve({
73
74
  id,
74
75
  name,
75
76
  args
76
- }), schema);
77
+ });
78
+ if (result instanceof RemoteState) {
79
+ const output$1 = parseOutput(result.data, schema);
80
+ if (!output$1.success) return {
81
+ id,
82
+ name,
83
+ error: {
84
+ code: ERROR_CODE.INVALID_OUTPUT,
85
+ message: output$1.error?.message
86
+ }
87
+ };
88
+ return {
89
+ id,
90
+ name,
91
+ result
92
+ };
93
+ }
94
+ const output = parseOutput(await result, schema);
77
95
  if (output.success) return {
78
96
  id,
79
97
  name,