@irpclib/irpc 1.0.0-beta.21 → 1.0.0-beta.23
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 +7 -4
- package/dist/call.js +25 -7
- package/dist/context.d.ts +6 -16
- package/dist/context.js +11 -22
- package/dist/enum.d.ts +19 -12
- package/dist/enum.js +20 -13
- package/dist/error.d.ts +2 -0
- package/dist/error.js +2 -0
- package/dist/file.d.ts +37 -0
- package/dist/file.js +86 -0
- package/dist/index.d.ts +11 -6
- package/dist/index.js +11 -6
- package/dist/module.d.ts +41 -9
- package/dist/module.js +142 -12
- package/dist/packet.d.ts +32 -0
- package/dist/packet.js +100 -0
- package/dist/reader.d.ts +16 -3
- package/dist/reader.js +22 -4
- package/dist/resolver.js +5 -0
- package/dist/router.d.ts +42 -0
- package/dist/router.js +59 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +15 -0
- package/dist/state.d.ts +13 -17
- package/dist/state.js +77 -23
- package/dist/store.d.ts +44 -0
- package/dist/store.js +76 -0
- package/dist/stream.d.ts +13 -2
- package/dist/stream.js +78 -15
- package/dist/transport.d.ts +8 -1
- package/dist/transport.js +28 -5
- package/dist/types.d.ts +91 -16
- package/package.json +7 -3
- package/readme.md +8 -3
- package/dist/uuid.d.ts +0 -21
- package/dist/uuid.js +0 -45
package/dist/state.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { IRPC_STATUS } from "./enum.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ERROR_CODE, ERROR_MESSAGE } from "./error.js";
|
|
3
|
+
import { getAbortSignal } from "./context.js";
|
|
4
|
+
import { $do, anchor, mutable, onCleanup, subscribe } from "@anchorlib/core";
|
|
3
5
|
|
|
4
6
|
//#region src/state.ts
|
|
5
7
|
/**
|
|
@@ -12,62 +14,95 @@ import { anchor, mutable, subscribe } from "@anchorlib/core";
|
|
|
12
14
|
* @template T - The type of data held by the state.
|
|
13
15
|
*/
|
|
14
16
|
var RemoteState = class extends Promise {
|
|
15
|
-
state;
|
|
16
|
-
accept;
|
|
17
|
-
reject;
|
|
17
|
+
#state;
|
|
18
|
+
#accept;
|
|
19
|
+
#reject;
|
|
20
|
+
#closed = false;
|
|
21
|
+
get state() {
|
|
22
|
+
return this.#state;
|
|
23
|
+
}
|
|
18
24
|
/**
|
|
19
25
|
* The current data payload of the state.
|
|
20
26
|
*/
|
|
21
27
|
get data() {
|
|
22
|
-
return this
|
|
28
|
+
return this.#state.data;
|
|
23
29
|
}
|
|
24
30
|
set data(data) {
|
|
25
|
-
this
|
|
31
|
+
if (this.#closed) return;
|
|
32
|
+
this.#state.data = data;
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* The current error encountered by the state, if any.
|
|
29
36
|
*/
|
|
30
37
|
get error() {
|
|
31
|
-
return this
|
|
38
|
+
return this.#state.error;
|
|
32
39
|
}
|
|
33
40
|
set error(error) {
|
|
34
|
-
this
|
|
41
|
+
if (this.#closed) return;
|
|
42
|
+
this.#state.error = error;
|
|
35
43
|
}
|
|
36
44
|
/**
|
|
37
45
|
* The execution status of the state (PENDING, SUCCESS, ERROR).
|
|
38
46
|
* Transitioning to a terminal status (SUCCESS or ERROR) will automatically resolve or reject the underlying Promise.
|
|
39
47
|
*/
|
|
40
48
|
get status() {
|
|
41
|
-
return this
|
|
49
|
+
return this.#state.status;
|
|
42
50
|
}
|
|
43
51
|
set status(status) {
|
|
52
|
+
if (this.#closed) return;
|
|
44
53
|
this.state.status = status;
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
this.destroy();
|
|
48
|
-
} else if (this.status === IRPC_STATUS.SUCCESS) {
|
|
49
|
-
this.accept(this.data);
|
|
50
|
-
this.destroy();
|
|
51
|
-
}
|
|
54
|
+
if (status === IRPC_STATUS.ERROR) this.reject();
|
|
55
|
+
else if (status === IRPC_STATUS.SUCCESS) this.accept();
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
54
58
|
* Initializes a new RemoteState with an optional initial payload.
|
|
55
59
|
*
|
|
56
60
|
* @param init - An optional starting value for the data payload.
|
|
61
|
+
* @param status - The initial status of the state (PENDING, SUCCESS, ERROR).
|
|
57
62
|
*/
|
|
58
|
-
constructor(init) {
|
|
63
|
+
constructor(init, status = IRPC_STATUS.PENDING) {
|
|
59
64
|
let acceptFn;
|
|
60
65
|
let rejectFn;
|
|
61
66
|
super((resolve, reject) => {
|
|
62
67
|
acceptFn = resolve;
|
|
63
68
|
rejectFn = reject;
|
|
64
69
|
});
|
|
65
|
-
this
|
|
66
|
-
this
|
|
67
|
-
this
|
|
70
|
+
this.#accept = acceptFn;
|
|
71
|
+
this.#reject = rejectFn;
|
|
72
|
+
this.#state = mutable({
|
|
68
73
|
data: init,
|
|
69
74
|
error: void 0,
|
|
70
|
-
status
|
|
75
|
+
status
|
|
76
|
+
});
|
|
77
|
+
onCleanup(() => this.close());
|
|
78
|
+
}
|
|
79
|
+
accept(...args) {
|
|
80
|
+
$do(() => {
|
|
81
|
+
if (this.#closed) return;
|
|
82
|
+
const value = args.length ? args[0] : this.data;
|
|
83
|
+
this.#closed = true;
|
|
84
|
+
this.#state.status = IRPC_STATUS.SUCCESS;
|
|
85
|
+
this.#state.data = value;
|
|
86
|
+
this.#accept(value);
|
|
87
|
+
this.destroy();
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
reject(...args) {
|
|
91
|
+
$do(() => {
|
|
92
|
+
if (this.#closed) return;
|
|
93
|
+
if (args.length) this.#state.error = args[0];
|
|
94
|
+
this.#closed = true;
|
|
95
|
+
this.#state.status = IRPC_STATUS.ERROR;
|
|
96
|
+
this.#reject(this.error ?? new Error(ERROR_MESSAGE[ERROR_CODE.UNKNOWN]));
|
|
97
|
+
this.destroy();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
abort() {
|
|
101
|
+
$do(() => {
|
|
102
|
+
this.#closed = true;
|
|
103
|
+
this.#state.status = IRPC_STATUS.ABORTED;
|
|
104
|
+
this.#accept(this.data);
|
|
105
|
+
this.destroy();
|
|
71
106
|
});
|
|
72
107
|
}
|
|
73
108
|
/**
|
|
@@ -80,6 +115,15 @@ var RemoteState = class extends Promise {
|
|
|
80
115
|
return subscribe(this.state, handler);
|
|
81
116
|
}
|
|
82
117
|
/**
|
|
118
|
+
* Closes the reactive state and terminates the underlying Promise.
|
|
119
|
+
*/
|
|
120
|
+
close() {
|
|
121
|
+
if (this.#closed) return;
|
|
122
|
+
this.#closed = true;
|
|
123
|
+
this.#accept(this.data);
|
|
124
|
+
this.destroy();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
83
127
|
* Destroys the reactive state bindings.
|
|
84
128
|
*/
|
|
85
129
|
destroy() {
|
|
@@ -105,17 +149,27 @@ var RemoteState = class extends Promise {
|
|
|
105
149
|
*/
|
|
106
150
|
function stream(construct, init) {
|
|
107
151
|
const state = new RemoteState(init);
|
|
152
|
+
const abortSignal = getAbortSignal();
|
|
108
153
|
const accept = ((...values) => {
|
|
109
154
|
if (values.length > 0) state.data = values[0];
|
|
110
155
|
state.status = IRPC_STATUS.SUCCESS;
|
|
156
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
111
157
|
});
|
|
112
158
|
const reject = (error) => {
|
|
113
159
|
state.error = error;
|
|
114
160
|
state.status = IRPC_STATUS.ERROR;
|
|
161
|
+
abortSignal?.removeEventListener("abort", abort);
|
|
115
162
|
};
|
|
163
|
+
const abort = () => state.abort();
|
|
164
|
+
abortSignal?.addEventListener("abort", abort, { once: true });
|
|
116
165
|
try {
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
166
|
+
const cleanup = construct(state, accept, reject);
|
|
167
|
+
if (cleanup instanceof Promise) cleanup.then((futureCleanup) => {
|
|
168
|
+
if (typeof futureCleanup === "function") if (abortSignal?.aborted || state.status !== IRPC_STATUS.PENDING) $do(futureCleanup);
|
|
169
|
+
else abortSignal?.addEventListener("abort", () => $do(futureCleanup), { once: true });
|
|
170
|
+
}).catch(reject);
|
|
171
|
+
else if (typeof cleanup === "function") if (abortSignal?.aborted || state.status !== IRPC_STATUS.PENDING) $do(cleanup);
|
|
172
|
+
else abortSignal?.addEventListener("abort", () => $do(cleanup), { once: true });
|
|
119
173
|
} catch (error) {
|
|
120
174
|
reject(error);
|
|
121
175
|
}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { IRPC_STORE_EVENT } from "./enum.js";
|
|
2
|
+
import { IRPCData } from "./types.js";
|
|
3
|
+
import { IRPCPackage } from "./module.js";
|
|
4
|
+
import { IRPCRouter } from "./router.js";
|
|
5
|
+
import { IRPCStream } from "./stream.js";
|
|
6
|
+
|
|
7
|
+
//#region src/store.d.ts
|
|
8
|
+
type IRPCStoreEvent = {
|
|
9
|
+
type: typeof IRPC_STORE_EVENT.REGISTER;
|
|
10
|
+
data: IRPCPackage;
|
|
11
|
+
} | {
|
|
12
|
+
type: typeof IRPC_STORE_EVENT.ROUTE;
|
|
13
|
+
data: IRPCRouter;
|
|
14
|
+
} | {
|
|
15
|
+
type: typeof IRPC_STORE_EVENT.QUEUE;
|
|
16
|
+
data: IRPCStream<IRPCData>;
|
|
17
|
+
} | {
|
|
18
|
+
type: typeof IRPC_STORE_EVENT.DEQUEUE;
|
|
19
|
+
data: IRPCStream<IRPCData>;
|
|
20
|
+
} | {
|
|
21
|
+
type: typeof IRPC_STORE_EVENT.ERROR;
|
|
22
|
+
error: Error;
|
|
23
|
+
data?: unknown[];
|
|
24
|
+
};
|
|
25
|
+
type IRPCStoreSubscriber = (event: IRPCStoreEvent) => void;
|
|
26
|
+
declare class IRPCStore {
|
|
27
|
+
#private;
|
|
28
|
+
calls: Set<IRPCStream<IRPCData>>;
|
|
29
|
+
routers: Set<IRPCRouter>;
|
|
30
|
+
packages: Set<IRPCPackage>;
|
|
31
|
+
callCount: number;
|
|
32
|
+
errorCount: number;
|
|
33
|
+
register(pkg: IRPCPackage): void;
|
|
34
|
+
route(router: IRPCRouter): void;
|
|
35
|
+
queue(call: IRPCStream<IRPCData>): void;
|
|
36
|
+
dequeue(call: IRPCStream<IRPCData>): void;
|
|
37
|
+
error(error: Error, data?: unknown[]): void;
|
|
38
|
+
print(): void;
|
|
39
|
+
subscribe(handler: IRPCStoreSubscriber): () => boolean;
|
|
40
|
+
private broadcast;
|
|
41
|
+
}
|
|
42
|
+
declare const IRPC_STORE: IRPCStore;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { IRPCStore, IRPCStoreEvent, IRPCStoreSubscriber, IRPC_STORE };
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { IRPC_STORE_EVENT } from "./enum.js";
|
|
2
|
+
import { IRPCPackage } from "./module.js";
|
|
3
|
+
import { IRPCRouter } from "./router.js";
|
|
4
|
+
import { IRPCStream } from "./stream.js";
|
|
5
|
+
|
|
6
|
+
//#region src/store.ts
|
|
7
|
+
var IRPCStore = class {
|
|
8
|
+
#subscribers = /* @__PURE__ */ new Set();
|
|
9
|
+
calls = /* @__PURE__ */ new Set();
|
|
10
|
+
routers = /* @__PURE__ */ new Set();
|
|
11
|
+
packages = /* @__PURE__ */ new Set();
|
|
12
|
+
callCount = 0;
|
|
13
|
+
errorCount = 0;
|
|
14
|
+
register(pkg) {
|
|
15
|
+
if (!(pkg instanceof IRPCPackage)) throw new Error("Invalid package: package must be an instance of IRPCPackage.");
|
|
16
|
+
this.packages.add(pkg);
|
|
17
|
+
this.broadcast({
|
|
18
|
+
type: IRPC_STORE_EVENT.REGISTER,
|
|
19
|
+
data: pkg
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
route(router) {
|
|
23
|
+
if (!(router instanceof IRPCRouter)) throw new Error("Invalid router: router must be an instance of IRPCRouter.");
|
|
24
|
+
this.routers.add(router);
|
|
25
|
+
this.broadcast({
|
|
26
|
+
type: IRPC_STORE_EVENT.ROUTE,
|
|
27
|
+
data: router
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
queue(call) {
|
|
31
|
+
if (!(call instanceof IRPCStream)) throw new Error("Invalid call: call must be an instance of IRPCStream.");
|
|
32
|
+
this.calls.add(call);
|
|
33
|
+
this.callCount += 1;
|
|
34
|
+
this.broadcast({
|
|
35
|
+
type: IRPC_STORE_EVENT.QUEUE,
|
|
36
|
+
data: call
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
dequeue(call) {
|
|
40
|
+
if (!(call instanceof IRPCStream)) throw new Error("Invalid call: call must be an instance of IRPCStream.");
|
|
41
|
+
this.calls.delete(call);
|
|
42
|
+
this.broadcast({
|
|
43
|
+
type: IRPC_STORE_EVENT.DEQUEUE,
|
|
44
|
+
data: call
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
error(error, data) {
|
|
48
|
+
this.errorCount += 1;
|
|
49
|
+
this.broadcast({
|
|
50
|
+
type: IRPC_STORE_EVENT.ERROR,
|
|
51
|
+
error,
|
|
52
|
+
data
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
print() {
|
|
56
|
+
console.table([{
|
|
57
|
+
Packages: this.packages.size,
|
|
58
|
+
Routers: this.routers.size,
|
|
59
|
+
Running: this.calls.size,
|
|
60
|
+
Calls: this.callCount,
|
|
61
|
+
Errors: this.errorCount
|
|
62
|
+
}]);
|
|
63
|
+
}
|
|
64
|
+
subscribe(handler) {
|
|
65
|
+
if (typeof handler !== "function") throw new Error("Invalid handler: handler must be a function.");
|
|
66
|
+
this.#subscribers.add(handler);
|
|
67
|
+
return () => this.#subscribers.delete(handler);
|
|
68
|
+
}
|
|
69
|
+
broadcast(event) {
|
|
70
|
+
for (const subscriber of this.#subscribers) subscriber(event);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const IRPC_STORE = new IRPCStore();
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { IRPCStore, IRPC_STORE };
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { IRPCData, IRPCError, IRPCPacketStream, IRPCResponse, IRPCStatus } from "./types.js";
|
|
1
|
+
import { IRPCData, IRPCDataSchema, IRPCError, IRPCInputs, IRPCPacketStream, IRPCResponse, IRPCSpec, IRPCStatus } from "./types.js";
|
|
2
|
+
import { IRPCRouter } from "./router.js";
|
|
2
3
|
|
|
3
4
|
//#region src/stream.d.ts
|
|
4
5
|
|
|
@@ -14,20 +15,29 @@ declare class IRPCStream<T extends IRPCData> {
|
|
|
14
15
|
private id;
|
|
15
16
|
private name;
|
|
16
17
|
private initializer;
|
|
18
|
+
spec?: IRPCSpec<IRPCInputs, IRPCDataSchema> | undefined;
|
|
19
|
+
router?: IRPCRouter | undefined;
|
|
17
20
|
private pipeHandlers;
|
|
18
21
|
private closeHandlers;
|
|
19
22
|
private errorHandlers;
|
|
20
23
|
value?: T;
|
|
21
24
|
error?: IRPCError;
|
|
22
25
|
status: IRPCStatus;
|
|
26
|
+
closed: boolean;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
startedAt?: number;
|
|
29
|
+
updatedAt?: number;
|
|
30
|
+
controller?: AbortController;
|
|
23
31
|
/**
|
|
24
32
|
* Initializes a stream wrapping an asynchronous RPC execution.
|
|
25
33
|
*
|
|
26
34
|
* @param id - The unique identifier of the RPC request.
|
|
27
35
|
* @param name - The name of the specification processing the execution.
|
|
28
36
|
* @param initializer - An execution callback that yields an IRPCResponse.
|
|
37
|
+
* @param spec - The specification for the RPC execution.
|
|
38
|
+
* @param router - The router instance managing the stream.
|
|
29
39
|
*/
|
|
30
|
-
constructor(id: string, name: string, initializer: () => Promise<IRPCResponse>);
|
|
40
|
+
constructor(id: string, name: string, initializer: () => Promise<IRPCResponse>, spec?: IRPCSpec<IRPCInputs, IRPCDataSchema> | undefined, router?: IRPCRouter | undefined);
|
|
31
41
|
/**
|
|
32
42
|
* Evaluates the underlying initializer and propagates standard transport packets
|
|
33
43
|
* to all bound pipe handlers based on the output lifecycle.
|
|
@@ -52,6 +62,7 @@ declare class IRPCStream<T extends IRPCData> {
|
|
|
52
62
|
* @param handler - A callback function invoked at stream completion.
|
|
53
63
|
*/
|
|
54
64
|
close(handler: () => void): void;
|
|
65
|
+
private finish;
|
|
55
66
|
}
|
|
56
67
|
//#endregion
|
|
57
68
|
export { IRPCStream };
|
package/dist/stream.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
|
|
2
2
|
import { ERROR_CODE } from "./error.js";
|
|
3
|
+
import { getAbortController, getAbortSignal } from "./context.js";
|
|
3
4
|
import { RemoteState } from "./state.js";
|
|
5
|
+
import { IRPC_STORE } from "./store.js";
|
|
4
6
|
|
|
5
7
|
//#region src/stream.ts
|
|
6
8
|
/**
|
|
@@ -18,28 +20,50 @@ var IRPCStream = class {
|
|
|
18
20
|
value;
|
|
19
21
|
error;
|
|
20
22
|
status = IRPC_STATUS.IDLE;
|
|
23
|
+
closed = false;
|
|
24
|
+
createdAt = Date.now();
|
|
25
|
+
startedAt;
|
|
26
|
+
updatedAt;
|
|
27
|
+
controller;
|
|
21
28
|
/**
|
|
22
29
|
* Initializes a stream wrapping an asynchronous RPC execution.
|
|
23
30
|
*
|
|
24
31
|
* @param id - The unique identifier of the RPC request.
|
|
25
32
|
* @param name - The name of the specification processing the execution.
|
|
26
33
|
* @param initializer - An execution callback that yields an IRPCResponse.
|
|
34
|
+
* @param spec - The specification for the RPC execution.
|
|
35
|
+
* @param router - The router instance managing the stream.
|
|
27
36
|
*/
|
|
28
|
-
constructor(id, name, initializer) {
|
|
37
|
+
constructor(id, name, initializer, spec, router) {
|
|
29
38
|
this.id = id;
|
|
30
39
|
this.name = name;
|
|
31
40
|
this.initializer = initializer;
|
|
41
|
+
this.spec = spec;
|
|
42
|
+
this.router = router;
|
|
43
|
+
IRPC_STORE.queue(this);
|
|
32
44
|
}
|
|
33
45
|
/**
|
|
34
46
|
* Evaluates the underlying initializer and propagates standard transport packets
|
|
35
47
|
* to all bound pipe handlers based on the output lifecycle.
|
|
36
48
|
*/
|
|
37
49
|
async start() {
|
|
38
|
-
if (this.status !== IRPC_STATUS.IDLE) return;
|
|
50
|
+
if (this.status !== IRPC_STATUS.IDLE || this.closed) return;
|
|
51
|
+
this.startedAt = Date.now();
|
|
52
|
+
this.controller = getAbortController();
|
|
53
|
+
const abortSignal = getAbortSignal();
|
|
54
|
+
if (abortSignal?.aborted) {
|
|
55
|
+
this.finish();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
39
58
|
this.status = IRPC_STATUS.PENDING;
|
|
40
59
|
const { id, name } = this;
|
|
41
60
|
try {
|
|
42
|
-
const
|
|
61
|
+
const response = await this.initializer();
|
|
62
|
+
if (abortSignal?.aborted) {
|
|
63
|
+
this.finish();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const { result } = response;
|
|
43
67
|
if (result instanceof RemoteState) {
|
|
44
68
|
this.value = result.data;
|
|
45
69
|
if (result.status === IRPC_STATUS.SUCCESS || result.status === IRPC_STATUS.ERROR) {
|
|
@@ -60,8 +84,8 @@ var IRPCStream = class {
|
|
|
60
84
|
createdAt: Date.now()
|
|
61
85
|
};
|
|
62
86
|
this.pipeHandlers.forEach((handler) => handler(packet));
|
|
63
|
-
this.errorHandlers.forEach((handler) => handler(this.error));
|
|
64
|
-
this.
|
|
87
|
+
if (this.error) this.errorHandlers.forEach((handler) => handler(this.error));
|
|
88
|
+
this.finish();
|
|
65
89
|
return;
|
|
66
90
|
}
|
|
67
91
|
this.pipeHandlers.forEach((handler) => {
|
|
@@ -81,13 +105,13 @@ var IRPCStream = class {
|
|
|
81
105
|
handler({
|
|
82
106
|
id,
|
|
83
107
|
name,
|
|
84
|
-
type: IRPC_PACKET_TYPE.EVENT,
|
|
85
|
-
status: state.status,
|
|
86
108
|
data: {
|
|
87
109
|
type,
|
|
88
110
|
keys,
|
|
89
111
|
value
|
|
90
112
|
},
|
|
113
|
+
type: IRPC_PACKET_TYPE.EVENT,
|
|
114
|
+
status: state.status,
|
|
91
115
|
createdAt: Date.now()
|
|
92
116
|
});
|
|
93
117
|
});
|
|
@@ -111,25 +135,43 @@ var IRPCStream = class {
|
|
|
111
135
|
createdAt: Date.now()
|
|
112
136
|
});
|
|
113
137
|
});
|
|
114
|
-
|
|
138
|
+
abortSignal?.removeEventListener("abort", abortStream);
|
|
139
|
+
this.finish();
|
|
115
140
|
unsubscribe();
|
|
116
141
|
}
|
|
117
142
|
});
|
|
143
|
+
const abortStream = () => {
|
|
144
|
+
unsubscribe();
|
|
145
|
+
this.finish();
|
|
146
|
+
};
|
|
147
|
+
abortSignal?.addEventListener("abort", abortStream, { once: true });
|
|
118
148
|
} else {
|
|
119
149
|
this.value = result;
|
|
120
|
-
|
|
150
|
+
if (response.error) {
|
|
151
|
+
this.error = {
|
|
152
|
+
code: ERROR_CODE.STREAM_ERROR,
|
|
153
|
+
message: response.error.message
|
|
154
|
+
};
|
|
155
|
+
this.status = IRPC_STATUS.ERROR;
|
|
156
|
+
this.errorHandlers.forEach((handler) => handler(this.error));
|
|
157
|
+
} else this.status = IRPC_STATUS.SUCCESS;
|
|
121
158
|
const packet = {
|
|
122
159
|
id,
|
|
123
160
|
name,
|
|
124
161
|
type: IRPC_PACKET_TYPE.ANSWER,
|
|
125
|
-
status: IRPC_STATUS.SUCCESS,
|
|
126
162
|
data: this.value,
|
|
163
|
+
error: this.error,
|
|
164
|
+
status: this.status,
|
|
127
165
|
createdAt: Date.now()
|
|
128
166
|
};
|
|
129
167
|
this.pipeHandlers.forEach((handler) => handler(packet));
|
|
130
|
-
this.
|
|
168
|
+
this.finish();
|
|
131
169
|
}
|
|
132
170
|
} catch (error) {
|
|
171
|
+
IRPC_STORE.error(error, [{
|
|
172
|
+
id: this.id,
|
|
173
|
+
name: this.name
|
|
174
|
+
}]);
|
|
133
175
|
this.error = {
|
|
134
176
|
code: ERROR_CODE.STREAM_ERROR,
|
|
135
177
|
message: error.message
|
|
@@ -146,7 +188,7 @@ var IRPCStream = class {
|
|
|
146
188
|
});
|
|
147
189
|
});
|
|
148
190
|
this.errorHandlers.forEach((handler) => handler(this.error));
|
|
149
|
-
this.
|
|
191
|
+
this.finish();
|
|
150
192
|
return;
|
|
151
193
|
}
|
|
152
194
|
}
|
|
@@ -169,8 +211,12 @@ var IRPCStream = class {
|
|
|
169
211
|
});
|
|
170
212
|
return;
|
|
171
213
|
}
|
|
214
|
+
if (this.closed) return;
|
|
172
215
|
this.pipeHandlers.add(handler);
|
|
173
|
-
this.start().catch(() => {
|
|
216
|
+
this.start().catch((err) => IRPC_STORE.error(err, [{
|
|
217
|
+
id: this.id,
|
|
218
|
+
name: this.name
|
|
219
|
+
}]));
|
|
174
220
|
}
|
|
175
221
|
/**
|
|
176
222
|
* Binds a handler to trap any internal runtime failures independently.
|
|
@@ -182,8 +228,12 @@ var IRPCStream = class {
|
|
|
182
228
|
handler(this.error);
|
|
183
229
|
return;
|
|
184
230
|
}
|
|
231
|
+
if (this.closed) return;
|
|
185
232
|
this.errorHandlers.add(handler);
|
|
186
|
-
this.start().catch(() => {
|
|
233
|
+
this.start().catch((err) => IRPC_STORE.error(err, [{
|
|
234
|
+
id: this.id,
|
|
235
|
+
name: this.name
|
|
236
|
+
}]));
|
|
187
237
|
}
|
|
188
238
|
/**
|
|
189
239
|
* Binds a handler triggered upon terminal completion of the stream process (success or error).
|
|
@@ -195,8 +245,21 @@ var IRPCStream = class {
|
|
|
195
245
|
handler();
|
|
196
246
|
return;
|
|
197
247
|
}
|
|
248
|
+
if (this.closed) return;
|
|
198
249
|
this.closeHandlers.add(handler);
|
|
199
|
-
this.start().catch(() => {
|
|
250
|
+
this.start().catch((err) => IRPC_STORE.error(err, [{
|
|
251
|
+
id: this.id,
|
|
252
|
+
name: this.name
|
|
253
|
+
}]));
|
|
254
|
+
}
|
|
255
|
+
finish() {
|
|
256
|
+
this.closed = true;
|
|
257
|
+
this.closeHandlers.forEach((handler) => handler());
|
|
258
|
+
this.pipeHandlers.clear();
|
|
259
|
+
this.errorHandlers.clear();
|
|
260
|
+
this.closeHandlers.clear();
|
|
261
|
+
this.updatedAt = Date.now();
|
|
262
|
+
IRPC_STORE.dequeue(this);
|
|
200
263
|
}
|
|
201
264
|
};
|
|
202
265
|
|
package/dist/transport.d.ts
CHANGED
|
@@ -21,18 +21,25 @@ declare class IRPCTransport {
|
|
|
21
21
|
constructor(config?: TransportConfig | undefined);
|
|
22
22
|
/**
|
|
23
23
|
* Initiates an RPC call with the given specification and arguments.
|
|
24
|
+
* @param reader - The reader instance to attach to the RPC call.
|
|
24
25
|
* @param spec - The RPC specification defining the method to call.
|
|
25
26
|
* @param args - An array of arguments to pass to the RPC method.
|
|
26
27
|
* @param config - Optional call configuration, including timeout, retry settings, and more.
|
|
27
28
|
* @returns A promise that resolves with the RPC response data or rejects with an error.
|
|
28
29
|
*/
|
|
29
|
-
call(spec: IRPCSpec<IRPCInputs, IRPCOutput>, args: IRPCData[], config?: IRPCCallConfig): IRPCReader<IRPCData>;
|
|
30
|
+
call(spec: IRPCSpec<IRPCInputs, IRPCOutput>, args: IRPCData[], config?: IRPCCallConfig, reader?: IRPCReader<IRPCData>): IRPCReader<IRPCData>;
|
|
30
31
|
/**
|
|
31
32
|
* Schedules an RPC call for execution, implementing debouncing logic.
|
|
32
33
|
* Queued calls will be dispatched after the configured debounce delay.
|
|
33
34
|
* @param call - The RPC call to schedule.
|
|
34
35
|
*/
|
|
35
36
|
schedule(call: IRPCCall): void;
|
|
37
|
+
/**
|
|
38
|
+
* Closes an RPC call. This base implementation does nothing.
|
|
39
|
+
* Subclasses should override this method to provide closing logic.
|
|
40
|
+
* @param call - The RPC call to cancel.
|
|
41
|
+
*/
|
|
42
|
+
close(call: IRPCCall): void;
|
|
36
43
|
/**
|
|
37
44
|
* Dispatches a batch of RPC calls. This base implementation rejects all calls
|
|
38
45
|
* with a "not implemented" error. Subclasses should override this method to
|
package/dist/transport.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
|
|
2
2
|
import { ERROR_CODE, ERROR_MESSAGE } from "./error.js";
|
|
3
|
+
import { IRPCReader } from "./reader.js";
|
|
4
|
+
import { IRPC_STORE } from "./store.js";
|
|
3
5
|
import { IRPCCall } from "./call.js";
|
|
6
|
+
import { onCleanup, uuid } from "@anchorlib/core";
|
|
4
7
|
|
|
5
8
|
//#region src/transport.ts
|
|
6
9
|
/**
|
|
@@ -21,12 +24,13 @@ var IRPCTransport = class {
|
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Initiates an RPC call with the given specification and arguments.
|
|
27
|
+
* @param reader - The reader instance to attach to the RPC call.
|
|
24
28
|
* @param spec - The RPC specification defining the method to call.
|
|
25
29
|
* @param args - An array of arguments to pass to the RPC method.
|
|
26
30
|
* @param config - Optional call configuration, including timeout, retry settings, and more.
|
|
27
31
|
* @returns A promise that resolves with the RPC response data or rejects with an error.
|
|
28
32
|
*/
|
|
29
|
-
call(spec, args, config) {
|
|
33
|
+
call(spec, args, config, reader = new IRPCReader(uuid())) {
|
|
30
34
|
const payload = {
|
|
31
35
|
name: spec.name,
|
|
32
36
|
args
|
|
@@ -40,8 +44,15 @@ var IRPCTransport = class {
|
|
|
40
44
|
maxRetries,
|
|
41
45
|
retryMode,
|
|
42
46
|
retryDelay
|
|
43
|
-
});
|
|
44
|
-
|
|
47
|
+
}, reader);
|
|
48
|
+
if (spec.stream) {
|
|
49
|
+
this.dispatch([call]).finally(() => {}).catch((err) => IRPC_STORE.error(err, [{
|
|
50
|
+
id: call.id,
|
|
51
|
+
name: call.payload.name
|
|
52
|
+
}]));
|
|
53
|
+
return call.reader;
|
|
54
|
+
} else this.schedule(call);
|
|
55
|
+
onCleanup(() => this.close(call));
|
|
45
56
|
return call.reader;
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
@@ -52,12 +63,16 @@ var IRPCTransport = class {
|
|
|
52
63
|
schedule(call) {
|
|
53
64
|
const { debounce } = this.config ?? {};
|
|
54
65
|
if (debounce === false) {
|
|
55
|
-
this.dispatch([call]).finally(() => {}).catch(() => {
|
|
66
|
+
this.dispatch([call]).finally(() => {}).catch((err) => IRPC_STORE.error(err, [{
|
|
67
|
+
id: call.id,
|
|
68
|
+
name: call.payload.name
|
|
69
|
+
}]));
|
|
56
70
|
return;
|
|
57
71
|
}
|
|
58
72
|
const timeout = typeof debounce === "number" && !Number.isNaN(debounce) ? debounce : 0;
|
|
59
73
|
const dispatch = () => {
|
|
60
|
-
|
|
74
|
+
const pending = Array.from(this.queue);
|
|
75
|
+
this.dispatch(pending);
|
|
61
76
|
this.queue.clear();
|
|
62
77
|
};
|
|
63
78
|
if (!this.queue.size) if (timeout === 0) queueMicrotask(dispatch);
|
|
@@ -65,6 +80,14 @@ var IRPCTransport = class {
|
|
|
65
80
|
this.queue.add(call);
|
|
66
81
|
}
|
|
67
82
|
/**
|
|
83
|
+
* Closes an RPC call. This base implementation does nothing.
|
|
84
|
+
* Subclasses should override this method to provide closing logic.
|
|
85
|
+
* @param call - The RPC call to cancel.
|
|
86
|
+
*/
|
|
87
|
+
close(call) {
|
|
88
|
+
console.log("[irpc] Closing call", call);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
68
91
|
* Dispatches a batch of RPC calls. This base implementation rejects all calls
|
|
69
92
|
* with a "not implemented" error. Subclasses should override this method to
|
|
70
93
|
* provide actual transport mechanism.
|