@xylabs/threads 4.7.7 → 4.7.9
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/browser/worker/worker.browser.mjs +3 -3
- package/dist/browser/worker/worker.browser.mjs.map +1 -1
- package/dist/types/worker/worker.browser.d.ts.map +1 -1
- package/package.json +6 -8
- package/src/common.ts +23 -0
- package/src/index-browser.ts +11 -0
- package/src/index-node.ts +11 -0
- package/src/master/get-bundle-url.browser.ts +32 -0
- package/src/master/implementation.browser.ts +82 -0
- package/src/master/implementation.node.ts +208 -0
- package/src/master/index-browser.ts +19 -0
- package/src/master/index-node.ts +19 -0
- package/src/master/invocation-proxy.ts +151 -0
- package/src/master/pool-browser.ts +400 -0
- package/src/master/pool-node.ts +400 -0
- package/src/master/pool-types.ts +83 -0
- package/src/master/register.ts +11 -0
- package/src/master/spawn.ts +172 -0
- package/src/master/thread.ts +29 -0
- package/src/observable-promise.ts +184 -0
- package/src/observable.ts +44 -0
- package/src/ponyfills.ts +31 -0
- package/src/promise.ts +26 -0
- package/src/serializers.ts +68 -0
- package/src/symbols.ts +5 -0
- package/src/transferable.ts +69 -0
- package/src/types/master.ts +132 -0
- package/src/types/messages.ts +72 -0
- package/src/types/worker.ts +14 -0
- package/src/worker/WorkerGlobalScope.ts +5 -0
- package/src/worker/expose.ts +234 -0
- package/src/worker/is-observable.d.ts +7 -0
- package/src/worker/worker.browser.ts +70 -0
- package/src/worker/worker.node.ts +68 -0
- package/CHANGELOG.md +0 -11
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/* eslint-disable unicorn/no-thenable */
|
|
2
|
+
/* eslint-disable @typescript-eslint/member-ordering */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
4
|
+
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
7
|
+
/* eslint-disable unicorn/no-this-assignment */
|
|
8
|
+
import type { ObservableLike, SubscriptionObserver } from 'observable-fns'
|
|
9
|
+
import { Observable } from 'observable-fns'
|
|
10
|
+
|
|
11
|
+
type OnFulfilled<T, Result = void> = (value: T) => Result
|
|
12
|
+
type OnRejected<Result = void> = (error: Error) => Result
|
|
13
|
+
|
|
14
|
+
type Initializer<T> = (observer: SubscriptionObserver<T>) => UnsubscribeFn | void
|
|
15
|
+
|
|
16
|
+
type Thenable<T> = { then: (onFulfilled?: (value: T) => any, onRejected?: (error: any) => any) => any }
|
|
17
|
+
|
|
18
|
+
type UnsubscribeFn = () => void
|
|
19
|
+
|
|
20
|
+
const doNothing = () => {}
|
|
21
|
+
const returnInput = <T>(input: T): T => input
|
|
22
|
+
const runDeferred = (fn: () => void) => Promise.resolve().then(fn)
|
|
23
|
+
|
|
24
|
+
function fail(error: Error): never {
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isThenable(thing: any): thing is Thenable<any> {
|
|
29
|
+
return thing && typeof thing.then === 'function'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a hybrid, combining the APIs of an Observable and a Promise.
|
|
34
|
+
*
|
|
35
|
+
* It is used to proxy async process states when we are initially not sure
|
|
36
|
+
* if that async process will yield values once (-> Promise) or multiple
|
|
37
|
+
* times (-> Observable).
|
|
38
|
+
*
|
|
39
|
+
* Note that the observable promise inherits some of the observable's characteristics:
|
|
40
|
+
* The `init` function will be called *once for every time anyone subscribes to it*.
|
|
41
|
+
*
|
|
42
|
+
* If this is undesired, derive a hot observable from it using `makeHot()` and
|
|
43
|
+
* subscribe to that.
|
|
44
|
+
*/
|
|
45
|
+
export class ObservablePromise<T> extends Observable<T> implements Promise<T> {
|
|
46
|
+
readonly [Symbol.toStringTag] = '[object ObservablePromise]'
|
|
47
|
+
private initHasRun = false
|
|
48
|
+
private fulfillmentCallbacks: Array<OnFulfilled<T>> = []
|
|
49
|
+
private rejectionCallbacks: OnRejected[] = []
|
|
50
|
+
|
|
51
|
+
private firstValue: T | undefined
|
|
52
|
+
private firstValueSet = false
|
|
53
|
+
private rejection: Error | undefined
|
|
54
|
+
private state: 'fulfilled' | 'pending' | 'rejected' = 'pending'
|
|
55
|
+
|
|
56
|
+
constructor(init: Initializer<T>) {
|
|
57
|
+
super((originalObserver: SubscriptionObserver<T>) => {
|
|
58
|
+
// tslint:disable-next-line no-this-assignment
|
|
59
|
+
const self = this
|
|
60
|
+
const observer: SubscriptionObserver<T> = {
|
|
61
|
+
...originalObserver,
|
|
62
|
+
complete() {
|
|
63
|
+
originalObserver.complete()
|
|
64
|
+
self.onCompletion()
|
|
65
|
+
},
|
|
66
|
+
error(error: Error) {
|
|
67
|
+
originalObserver.error(error)
|
|
68
|
+
self.onError(error)
|
|
69
|
+
},
|
|
70
|
+
next(value: T) {
|
|
71
|
+
originalObserver.next(value)
|
|
72
|
+
self.onNext(value)
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
this.initHasRun = true
|
|
78
|
+
return init(observer)
|
|
79
|
+
} catch (error) {
|
|
80
|
+
observer.error(error)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private onNext(value: T) {
|
|
86
|
+
if (!this.firstValueSet) {
|
|
87
|
+
this.firstValue = value
|
|
88
|
+
this.firstValueSet = true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private onError(error: Error) {
|
|
93
|
+
this.state = 'rejected'
|
|
94
|
+
this.rejection = error
|
|
95
|
+
|
|
96
|
+
for (const onRejected of this.rejectionCallbacks) {
|
|
97
|
+
// Promisifying the call to turn errors into unhandled promise rejections
|
|
98
|
+
// instead of them failing sync and cancelling the iteration
|
|
99
|
+
runDeferred(() => onRejected(error))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private onCompletion() {
|
|
104
|
+
this.state = 'fulfilled'
|
|
105
|
+
|
|
106
|
+
for (const onFulfilled of this.fulfillmentCallbacks) {
|
|
107
|
+
// Promisifying the call to turn errors into unhandled promise rejections
|
|
108
|
+
// instead of them failing sync and cancelling the iteration
|
|
109
|
+
runDeferred(() => onFulfilled(this.firstValue as T))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
then<TResult1 = T, TResult2 = never>(
|
|
114
|
+
onFulfilledRaw?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
115
|
+
onRejectedRaw?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
|
116
|
+
): Promise<TResult1 | TResult2> {
|
|
117
|
+
const onFulfilled: OnFulfilled<T, TResult1> = onFulfilledRaw || (returnInput as any)
|
|
118
|
+
const onRejected = onRejectedRaw || fail
|
|
119
|
+
let onRejectedCalled = false
|
|
120
|
+
|
|
121
|
+
return new Promise<TResult1 | TResult2>((resolve, reject) => {
|
|
122
|
+
const rejectionCallback = (error: Error) => {
|
|
123
|
+
if (onRejectedCalled) return
|
|
124
|
+
onRejectedCalled = true
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
resolve(onRejected(error))
|
|
128
|
+
} catch (anotherError) {
|
|
129
|
+
reject(anotherError)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const fulfillmentCallback = (value: T) => {
|
|
133
|
+
try {
|
|
134
|
+
resolve(onFulfilled(value))
|
|
135
|
+
} catch (ex) {
|
|
136
|
+
const error = ex as Error
|
|
137
|
+
rejectionCallback(error)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!this.initHasRun) {
|
|
141
|
+
this.subscribe({ error: rejectionCallback })
|
|
142
|
+
}
|
|
143
|
+
if (this.state === 'fulfilled') {
|
|
144
|
+
return resolve(onFulfilled(this.firstValue as T))
|
|
145
|
+
}
|
|
146
|
+
if (this.state === 'rejected') {
|
|
147
|
+
onRejectedCalled = true
|
|
148
|
+
return resolve(onRejected(this.rejection as Error))
|
|
149
|
+
}
|
|
150
|
+
this.fulfillmentCallbacks.push(fulfillmentCallback)
|
|
151
|
+
this.rejectionCallbacks.push(rejectionCallback)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
catch<Result = never>(onRejected: ((error: Error) => Promise<Result> | Result) | null | undefined) {
|
|
156
|
+
return this.then(undefined, onRejected) as Promise<Result>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
finally(onCompleted?: (() => void) | null | undefined) {
|
|
160
|
+
const handler = onCompleted || doNothing
|
|
161
|
+
return this.then(
|
|
162
|
+
(value: T) => {
|
|
163
|
+
handler()
|
|
164
|
+
return value
|
|
165
|
+
},
|
|
166
|
+
() => handler(),
|
|
167
|
+
) as Promise<T>
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static override from<T>(thing: Observable<T> | ObservableLike<T> | ArrayLike<T> | Thenable<T>): ObservablePromise<T> {
|
|
171
|
+
return isThenable(thing)
|
|
172
|
+
? new ObservablePromise((observer) => {
|
|
173
|
+
const onFulfilled = (value: T) => {
|
|
174
|
+
observer.next(value)
|
|
175
|
+
observer.complete()
|
|
176
|
+
}
|
|
177
|
+
const onRejected = (error: any) => {
|
|
178
|
+
observer.error(error)
|
|
179
|
+
}
|
|
180
|
+
thing.then(onFulfilled, onRejected)
|
|
181
|
+
})
|
|
182
|
+
: (super.from(thing) as ObservablePromise<T>)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type { ObservableLike, SubscriptionObserver } from 'observable-fns'
|
|
3
|
+
import { Observable } from 'observable-fns'
|
|
4
|
+
|
|
5
|
+
const $observers = Symbol('observers')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Observable subject. Implements the Observable interface, but also exposes
|
|
9
|
+
* the `next()`, `error()`, `complete()` methods to initiate observable
|
|
10
|
+
* updates "from the outside".
|
|
11
|
+
*
|
|
12
|
+
* Use `Observable.from(subject)` to derive an observable that proxies all
|
|
13
|
+
* values, errors and the completion raised on this subject, but does not
|
|
14
|
+
* expose the `next()`, `error()`, `complete()` methods.
|
|
15
|
+
*/
|
|
16
|
+
export class Subject<T> extends Observable<T> implements ObservableLike<T> {
|
|
17
|
+
private [$observers]: Array<SubscriptionObserver<T>>
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
super((observer) => {
|
|
21
|
+
this[$observers] = [...(this[$observers] || []), observer]
|
|
22
|
+
const unsubscribe = () => {
|
|
23
|
+
this[$observers] = this[$observers].filter(someObserver => someObserver !== observer)
|
|
24
|
+
}
|
|
25
|
+
return unsubscribe
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
this[$observers] = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
complete() {
|
|
32
|
+
for (const observer of this[$observers]) observer.complete()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
error(error: any) {
|
|
36
|
+
for (const observer of this[$observers]) observer.error(error)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
next(value: T) {
|
|
40
|
+
for (const observer of this[$observers]) observer.next(value)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { Observable } from 'observable-fns'
|
package/src/ponyfills.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
export type SettlementResult<T> =
|
|
3
|
+
| {
|
|
4
|
+
status: 'fulfilled'
|
|
5
|
+
value: T
|
|
6
|
+
}
|
|
7
|
+
| {
|
|
8
|
+
reason: any
|
|
9
|
+
status: 'rejected'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Based on <https://github.com/es-shims/Promise.allSettled/blob/master/implementation.js>
|
|
13
|
+
export function allSettled<T>(values: T[]): Promise<Array<SettlementResult<T>>> {
|
|
14
|
+
return Promise.all(
|
|
15
|
+
values.map((item) => {
|
|
16
|
+
const onFulfill = (value: T) => {
|
|
17
|
+
return { status: 'fulfilled', value } as const
|
|
18
|
+
}
|
|
19
|
+
const onReject = (reason: any) => {
|
|
20
|
+
return { reason, status: 'rejected' } as const
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const itemPromise = Promise.resolve(item)
|
|
24
|
+
try {
|
|
25
|
+
return itemPromise.then(onFulfill, onReject)
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return Promise.reject(error)
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
31
|
+
}
|
package/src/promise.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// eslint-disable-next-line unicorn/no-useless-undefined
|
|
2
|
+
const doNothing = () => undefined
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new promise and exposes its resolver function.
|
|
6
|
+
* Use with care!
|
|
7
|
+
*/
|
|
8
|
+
export function createPromiseWithResolver<T>(): [Promise<T>, (result: T) => void] {
|
|
9
|
+
let alreadyResolved = false
|
|
10
|
+
let resolvedTo: T
|
|
11
|
+
let resolver: (value: T | PromiseLike<T>) => void = doNothing
|
|
12
|
+
|
|
13
|
+
const promise = new Promise<T>((resolve) => {
|
|
14
|
+
if (alreadyResolved) {
|
|
15
|
+
resolve(resolvedTo)
|
|
16
|
+
} else {
|
|
17
|
+
resolver = resolve
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
const exposedResolver = (value: T) => {
|
|
21
|
+
alreadyResolved = true
|
|
22
|
+
resolvedTo = value
|
|
23
|
+
resolver(resolvedTo)
|
|
24
|
+
}
|
|
25
|
+
return [promise, exposedResolver]
|
|
26
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable import-x/no-internal-modules */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import type { SerializedError } from './types/messages.ts'
|
|
4
|
+
|
|
5
|
+
export interface Serializer<Msg = JsonSerializable, Input = any> {
|
|
6
|
+
deserialize(message: Msg): Input
|
|
7
|
+
serialize(input: Input): Msg
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SerializerImplementation<Msg = JsonSerializable, Input = any> {
|
|
11
|
+
deserialize(message: Msg, defaultDeserialize: (msg: Msg) => Input): Input
|
|
12
|
+
serialize(input: Input, defaultSerialize: (inp: Input) => Msg): Msg
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function extendSerializer<MessageType, InputType = any>(
|
|
16
|
+
extend: Serializer<MessageType, InputType>,
|
|
17
|
+
implementation: SerializerImplementation<MessageType, InputType>,
|
|
18
|
+
): Serializer<MessageType, InputType> {
|
|
19
|
+
const fallbackDeserializer = extend.deserialize.bind(extend)
|
|
20
|
+
const fallbackSerializer = extend.serialize.bind(extend)
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
deserialize(message: MessageType): InputType {
|
|
24
|
+
return implementation.deserialize(message, fallbackDeserializer)
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
serialize(input: InputType): MessageType {
|
|
28
|
+
return implementation.serialize(input, fallbackSerializer)
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type JsonSerializablePrimitive = string | number | boolean | null
|
|
34
|
+
|
|
35
|
+
type JsonSerializableObject = {
|
|
36
|
+
[key: string]: JsonSerializablePrimitive | JsonSerializablePrimitive[] | JsonSerializableObject | JsonSerializableObject[] | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type JsonSerializable = JsonSerializablePrimitive | JsonSerializablePrimitive[] | JsonSerializableObject | JsonSerializableObject[]
|
|
40
|
+
|
|
41
|
+
const DefaultErrorSerializer: Serializer<SerializedError, Error> = {
|
|
42
|
+
deserialize(message: SerializedError): Error {
|
|
43
|
+
return Object.assign(new Error(message.message), {
|
|
44
|
+
name: message.name,
|
|
45
|
+
stack: message.stack,
|
|
46
|
+
})
|
|
47
|
+
},
|
|
48
|
+
serialize(error: Error): SerializedError {
|
|
49
|
+
return {
|
|
50
|
+
__error_marker: '$$error',
|
|
51
|
+
message: error.message,
|
|
52
|
+
name: error.name,
|
|
53
|
+
stack: error.stack,
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const isSerializedError = (thing: any): thing is SerializedError =>
|
|
59
|
+
thing && typeof thing === 'object' && '__error_marker' in thing && thing.__error_marker === '$$error'
|
|
60
|
+
|
|
61
|
+
export const DefaultSerializer: Serializer<JsonSerializable> = {
|
|
62
|
+
deserialize(message: JsonSerializable): any {
|
|
63
|
+
return isSerializedError(message) ? DefaultErrorSerializer.deserialize(message) : message
|
|
64
|
+
},
|
|
65
|
+
serialize(input: any): JsonSerializable {
|
|
66
|
+
return input instanceof Error ? (DefaultErrorSerializer.serialize(input) as any as JsonSerializable) : input
|
|
67
|
+
},
|
|
68
|
+
}
|
package/src/symbols.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { $transferable } from './symbols.ts'
|
|
3
|
+
|
|
4
|
+
export interface TransferDescriptor<T = any> {
|
|
5
|
+
[$transferable]: true
|
|
6
|
+
send: T
|
|
7
|
+
transferables: Transferable[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isTransferable(thing: any): thing is Transferable {
|
|
11
|
+
if (!thing || typeof thing !== 'object') return false
|
|
12
|
+
// Don't check too thoroughly, since the list of transferable things in JS might grow over time
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isTransferDescriptor(thing: any): thing is TransferDescriptor {
|
|
17
|
+
return thing && typeof thing === 'object' && thing[$transferable]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Mark a transferable object as such, so it will no be serialized and
|
|
22
|
+
* deserialized on messaging with the main thread, but to transfer
|
|
23
|
+
* ownership of it to the receiving thread.
|
|
24
|
+
*
|
|
25
|
+
* Only works with array buffers, message ports and few more special
|
|
26
|
+
* types of objects, but it's much faster than serializing and
|
|
27
|
+
* deserializing them.
|
|
28
|
+
*
|
|
29
|
+
* Note:
|
|
30
|
+
* The transferable object cannot be accessed by this thread again
|
|
31
|
+
* unless the receiving thread transfers it back again!
|
|
32
|
+
*
|
|
33
|
+
* @param transferable Array buffer, message port or similar.
|
|
34
|
+
* @see <https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast>
|
|
35
|
+
*/
|
|
36
|
+
export function Transfer(transferable: Transferable): TransferDescriptor
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Mark transferable objects within an arbitrary object or array as
|
|
40
|
+
* being a transferable object. They will then not be serialized
|
|
41
|
+
* and deserialized on messaging with the main thread, but ownership
|
|
42
|
+
* of them will be tranferred to the receiving thread.
|
|
43
|
+
*
|
|
44
|
+
* Only array buffers, message ports and few more special types of
|
|
45
|
+
* objects can be transferred, but it's much faster than serializing and
|
|
46
|
+
* deserializing them.
|
|
47
|
+
*
|
|
48
|
+
* Note:
|
|
49
|
+
* The transferable object cannot be accessed by this thread again
|
|
50
|
+
* unless the receiving thread transfers it back again!
|
|
51
|
+
*
|
|
52
|
+
* @param transferable Array buffer, message port or similar.
|
|
53
|
+
* @see <https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast>
|
|
54
|
+
*/
|
|
55
|
+
export function Transfer<T>(payload: T, transferables: Transferable[]): TransferDescriptor
|
|
56
|
+
|
|
57
|
+
export function Transfer<T>(payload: T, transferables?: Transferable[]): TransferDescriptor {
|
|
58
|
+
console.log('Transfer')
|
|
59
|
+
if (!transferables) {
|
|
60
|
+
if (!isTransferable(payload)) throw new Error('Not transferable')
|
|
61
|
+
transferables = [payload]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
[$transferable]: true,
|
|
66
|
+
send: payload,
|
|
67
|
+
transferables,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/// <reference lib="dom" />
|
|
3
|
+
// tslint:disable max-classes-per-file
|
|
4
|
+
|
|
5
|
+
// Cannot use `compilerOptions.esModuleInterop` and default import syntax
|
|
6
|
+
// See <https://github.com/microsoft/TypeScript/issues/28009>
|
|
7
|
+
import type { Observable } from 'observable-fns'
|
|
8
|
+
|
|
9
|
+
import type { ObservablePromise } from '../observable-promise.ts'
|
|
10
|
+
import type {
|
|
11
|
+
$errors, $events, $terminate, $worker,
|
|
12
|
+
} from '../symbols.ts'
|
|
13
|
+
import type { TransferDescriptor } from '../transferable.ts'
|
|
14
|
+
|
|
15
|
+
interface ObservableLikeSubscription {
|
|
16
|
+
unsubscribe(): any
|
|
17
|
+
}
|
|
18
|
+
interface ObservableLike<T> {
|
|
19
|
+
subscribe(onNext: (value: T) => any, onError?: (error: any) => any, onComplete?: () => any): ObservableLikeSubscription
|
|
20
|
+
subscribe(listeners: { complete?(): any; error?(error: any): any; next?(value: T): any }): ObservableLikeSubscription
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type StripAsync<Type> =
|
|
24
|
+
Type extends Promise<infer PromiseBaseType> ? PromiseBaseType
|
|
25
|
+
: Type extends ObservableLike<infer ObservableBaseType> ? ObservableBaseType
|
|
26
|
+
: Type
|
|
27
|
+
|
|
28
|
+
type StripTransfer<Type> = Type extends TransferDescriptor<infer BaseType> ? BaseType : Type
|
|
29
|
+
|
|
30
|
+
export type ModuleMethods = { [methodName: string]: (...args: any) => any }
|
|
31
|
+
|
|
32
|
+
type ProxyableArgs<Args extends any[]> =
|
|
33
|
+
Args extends [arg0: infer Arg0, ...rest: infer RestArgs] ? [Arg0 extends Transferable ? Arg0 | TransferDescriptor<Arg0> : Arg0, ...RestArgs] : Args
|
|
34
|
+
|
|
35
|
+
export type ProxyableFunction<Args extends any[], ReturnType> =
|
|
36
|
+
Args extends [] ? () => ObservablePromise<StripTransfer<StripAsync<ReturnType>>>
|
|
37
|
+
: (...args: ProxyableArgs<Args>) => ObservablePromise<StripTransfer<StripAsync<ReturnType>>>
|
|
38
|
+
|
|
39
|
+
export type ModuleProxy<Methods extends ModuleMethods> = {
|
|
40
|
+
[method in keyof Methods]: ProxyableFunction<Parameters<Methods[method]>, ReturnType<Methods[method]>>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PrivateThreadProps {
|
|
44
|
+
[$errors]: Observable<Error>
|
|
45
|
+
[$events]: Observable<WorkerEvent>
|
|
46
|
+
[$terminate]: () => Promise<void>
|
|
47
|
+
[$worker]: Worker
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type FunctionThread<Args extends any[] = any[], ReturnType = any> = ProxyableFunction<Args, ReturnType> & PrivateThreadProps
|
|
51
|
+
export type ModuleThread<Methods extends ModuleMethods = any> = ModuleProxy<Methods> & PrivateThreadProps
|
|
52
|
+
|
|
53
|
+
// We have those extra interfaces to keep the general non-specific `Thread` type
|
|
54
|
+
// as an interface, so it's displayed concisely in any TypeScript compiler output.
|
|
55
|
+
interface AnyFunctionThread extends PrivateThreadProps {
|
|
56
|
+
(...args: any[]): ObservablePromise<any>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// tslint:disable-next-line no-empty-interface
|
|
60
|
+
interface AnyModuleThread extends PrivateThreadProps {
|
|
61
|
+
// Not specifying an index signature here as that would make `ModuleThread` incompatible
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Worker thread. Either a `FunctionThread` or a `ModuleThread`. */
|
|
65
|
+
export type Thread = AnyFunctionThread | AnyModuleThread
|
|
66
|
+
|
|
67
|
+
export type TransferList = Transferable[]
|
|
68
|
+
|
|
69
|
+
/** Worker instance. Either a web worker or a node.js Worker provided by `worker_threads` or `tiny-worker`. */
|
|
70
|
+
export interface Worker extends EventTarget {
|
|
71
|
+
postMessage(value: any, transferList?: TransferList): void
|
|
72
|
+
/** In nodejs 10+ return type is Promise while with tiny-worker and in browser return type is void */
|
|
73
|
+
terminate(callback?: (error?: Error, exitCode?: number) => void): void | Promise<number>
|
|
74
|
+
}
|
|
75
|
+
export interface ThreadsWorkerOptions extends WorkerOptions {
|
|
76
|
+
/** Whether to apply CORS protection workaround. Defaults to true. */
|
|
77
|
+
CORSWorkaround?: boolean
|
|
78
|
+
/** Prefix for the path passed to the Worker constructor. Web worker only. */
|
|
79
|
+
_baseURL?: string
|
|
80
|
+
/** Resource limits passed on to Node worker_threads */
|
|
81
|
+
resourceLimits?: {
|
|
82
|
+
/** The size of a pre-allocated memory range used for generated code. */
|
|
83
|
+
codeRangeSizeMb?: number
|
|
84
|
+
/** The maximum size of the main heap in MB. */
|
|
85
|
+
maxOldGenerationSizeMb?: number
|
|
86
|
+
/** The maximum size of a heap space for recently created objects. */
|
|
87
|
+
maxYoungGenerationSizeMb?: number
|
|
88
|
+
}
|
|
89
|
+
/** Data passed on to node.js worker_threads. */
|
|
90
|
+
workerData?: any
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Worker implementation. Either web worker or a node.js Worker class. */
|
|
94
|
+
export declare class WorkerImplementation extends EventTarget implements Worker {
|
|
95
|
+
constructor(path: string, options?: ThreadsWorkerOptions)
|
|
96
|
+
postMessage(value: any, transferList?: TransferList): void
|
|
97
|
+
terminate(): void | Promise<number>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Class to spawn workers from a blob or source string. */
|
|
101
|
+
export declare class BlobWorker extends WorkerImplementation {
|
|
102
|
+
constructor(blob: Blob, options?: ThreadsWorkerOptions)
|
|
103
|
+
static fromText(source: string, options?: ThreadsWorkerOptions): WorkerImplementation
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface ImplementationExport {
|
|
107
|
+
blob: typeof BlobWorker
|
|
108
|
+
default: typeof WorkerImplementation
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Event as emitted by worker thread. Subscribe to using `Thread.events(thread)`. */
|
|
112
|
+
export enum WorkerEventType {
|
|
113
|
+
internalError = 'internalError',
|
|
114
|
+
message = 'message',
|
|
115
|
+
termination = 'termination',
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface WorkerInternalErrorEvent {
|
|
119
|
+
error: Error
|
|
120
|
+
type: WorkerEventType.internalError
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface WorkerMessageEvent<Data> {
|
|
124
|
+
data: Data
|
|
125
|
+
type: WorkerEventType.message
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface WorkerTerminationEvent {
|
|
129
|
+
type: WorkerEventType.termination
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type WorkerEvent = WorkerInternalErrorEvent | WorkerMessageEvent<any> | WorkerTerminationEvent
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/member-ordering */
|
|
3
|
+
export interface SerializedError {
|
|
4
|
+
__error_marker: '$$error'
|
|
5
|
+
message: string
|
|
6
|
+
name: string
|
|
7
|
+
stack?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/////////////////////////////
|
|
11
|
+
// Messages sent by master:
|
|
12
|
+
|
|
13
|
+
export enum MasterMessageType {
|
|
14
|
+
cancel = 'cancel',
|
|
15
|
+
run = 'run',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type MasterJobCancelMessage = {
|
|
19
|
+
type: MasterMessageType.cancel
|
|
20
|
+
uid: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MasterJobRunMessage = {
|
|
24
|
+
type: MasterMessageType.run
|
|
25
|
+
uid: number
|
|
26
|
+
method?: string
|
|
27
|
+
args: any[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
////////////////////////////
|
|
31
|
+
// Messages sent by worker:
|
|
32
|
+
|
|
33
|
+
export enum WorkerMessageType {
|
|
34
|
+
error = 'error',
|
|
35
|
+
init = 'init',
|
|
36
|
+
result = 'result',
|
|
37
|
+
running = 'running',
|
|
38
|
+
uncaughtError = 'uncaughtError',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type WorkerUncaughtErrorMessage = {
|
|
42
|
+
type: WorkerMessageType.uncaughtError
|
|
43
|
+
error: {
|
|
44
|
+
message: string
|
|
45
|
+
name: string
|
|
46
|
+
stack?: string
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type WorkerInitMessage = {
|
|
51
|
+
type: WorkerMessageType.init
|
|
52
|
+
exposed: { type: 'function' } | { type: 'module'; methods: string[] }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type WorkerJobErrorMessage = {
|
|
56
|
+
type: WorkerMessageType.error
|
|
57
|
+
uid: number
|
|
58
|
+
error: SerializedError
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type WorkerJobResultMessage = {
|
|
62
|
+
type: WorkerMessageType.result
|
|
63
|
+
uid: number
|
|
64
|
+
complete?: true
|
|
65
|
+
payload?: any
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type WorkerJobStartMessage = {
|
|
69
|
+
type: WorkerMessageType.running
|
|
70
|
+
uid: number
|
|
71
|
+
resultType: 'observable' | 'promise'
|
|
72
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
type UnsubscribeFn = () => void
|
|
3
|
+
|
|
4
|
+
export interface AbstractedWorkerAPI {
|
|
5
|
+
isWorkerRuntime(): boolean
|
|
6
|
+
postMessageToMaster(message: any, transferList?: Transferable[]): void
|
|
7
|
+
subscribeToMasterMessages(onMessage: (data: any) => void): UnsubscribeFn
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type WorkerFunction = ((...args: any[]) => any) | (() => any)
|
|
11
|
+
|
|
12
|
+
export type WorkerModule<Keys extends string> = {
|
|
13
|
+
[key in Keys]: WorkerFunction
|
|
14
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export interface WorkerGlobalScope {
|
|
2
|
+
addEventListener(eventName: string, listener: (event: Event) => void): void
|
|
3
|
+
postMessage(message: unknown, transferList?: Readonly<Transferable[]>): void
|
|
4
|
+
removeEventListener(eventName: string, listener: (event: Event) => void): void
|
|
5
|
+
}
|