@xylabs/threads 4.7.0-rc.1 → 4.7.0

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.
@@ -0,0 +1,35 @@
1
+ import {
2
+ typescriptConfig,
3
+ unicornConfig,
4
+ workspacesConfig,
5
+ rulesConfig,
6
+ importConfig,
7
+ } from '@xylabs/eslint-config-flat'
8
+
9
+ export default [
10
+ { ignores: ['.yarn', 'dist', '**/dist/**',
11
+ 'build', '**/build/**', 'node_modules/**', 'public', 'storybook-static', 'eslint.config.mjs',
12
+ 'rollup.config.js', 'test-tooling', 'test', 'types', 'worker.*', '*.mjs', '*.d.ts', '*.js'] },
13
+ unicornConfig,
14
+ workspacesConfig,
15
+ rulesConfig,
16
+ typescriptConfig,
17
+ importConfig,
18
+ {
19
+ rules: {
20
+ 'no-restricted-syntax': ['off'],
21
+ 'no-restricted-imports': [
22
+ 'warn',
23
+ {
24
+ paths: [
25
+ ...rulesConfig.rules['no-restricted-imports'][1].paths,
26
+ '@xyo-network/*',
27
+ 'lodash',
28
+ 'lodash-es',
29
+ '@xylabs/lodash',
30
+ ],
31
+ },
32
+ ],
33
+ },
34
+ },
35
+ ]
package/index.mjs ADDED
@@ -0,0 +1,10 @@
1
+ import * as Threads from './dist/index.js'
2
+
3
+ export const registerSerializer = Threads.registerSerializer
4
+ export const spawn = Threads.spawn
5
+ export const BlobWorker = Threads.BlobWorker
6
+ export const DefaultSerializer = Threads.DefaultSerializer
7
+ export const Pool = Threads.Pool
8
+ export const Thread = Threads.Thread
9
+ export const Transfer = Threads.Transfer
10
+ export const Worker = Threads.Worker
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/threads",
3
- "version": "4.7.0-rc.1",
3
+ "version": "4.7.0",
4
4
  "description": "Web workers & worker threads as simple as a function call",
5
5
  "keywords": [
6
6
  "thread",
@@ -33,67 +33,97 @@
33
33
  "exports": {
34
34
  ".": {
35
35
  "types": "./dist/types/index.d.ts",
36
- "import": "./dist/neutral/index.mjs"
36
+ "import": "./dist/neutral/index.mjs",
37
+ "default": "./dist/neutral/index.mjs"
37
38
  },
38
39
  "./implementation": {
39
40
  "browser": {
40
41
  "types": "./dist/types/master/implementation.browser.d.ts",
41
- "import": "./dist/browser/master/implementation.browser.mjs"
42
+ "import": "./dist/browser/master/implementation.browser.mjs",
43
+ "default": "./dist/browser/master/implementation.browser.mjs"
42
44
  },
43
45
  "node": {
44
46
  "types": "./dist/types/master/implementation.node.d.ts",
45
- "import": "./dist/node/master/implementation.node.mjs"
47
+ "import": "./dist/node/master/implementation.node.mjs",
48
+ "default": "./dist/node/master/implementation.node.mjs"
46
49
  },
47
50
  "types": "./dist/types/master/implementation.d.ts",
48
- "import": "./dist/neutral/master/implementation.mjs"
51
+ "import": "./dist/neutral/master/implementation.mjs",
52
+ "default": "./dist/neutral/master/implementation.mjs"
49
53
  },
50
54
  "./master": {
51
55
  "types": "./dist/types/master/index.d.ts",
52
- "import": "./dist/neutral/master/index.mjs"
56
+ "import": "./dist/neutral/master/index.mjs",
57
+ "default": "./dist/neutral/master/index.mjs"
53
58
  },
54
59
  "./spawn": {
55
60
  "types": "./dist/types/master/spawn.d.ts",
56
- "import": "./dist/neutral/master/spawn.mjs"
61
+ "import": "./dist/neutral/master/spawn.mjs",
62
+ "default": "./dist/neutral/master/spawn.mjs"
57
63
  },
58
64
  "./thread": {
59
65
  "types": "./dist/types/master/thread.d.ts",
60
- "import": "./dist/neutral/master/thread.mjs"
66
+ "import": "./dist/neutral/master/thread.mjs",
67
+ "default": "./dist/neutral/master/thread.mjs"
61
68
  },
62
69
  "./messenger": {
63
70
  "types": "./dist/types/types/messages.d.ts"
64
71
  },
65
72
  "./observable": {
66
73
  "types": "./dist/types/observable.d.ts",
67
- "import": "./dist/neutral/observable.mjs"
74
+ "import": "./dist/neutral/observable.mjs",
75
+ "default": "./dist/neutral/observable.mjs"
68
76
  },
69
77
  "./pool": {
70
78
  "types": "./dist/types/master/pool.d.ts",
71
- "import": "./dist/neutral/master/pool.mjs"
79
+ "import": "./dist/neutral/master/pool.mjs",
80
+ "default": "./dist/neutral/master/pool.mjs"
72
81
  },
73
82
  "./observable-promise": {
74
83
  "types": "./dist/types/observable-promise.d.ts",
75
- "import": "./dist/neutral/observable-promise.mjs"
84
+ "import": "./dist/neutral/observable-promise.mjs",
85
+ "default": "./dist/neutral/observable-promise.mjs"
76
86
  },
77
87
  "./register": {
78
88
  "types": "./dist/types/master/register.d.ts",
79
- "import": "./dist/neutral/master/register.mjs"
89
+ "import": "./dist/neutral/master/register.mjs",
90
+ "default": "./dist/neutral/master/register.mjs"
80
91
  },
81
92
  "./worker": {
82
93
  "browser": {
83
94
  "types": "./dist/types/worker/worker.browser.d.ts",
84
- "import": "./dist/browser/worker/worker.browser.mjs"
95
+ "import": "./dist/browser/worker/worker.browser.mjs",
96
+ "default": "./dist/browser/worker/worker.browser.mjs"
85
97
  },
86
98
  "node": {
87
99
  "types": "./dist/types/worker/worker.node.d.ts",
88
- "import": "./dist/node/worker/worker.node.mjs"
100
+ "import": "./dist/node/worker/worker.node.mjs",
101
+ "default": "./dist/node/worker/worker.node.mjs"
89
102
  }
90
103
  }
91
104
  },
105
+ "main": "dist/neutral/index.mjs",
92
106
  "module": "dist/neutral/index.mjs",
93
107
  "types": "dist/types/index.d.ts",
94
108
  "files": [
95
- "dist"
109
+ "dist/**",
110
+ "*.js",
111
+ "*.mjs",
112
+ "*.ts"
96
113
  ],
114
+ "ava": {
115
+ "extensions": [
116
+ "ts"
117
+ ],
118
+ "files": [
119
+ "./test/**/*.test.ts",
120
+ "./test-tooling/**/*.test.ts"
121
+ ],
122
+ "require": [
123
+ "ts-node/register"
124
+ ],
125
+ "serial": true
126
+ },
97
127
  "dependencies": {
98
128
  "debug": "^4.4.0",
99
129
  "is-observable-2-1-0": "npm:is-observable@2.1.0",
@@ -102,8 +132,8 @@
102
132
  "devDependencies": {
103
133
  "@types/debug": "^4.1.12",
104
134
  "@types/node": "^22.13.10",
105
- "@xylabs/eslint-config-flat": "^6.1.3",
106
- "@xylabs/ts-scripts-yarn3": "^6.1.3",
135
+ "@xylabs/eslint-config-flat": "^6.1.4",
136
+ "@xylabs/ts-scripts-yarn3": "^6.1.4",
107
137
  "typescript": "^5.8.2"
108
138
  },
109
139
  "optionalDependencies": {
@@ -111,6 +141,5 @@
111
141
  },
112
142
  "publishConfig": {
113
143
  "access": "public"
114
- },
115
- "stableVersion": "4.6.4"
144
+ }
116
145
  }
package/src/common.ts ADDED
@@ -0,0 +1,23 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type {
3
+ JsonSerializable, Serializer, SerializerImplementation,
4
+ } from './serializers.ts'
5
+ import { DefaultSerializer, extendSerializer } from './serializers.ts'
6
+
7
+ declare global {
8
+ var registeredSerializer: Serializer<JsonSerializable>
9
+ }
10
+
11
+ globalThis.registeredSerializer = globalThis.registeredSerializer ?? DefaultSerializer
12
+
13
+ export function registerSerializer(serializer: SerializerImplementation<JsonSerializable>) {
14
+ globalThis.registeredSerializer = extendSerializer(globalThis.registeredSerializer, serializer)
15
+ }
16
+
17
+ export function deserialize(message: JsonSerializable): any {
18
+ return globalThis.registeredSerializer.deserialize(message)
19
+ }
20
+
21
+ export function serialize(input: any): JsonSerializable {
22
+ return globalThis.registeredSerializer.serialize(input)
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /* eslint-disable import-x/no-internal-modules */
2
+ export { registerSerializer } from './common.ts'
3
+ export * from './master/index.ts'
4
+ export type { QueuedTask } from './master/pool.ts'
5
+ export type { ExposedToThreadType as ExposedAs } from './master/spawn.ts'
6
+ export type {
7
+ JsonSerializable, Serializer, SerializerImplementation,
8
+ } from './serializers.ts'
9
+ export { DefaultSerializer } from './serializers.ts'
10
+ export type { TransferDescriptor } from './transferable.ts'
11
+ export { Transfer } from './transferable.ts'
@@ -0,0 +1,32 @@
1
+ // Source: <https://github.com/parcel-bundler/parcel/blob/master/packages/core/parcel-bundler/src/builtins/bundle-url.js>
2
+
3
+ let bundleURL: string | undefined
4
+
5
+ function getBundleURLCached(): string {
6
+ if (!bundleURL) {
7
+ bundleURL = getBundleURL()
8
+ }
9
+
10
+ return bundleURL
11
+ }
12
+
13
+ function getBundleURL(): string {
14
+ // Attempt to find the URL of the current script and use that as the base URL
15
+ try {
16
+ throw new Error('getBundleURL failed')
17
+ } catch (ex) {
18
+ const err = ex as Error
19
+ const matches = ('' + err.stack).match(/(https?|file|ftp|chrome-extension|moz-extension):\/\/[^\n)]+/g)
20
+ if (matches) {
21
+ return getBaseURL(matches[0])
22
+ }
23
+ }
24
+
25
+ return '/'
26
+ }
27
+
28
+ function getBaseURL(url: string): string {
29
+ return ('' + url).replace(/^((?:https?|file|ftp|chrome-extension|moz-extension):\/\/.+)?\/[^/]+(?:\?.*)?$/, '$1') + '/'
30
+ }
31
+
32
+ export { getBundleURLCached as getBundleURL }
@@ -0,0 +1,82 @@
1
+ /* eslint-disable @stylistic/max-len */
2
+ /* eslint-disable import-x/no-internal-modules */
3
+ // tslint:disable max-classes-per-file
4
+
5
+ import type { ImplementationExport, ThreadsWorkerOptions } from '../types/master.ts'
6
+ import { getBundleURL } from './get-bundle-url.browser.ts'
7
+
8
+ export const defaultPoolSize = typeof navigator !== 'undefined' && navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 4
9
+
10
+ const isAbsoluteURL = (value: string) => /^[A-Za-z][\d+.A-Za-z\-]*:/.test(value)
11
+
12
+ function createSourceBlobURL(code: string): string {
13
+ const blob = new Blob([code], { type: 'application/javascript' })
14
+ return URL.createObjectURL(blob)
15
+ }
16
+
17
+ function selectWorkerImplementation(): ImplementationExport {
18
+ if (typeof Worker === 'undefined') {
19
+ // Might happen on Safari, for instance
20
+ // The idea is to only fail if the constructor is actually used
21
+ return class NoWebWorker {
22
+ constructor() {
23
+ throw new Error(
24
+ "No web worker implementation available. You might have tried to spawn a worker within a worker in a browser that doesn't support workers in workers.",
25
+ )
26
+ }
27
+ } as unknown as ImplementationExport
28
+ }
29
+
30
+ class WebWorker extends Worker {
31
+ constructor(url: string | URL, options?: ThreadsWorkerOptions) {
32
+ if (typeof url === 'string' && options && options._baseURL) {
33
+ url = new URL(url, options._baseURL)
34
+ } else if (typeof url === 'string' && !isAbsoluteURL(url) && /^file:\/\//i.test(getBundleURL())) {
35
+ url = new URL(url, getBundleURL().replace(/\/[^/]+$/, '/'))
36
+ if (options?.CORSWorkaround ?? true) {
37
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`)
38
+ }
39
+ }
40
+ if (
41
+ typeof url === 'string'
42
+ && isAbsoluteURL(url) // Create source code blob loading JS file via `importScripts()`
43
+ // to circumvent worker CORS restrictions
44
+ && (options?.CORSWorkaround ?? true)
45
+ ) {
46
+ url = createSourceBlobURL(`importScripts(${JSON.stringify(url)});`)
47
+ }
48
+ super(url, options)
49
+ }
50
+ }
51
+
52
+ class BlobWorker extends WebWorker {
53
+ constructor(blob: Blob, options?: ThreadsWorkerOptions) {
54
+ const url = globalThis.URL.createObjectURL(blob)
55
+ super(url, options)
56
+ }
57
+
58
+ static fromText(source: string, options?: ThreadsWorkerOptions): WebWorker {
59
+ const blob = new globalThis.Blob([source], { type: 'text/javascript' })
60
+ return new BlobWorker(blob, options)
61
+ }
62
+ }
63
+
64
+ return {
65
+ blob: BlobWorker,
66
+ default: WebWorker,
67
+ }
68
+ }
69
+
70
+ let implementation: ImplementationExport
71
+
72
+ export function getWorkerImplementation(): ImplementationExport {
73
+ if (!implementation) {
74
+ implementation = selectWorkerImplementation()
75
+ }
76
+ return implementation
77
+ }
78
+
79
+ export function isWorkerRuntime() {
80
+ const isWindowContext = typeof globalThis !== 'undefined' && typeof Window !== 'undefined' && globalThis instanceof Window
81
+ return typeof globalThis !== 'undefined' && self['postMessage'] && !isWindowContext ? true : false
82
+ }
@@ -0,0 +1,208 @@
1
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
+
3
+ /* eslint-disable unicorn/prefer-add-event-listener */
4
+ /* eslint-disable unicorn/prefer-event-target */
5
+ /* eslint-disable @typescript-eslint/no-explicit-any */
6
+ /* eslint-disable unicorn/text-encoding-identifier-case */
7
+
8
+ import { EventEmitter } from 'node:events'
9
+ import { cpus } from 'node:os'
10
+ import path from 'node:path'
11
+ import { cwd } from 'node:process'
12
+ import { Worker as NativeWorker } from 'node:worker_threads'
13
+
14
+ import type {
15
+ ImplementationExport, ThreadsWorkerOptions, WorkerImplementation,
16
+ // eslint-disable-next-line import-x/no-internal-modules
17
+ } from '../types/master.ts'
18
+
19
+ declare const __non_webpack_require__: typeof require
20
+
21
+ type WorkerEventName = 'error' | 'message'
22
+
23
+ export const defaultPoolSize = cpus().length
24
+
25
+ function resolveScriptPath(scriptPath: string, baseURL?: string | undefined) {
26
+ const makeAbsolute = (filePath: string) => {
27
+ return path.isAbsolute(filePath) ? filePath : path.join(baseURL ?? cwd(), filePath)
28
+ }
29
+
30
+ const absolutePath = makeAbsolute(scriptPath)
31
+ return absolutePath
32
+ }
33
+
34
+ function initWorkerThreadsWorker(): ImplementationExport {
35
+ let allWorkers: Array<NativeWorker> = []
36
+
37
+ class Worker extends NativeWorker {
38
+ private mappedEventListeners: WeakMap<EventListener, EventListener>
39
+
40
+ constructor(scriptPath: string, options?: ThreadsWorkerOptions & { fromSource: boolean }) {
41
+ const resolvedScriptPath = options && options.fromSource ? null : resolveScriptPath(scriptPath, (options ?? {})._baseURL)
42
+ if (resolvedScriptPath) {
43
+ super(resolvedScriptPath, options)
44
+ } else {
45
+ // `options.fromSource` is true
46
+ const sourceCode = scriptPath
47
+ super(sourceCode, { ...options, eval: true })
48
+ }
49
+
50
+ this.mappedEventListeners = new WeakMap()
51
+ allWorkers.push(this)
52
+ }
53
+
54
+ addEventListener(eventName: string, rawListener: EventListener) {
55
+ const listener = (message: any) => {
56
+ rawListener({ data: message } as any)
57
+ }
58
+ this.mappedEventListeners.set(rawListener, listener)
59
+ this.on(eventName, listener)
60
+ }
61
+
62
+ removeEventListener(eventName: string, rawListener: EventListener) {
63
+ const listener = this.mappedEventListeners.get(rawListener) || rawListener
64
+ this.off(eventName, listener)
65
+ }
66
+ }
67
+
68
+ const terminateWorkersAndMaster = () => {
69
+ // we should terminate all workers and then gracefully shutdown self process
70
+ Promise.all(allWorkers.map(worker => worker.terminate())).then(
71
+ () => process.exit(0),
72
+ () => process.exit(1),
73
+ )
74
+ allWorkers = []
75
+ }
76
+
77
+ // Take care to not leave orphaned processes behind. See #147.
78
+ process.on('SIGINT', () => terminateWorkersAndMaster())
79
+ process.on('SIGTERM', () => terminateWorkersAndMaster())
80
+
81
+ class BlobWorker extends Worker {
82
+ constructor(blob: Uint8Array, options?: ThreadsWorkerOptions) {
83
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true })
84
+ }
85
+
86
+ static fromText(source: string, options?: ThreadsWorkerOptions): WorkerImplementation {
87
+ return new Worker(source, { ...options, fromSource: true }) as any
88
+ }
89
+ }
90
+
91
+ return {
92
+ blob: BlobWorker as any,
93
+ default: Worker as any,
94
+ }
95
+ }
96
+
97
+ function initTinyWorker(): ImplementationExport {
98
+ const TinyWorker = require('tiny-worker')
99
+
100
+ let allWorkers: Array<typeof TinyWorker> = []
101
+
102
+ class Worker extends TinyWorker {
103
+ private emitter: EventEmitter
104
+
105
+ constructor(scriptPath: string, options?: ThreadsWorkerOptions & { fromSource?: boolean }) {
106
+ // Need to apply a work-around for Windows or it will choke upon the absolute path
107
+ // (`Error [ERR_INVALID_PROTOCOL]: Protocol 'c:' not supported`)
108
+ const resolvedScriptPath
109
+ = options && options.fromSource
110
+ ? null
111
+ : process.platform === 'win32'
112
+ ? `file:///${resolveScriptPath(scriptPath).replaceAll('\\', '/')}`
113
+ : resolveScriptPath(scriptPath)
114
+
115
+ if (resolvedScriptPath) {
116
+ super(resolvedScriptPath, [], { esm: true })
117
+ } else {
118
+ // `options.fromSource` is true
119
+ const sourceCode = scriptPath
120
+ super(new Function(sourceCode), [], { esm: true })
121
+ }
122
+
123
+ allWorkers.push(this)
124
+
125
+ this.emitter = new EventEmitter()
126
+ this.onerror = (error: Error) => this.emitter.emit('error', error)
127
+ this.onmessage = (message: MessageEvent) => this.emitter.emit('message', message)
128
+ }
129
+
130
+ addEventListener(eventName: WorkerEventName, listener: EventListener) {
131
+ this.emitter.addListener(eventName, listener)
132
+ }
133
+
134
+ removeEventListener(eventName: WorkerEventName, listener: EventListener) {
135
+ this.emitter.removeListener(eventName, listener)
136
+ }
137
+
138
+ terminate() {
139
+ allWorkers = allWorkers.filter(worker => worker !== this)
140
+ return super.terminate()
141
+ }
142
+ }
143
+
144
+ const terminateWorkersAndMaster = () => {
145
+ // we should terminate all workers and then gracefully shutdown self process
146
+ Promise.all(allWorkers.map(worker => worker.terminate())).then(
147
+ () => process.exit(0),
148
+ () => process.exit(1),
149
+ )
150
+ allWorkers = []
151
+ }
152
+
153
+ // Take care to not leave orphaned processes behind
154
+ // See <https://github.com/avoidwork/tiny-worker#faq>
155
+ process.on('SIGINT', () => terminateWorkersAndMaster())
156
+ process.on('SIGTERM', () => terminateWorkersAndMaster())
157
+
158
+ class BlobWorker extends Worker {
159
+ constructor(blob: Uint8Array, options?: ThreadsWorkerOptions) {
160
+ super(Buffer.from(blob).toString('utf-8'), { ...options, fromSource: true })
161
+ }
162
+
163
+ static fromText(source: string, options?: ThreadsWorkerOptions): WorkerImplementation {
164
+ return new Worker(source, { ...options, fromSource: true }) as any
165
+ }
166
+ }
167
+
168
+ return {
169
+ blob: BlobWorker as any,
170
+ default: Worker as any,
171
+ }
172
+ }
173
+
174
+ let implementation: ImplementationExport
175
+ let isTinyWorker: boolean
176
+
177
+ function selectWorkerImplementation(): ImplementationExport {
178
+ try {
179
+ isTinyWorker = false
180
+ return initWorkerThreadsWorker()
181
+ } catch (ex) {
182
+ console.error(ex)
183
+ // tslint:disable-next-line no-console
184
+ console.debug('Node worker_threads not available. Trying to fall back to tiny-worker polyfill...')
185
+ isTinyWorker = true
186
+ return initTinyWorker()
187
+ }
188
+ }
189
+
190
+ export function getWorkerImplementation(): ImplementationExport {
191
+ if (!implementation) {
192
+ implementation = selectWorkerImplementation()
193
+ }
194
+ return implementation
195
+ }
196
+
197
+ export function isWorkerRuntime() {
198
+ if (isTinyWorker) {
199
+ return globalThis !== undefined && self['postMessage'] ? true : false
200
+ } else {
201
+ // Webpack hack
202
+ const isMainThread
203
+ = typeof __non_webpack_require__ === 'function'
204
+ ? __non_webpack_require__('worker_threads').isMainThread
205
+ : eval('require')('worker_threads').isMainThread
206
+ return !isMainThread
207
+ }
208
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ * This file is only a stub to make './implementation' resolve to the right module.
3
+ */
4
+
5
+ // We alias `src/master/implementation` to `src/master/implementation.browser` for web
6
+ // browsers already in the package.json, so if get here, it's safe to pass-through the
7
+ // node implementation
8
+
9
+ import * as BrowserImplementation from './implementation.browser.ts'
10
+ import * as NodeImplementation from './implementation.node.ts'
11
+
12
+ const runningInNode = typeof process !== 'undefined' && (process.arch as string) !== 'browser' && 'pid' in process
13
+ const implementation = runningInNode ? NodeImplementation : BrowserImplementation
14
+
15
+ /** Default size of pools. Depending on the platform the value might vary from device to device. */
16
+ export const defaultPoolSize = implementation.defaultPoolSize
17
+
18
+ export const getWorkerImplementation = implementation.getWorkerImplementation
19
+
20
+ /** Returns `true` if this code is currently running in a worker. */
21
+ export const isWorkerRuntime = implementation.isWorkerRuntime
@@ -0,0 +1,19 @@
1
+ /* eslint-disable import-x/no-internal-modules */
2
+ import type { BlobWorker as BlobWorkerClass, Worker as WorkerType } from '../types/master.ts'
3
+ import { getWorkerImplementation } from './implementation.ts'
4
+
5
+ export type { FunctionThread, ModuleThread } from '../types/master.ts'
6
+ export { Pool } from './pool.ts'
7
+ export { spawn } from './spawn.ts'
8
+ export { Thread } from './thread.ts'
9
+
10
+ export type BlobWorker = typeof BlobWorkerClass
11
+ export type Worker = WorkerType
12
+
13
+ /** Separate class to spawn workers from source code blobs or strings. */
14
+ export const BlobWorker = getWorkerImplementation().blob
15
+
16
+ /** Worker implementation. Either web worker or a node.js Worker class. */
17
+ export const Worker = getWorkerImplementation().default
18
+
19
+ export { isWorkerRuntime } from './implementation.ts'