@tstdl/base 0.90.18 → 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/cancellation/token.d.ts +1 -1
- package/cancellation/token.js +6 -4
- package/package.json +3 -3
- 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 +22 -13
- package/threading/thread-pool.d.ts +3 -2
- package/utils/feedable-async-iterable.d.ts +0 -1
- package/utils/feedable-async-iterable.js +5 -10
- 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/cancellation/token.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export declare class CancellationSignal implements PromiseLike<void>, Subscribab
|
|
|
88
88
|
export declare class CancellationToken extends CancellationSignal {
|
|
89
89
|
#private;
|
|
90
90
|
/** Signal for this token */
|
|
91
|
-
|
|
91
|
+
get signal(): CancellationSignal;
|
|
92
92
|
/**
|
|
93
93
|
* @param initialState which state to initialze this token to
|
|
94
94
|
* - `false`: unset
|
package/cancellation/token.js
CHANGED
|
@@ -86,8 +86,11 @@ export class CancellationSignal {
|
|
|
86
86
|
}
|
|
87
87
|
export class CancellationToken extends CancellationSignal {
|
|
88
88
|
#stateSubject;
|
|
89
|
+
#signal;
|
|
89
90
|
/** Signal for this token */
|
|
90
|
-
signal
|
|
91
|
+
get signal() {
|
|
92
|
+
return (this.#signal ??= new CancellationSignal(this.#stateSubject));
|
|
93
|
+
}
|
|
91
94
|
/**
|
|
92
95
|
* @param initialState which state to initialze this token to
|
|
93
96
|
* - `false`: unset
|
|
@@ -97,8 +100,7 @@ export class CancellationToken extends CancellationSignal {
|
|
|
97
100
|
constructor(initialState = false) {
|
|
98
101
|
const stateSubject = new BehaviorSubject(initialState);
|
|
99
102
|
super(stateSubject);
|
|
100
|
-
this.#stateSubject =
|
|
101
|
-
this.signal = new CancellationSignal(this.#stateSubject);
|
|
103
|
+
this.#stateSubject = stateSubject;
|
|
102
104
|
}
|
|
103
105
|
static from(source, config) {
|
|
104
106
|
const source$ = (source instanceof AbortSignal) ? fromEvent(source, 'abort', () => true)
|
|
@@ -126,7 +128,7 @@ export class CancellationToken extends CancellationSignal {
|
|
|
126
128
|
* Become a child of the provided parent. Events from the parent are propagated to this token. Events from this token are *not* propagated to the parent.
|
|
127
129
|
*/
|
|
128
130
|
inherit(parent, config) {
|
|
129
|
-
const state$ = (
|
|
131
|
+
const { state$ } = (parent instanceof CancellationToken) ? parent.signal : parent;
|
|
130
132
|
CancellationToken.connect(state$, this, config);
|
|
131
133
|
return this;
|
|
132
134
|
}
|
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"
|
|
@@ -124,9 +124,9 @@
|
|
|
124
124
|
"@typescript-eslint/parser": "6.8",
|
|
125
125
|
"concurrently": "8.2",
|
|
126
126
|
"esbuild": "0.19",
|
|
127
|
-
"eslint": "8.
|
|
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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
|
+
import { filter, Subject, takeUntil } from 'rxjs';
|
|
3
4
|
import { NotImplementedError } from '../errors/not-implemented.error.js';
|
|
4
5
|
import { deserialize, registerSerializer, serialize } from '../serializer/index.js';
|
|
5
6
|
import { hasOwnProperty } from '../utils/object/object.js';
|
|
6
7
|
import { reflectMethods } from '../utils/proxy.js';
|
|
7
8
|
import { getRandomString } from '../utils/random.js';
|
|
8
|
-
import { _throw } from '../utils/throw.js';
|
|
9
|
+
import { _throw, deferThrow } from '../utils/throw.js';
|
|
9
10
|
import { assert, isDefined } from '../utils/type-guards.js';
|
|
10
|
-
import { filter, Subject, takeUntil } from 'rxjs';
|
|
11
11
|
import { MessagePortRpcEndpoint } from './endpoints/message-port.rpc-endpoint.js';
|
|
12
12
|
import { createRpcMessage } from './model.js';
|
|
13
13
|
import { RpcEndpoint } from './rpc-endpoint.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
|
}
|
|
@@ -2,9 +2,9 @@ import { firstValueFrom, Subject } from 'rxjs';
|
|
|
2
2
|
import { CancellationToken } from '../cancellation/token.js';
|
|
3
3
|
import { CircularBuffer } from '../data-structures/circular-buffer.js';
|
|
4
4
|
export class FeedableAsyncIterable {
|
|
5
|
-
readSubject;
|
|
6
|
-
closedToken;
|
|
7
|
-
buffer;
|
|
5
|
+
readSubject = new Subject();
|
|
6
|
+
closedToken = new CancellationToken();
|
|
7
|
+
buffer = new CircularBuffer();
|
|
8
8
|
get $read() {
|
|
9
9
|
return firstValueFrom(this.readSubject);
|
|
10
10
|
}
|
|
@@ -17,14 +17,9 @@ export class FeedableAsyncIterable {
|
|
|
17
17
|
get bufferSize() {
|
|
18
18
|
return this.buffer.size;
|
|
19
19
|
}
|
|
20
|
-
constructor() {
|
|
21
|
-
this.readSubject = new Subject();
|
|
22
|
-
this.closedToken = new CancellationToken();
|
|
23
|
-
this.buffer = new CircularBuffer();
|
|
24
|
-
}
|
|
25
20
|
feed(item) {
|
|
26
21
|
if (this.closed) {
|
|
27
|
-
throw new Error('
|
|
22
|
+
throw new Error('Feedable is already closed.');
|
|
28
23
|
}
|
|
29
24
|
this.buffer.add({ item, error: undefined });
|
|
30
25
|
}
|
|
@@ -33,7 +28,7 @@ export class FeedableAsyncIterable {
|
|
|
33
28
|
}
|
|
34
29
|
throw(error) {
|
|
35
30
|
if (this.closed) {
|
|
36
|
-
throw new Error('
|
|
31
|
+
throw new Error('Feedable is already closed.');
|
|
37
32
|
}
|
|
38
33
|
this.buffer.add({ item: undefined, error });
|
|
39
34
|
this.end();
|
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
|
}
|