@tstdl/base 0.90.19 → 0.90.20
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/api/server/gateway.js +1 -1
- package/package.json +2 -2
- package/rpc/endpoints/message-port.rpc-endpoint.d.ts +1 -0
- package/rpc/endpoints/message-port.rpc-endpoint.js +4 -2
- package/rpc/rpc-endpoint.js +16 -3
- package/rpc/rpc.js +21 -12
- package/threading/thread-pool.d.ts +3 -2
- package/utils/throw.d.ts +1 -1
- package/utils/throw.js +2 -3
package/api/server/gateway.js
CHANGED
|
@@ -111,7 +111,7 @@ let ApiGateway = class ApiGateway {
|
|
|
111
111
|
};
|
|
112
112
|
this.apis.set(resource, resourceApis);
|
|
113
113
|
}
|
|
114
|
-
const endpointImplementation = implementation[name]?.bind(implementation) ?? deferThrow(new NotImplementedError(`Endpoint ${name} for resource ${resource} not implemented.`));
|
|
114
|
+
const endpointImplementation = implementation[name]?.bind(implementation) ?? deferThrow(() => new NotImplementedError(`Endpoint ${name} for resource ${resource} not implemented.`));
|
|
115
115
|
for (const method of methods) {
|
|
116
116
|
resourceApis.endpoints.set(method, { definition: endpointDefinition, implementation: endpointImplementation });
|
|
117
117
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.90.
|
|
3
|
+
"version": "0.90.20",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"esbuild": "0.19",
|
|
127
127
|
"eslint": "8.52",
|
|
128
128
|
"eslint-import-resolver-typescript": "3.6",
|
|
129
|
-
"eslint-plugin-import": "2.
|
|
129
|
+
"eslint-plugin-import": "2.29",
|
|
130
130
|
"tsc-alias": "1.8",
|
|
131
131
|
"typedoc": "0.25",
|
|
132
132
|
"typedoc-plugin-missing-exports": "2.1",
|
|
@@ -7,6 +7,7 @@ type BrowserSource = Worker | MessagePort | Window | SharedWorker;
|
|
|
7
7
|
type NodeSource = NodeWorkerThreads.MessagePort | NodeWorkerThreads.Worker;
|
|
8
8
|
export type MessagePortRpcEndpointSource = BrowserSource | NodeSource;
|
|
9
9
|
export declare class MessagePortRpcEndpoint extends RpcEndpoint {
|
|
10
|
+
#private;
|
|
10
11
|
private readonly source;
|
|
11
12
|
private readonly _postMessage;
|
|
12
13
|
readonly supportsTransfers: boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { fromEvent, map } from 'rxjs';
|
|
1
|
+
import { Subject, fromEvent, map, takeUntil } from 'rxjs';
|
|
2
2
|
import { isBrowser } from '../../environment.js';
|
|
3
3
|
import { RpcEndpoint } from '../rpc-endpoint.js';
|
|
4
4
|
export class MessagePortRpcEndpoint extends RpcEndpoint {
|
|
5
|
+
#closeSubject = new Subject();
|
|
5
6
|
source;
|
|
6
7
|
_postMessage;
|
|
7
8
|
supportsTransfers = true;
|
|
@@ -11,7 +12,7 @@ export class MessagePortRpcEndpoint extends RpcEndpoint {
|
|
|
11
12
|
this.source = (isBrowser && ((typeof SharedWorker == 'function') && (source instanceof SharedWorker)))
|
|
12
13
|
? source.port
|
|
13
14
|
: source;
|
|
14
|
-
this.message$ = fromEvent(this.source, 'message').pipe(map((message) => ((message instanceof MessageEvent) ? { ...message.data, metadata: { source: message } } : message)));
|
|
15
|
+
this.message$ = fromEvent(this.source, 'message').pipe(takeUntil(this.#closeSubject), map((message) => ((message instanceof MessageEvent) ? { ...message.data, metadata: { source: message } } : message)));
|
|
15
16
|
this._postMessage = isBrowser
|
|
16
17
|
? (data, transfer) => this.source.postMessage(data, { transfer })
|
|
17
18
|
: (data, transfer) => this.source.postMessage(data, transfer);
|
|
@@ -25,6 +26,7 @@ export class MessagePortRpcEndpoint extends RpcEndpoint {
|
|
|
25
26
|
if (this.source instanceof MessagePort) {
|
|
26
27
|
this.source.close();
|
|
27
28
|
}
|
|
29
|
+
this.#closeSubject.next();
|
|
28
30
|
}
|
|
29
31
|
postMessage(data, transfer) {
|
|
30
32
|
this._postMessage(data, transfer);
|
package/rpc/rpc-endpoint.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { filter
|
|
1
|
+
import { filter } from 'rxjs';
|
|
2
2
|
export class RpcEndpoint {
|
|
3
3
|
async request(message, transfer) {
|
|
4
|
-
const response
|
|
5
|
-
const $response = firstValueFrom(response$);
|
|
4
|
+
const $response = getResponsePromise(this.message$, message.id);
|
|
6
5
|
await this.postMessage(message, transfer);
|
|
7
6
|
const response = await $response;
|
|
8
7
|
return response.value;
|
|
@@ -16,3 +15,17 @@ export class RpcEndpoint {
|
|
|
16
15
|
await this.postMessage(message, transfer);
|
|
17
16
|
}
|
|
18
17
|
}
|
|
18
|
+
async function getResponsePromise(message$, messageId) {
|
|
19
|
+
const response$ = message$.pipe(filter((incomingMessage) => (incomingMessage.type == 'response') && (incomingMessage.id == messageId)));
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const subscription = response$.subscribe({
|
|
22
|
+
next(value) {
|
|
23
|
+
resolve(value);
|
|
24
|
+
subscription.unsubscribe();
|
|
25
|
+
},
|
|
26
|
+
complete() {
|
|
27
|
+
reject(new Error('RpcEndpoint was closed while waiting for response.'));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
package/rpc/rpc.js
CHANGED
|
@@ -6,7 +6,7 @@ import { deserialize, registerSerializer, serialize } from '../serializer/index.
|
|
|
6
6
|
import { hasOwnProperty } from '../utils/object/object.js';
|
|
7
7
|
import { reflectMethods } from '../utils/proxy.js';
|
|
8
8
|
import { getRandomString } from '../utils/random.js';
|
|
9
|
-
import { _throw } from '../utils/throw.js';
|
|
9
|
+
import { _throw, deferThrow } from '../utils/throw.js';
|
|
10
10
|
import { assert, isDefined } from '../utils/type-guards.js';
|
|
11
11
|
import { MessagePortRpcEndpoint } from './endpoints/message-port.rpc-endpoint.js';
|
|
12
12
|
import { createRpcMessage } from './model.js';
|
|
@@ -15,10 +15,16 @@ import { RpcError, RpcRemoteError } from './rpc-error.js';
|
|
|
15
15
|
const markedTransfers = new WeakMap();
|
|
16
16
|
const serializationTargets = new WeakMap();
|
|
17
17
|
const proxyTargets = new WeakSet();
|
|
18
|
+
const responsibleEndpoints = new WeakSet();
|
|
18
19
|
const proxyFinalizationRegistry = new FinalizationRegistry(async (data) => {
|
|
19
20
|
const message = createRpcMessage('release-proxy', { proxyId: data.id });
|
|
20
21
|
await data.endpoint.postMessage(message);
|
|
22
|
+
if (responsibleEndpoints.has(data.endpoint)) {
|
|
23
|
+
data.endpoint.close();
|
|
24
|
+
}
|
|
21
25
|
});
|
|
26
|
+
class RpcProxy {
|
|
27
|
+
}
|
|
22
28
|
// eslint-disable-next-line @typescript-eslint/no-redeclare, @typescript-eslint/naming-convention
|
|
23
29
|
export const Rpc = {
|
|
24
30
|
async connect(endpointOrSource, name = 'default') {
|
|
@@ -60,8 +66,6 @@ export const Rpc = {
|
|
|
60
66
|
}
|
|
61
67
|
};
|
|
62
68
|
function createProxy(endpoint, id, path = []) {
|
|
63
|
-
class RpcProxy {
|
|
64
|
-
}
|
|
65
69
|
// eslint-disable-next-line prefer-const
|
|
66
70
|
let proxy;
|
|
67
71
|
const handlers = {
|
|
@@ -97,13 +101,13 @@ function createProxy(endpoint, id, path = []) {
|
|
|
97
101
|
};
|
|
98
102
|
for (const method of reflectMethods) {
|
|
99
103
|
if (!hasOwnProperty(handlers, method)) {
|
|
100
|
-
handlers[method] = () => {
|
|
101
|
-
throw new Error(`${method} not supported on rpc proxies.`);
|
|
102
|
-
};
|
|
104
|
+
handlers[method] = deferThrow(() => new Error(`${method} not supported on rpc proxies.`));
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
proxy = new Proxy(RpcProxy, handlers);
|
|
106
|
-
|
|
108
|
+
if (path.length == 0) {
|
|
109
|
+
proxyFinalizationRegistry.register(proxy, { id, endpoint });
|
|
110
|
+
}
|
|
107
111
|
return proxy;
|
|
108
112
|
}
|
|
109
113
|
function exposeConnectable(object, endpoint, name) {
|
|
@@ -120,9 +124,9 @@ function exposeConnectable(object, endpoint, name) {
|
|
|
120
124
|
});
|
|
121
125
|
}
|
|
122
126
|
function exposeObject(object, endpoint, proxyId) {
|
|
123
|
-
const
|
|
127
|
+
const releaseSubject = new Subject();
|
|
124
128
|
endpoint.message$
|
|
125
|
-
.pipe(takeUntil(
|
|
129
|
+
.pipe(takeUntil(releaseSubject), filter((message) => (message.proxyId == proxyId)))
|
|
126
130
|
.subscribe(async (message) => {
|
|
127
131
|
try {
|
|
128
132
|
switch (message.type) {
|
|
@@ -154,7 +158,10 @@ function exposeObject(object, endpoint, proxyId) {
|
|
|
154
158
|
break;
|
|
155
159
|
}
|
|
156
160
|
case 'release-proxy': {
|
|
157
|
-
|
|
161
|
+
releaseSubject.next();
|
|
162
|
+
if (responsibleEndpoints.has(endpoint)) {
|
|
163
|
+
endpoint.close();
|
|
164
|
+
}
|
|
158
165
|
break;
|
|
159
166
|
}
|
|
160
167
|
case 'response':
|
|
@@ -200,6 +207,7 @@ function createProxyValue(value, endpoint) {
|
|
|
200
207
|
if (endpoint.supportsTransfers) {
|
|
201
208
|
const { port1, port2 } = new MessageChannel();
|
|
202
209
|
const newEndpoint = new MessagePortRpcEndpoint(port1);
|
|
210
|
+
responsibleEndpoints.add(newEndpoint);
|
|
203
211
|
newEndpoint.start();
|
|
204
212
|
exposeObject(value, newEndpoint, id);
|
|
205
213
|
return [{ type: 'proxy', id, port: port2 }, [port2]];
|
|
@@ -224,6 +232,9 @@ function parseRpcMessageValue(value, endpoint) {
|
|
|
224
232
|
}
|
|
225
233
|
case 'proxy': {
|
|
226
234
|
const proxyEndpoint = (isDefined(value.port)) ? new MessagePortRpcEndpoint(value.port) : endpoint;
|
|
235
|
+
if (isDefined(value.port)) {
|
|
236
|
+
responsibleEndpoints.add(proxyEndpoint);
|
|
237
|
+
}
|
|
227
238
|
proxyEndpoint.start();
|
|
228
239
|
return createProxy(proxyEndpoint, value.id);
|
|
229
240
|
}
|
|
@@ -243,8 +254,6 @@ function deref(object, path, skipLast = false) {
|
|
|
243
254
|
}
|
|
244
255
|
return { parent, value };
|
|
245
256
|
}
|
|
246
|
-
class RpcProxy {
|
|
247
|
-
}
|
|
248
257
|
registerSerializer(RpcProxy, 'RpcProxy', () => _throw(new Error('Not supported.')), (data, _, { rpcEndpoint }) => parseRpcMessageValue({ type: 'proxy', ...data }, rpcEndpoint));
|
|
249
258
|
function getRpcProxySerializationReplacer(endpoint, transfer) {
|
|
250
259
|
function rpcProxySerializationReplacer(value) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type * as NodeWorkerThreads from 'node:worker_threads';
|
|
3
|
+
import type { LiteralUnion } from 'type-fest';
|
|
2
4
|
import type { AsyncDisposable } from '../disposable/index.js';
|
|
3
5
|
import { disposeAsync } from '../disposable/index.js';
|
|
4
6
|
import type { Logger } from '../logger/index.js';
|
|
5
|
-
import type * as NodeWorkerThreads from 'node:worker_threads';
|
|
6
7
|
import type { ThreadWorker } from './thread-worker.js';
|
|
7
8
|
export type ThreadOptions = (WorkerOptions | NodeWorkerThreads.WorkerOptions) & {
|
|
8
9
|
threadCount?: number;
|
|
@@ -15,6 +16,6 @@ export declare class ThreadPool implements AsyncDisposable {
|
|
|
15
16
|
dispose(): Promise<void>;
|
|
16
17
|
[disposeAsync](): Promise<void>;
|
|
17
18
|
getProcessor<T extends ThreadWorker>(name?: string): (...args: Parameters<T>) => Promise<ReturnType<T>>;
|
|
18
|
-
process<T extends ThreadWorker>(name:
|
|
19
|
+
process<T extends ThreadWorker>(name: LiteralUnion<'default', string>, ...args: Parameters<T>): Promise<ReturnType<T>>;
|
|
19
20
|
private spawn;
|
|
20
21
|
}
|
package/utils/throw.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function _throw(value: any): never;
|
|
2
|
-
export declare function deferThrow(
|
|
2
|
+
export declare function deferThrow(valueProvider: () => any): () => never;
|
package/utils/throw.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export function _throw(value) {
|
|
2
2
|
throw value;
|
|
3
3
|
}
|
|
4
|
-
export function deferThrow(
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
4
|
+
export function deferThrow(valueProvider) {
|
|
6
5
|
return function deferThrow() {
|
|
7
|
-
throw
|
|
6
|
+
throw valueProvider();
|
|
8
7
|
};
|
|
9
8
|
}
|