@xylabs/threads 3.0.4

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.
Files changed (171) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +227 -0
  4. package/dist/common.d.ts +4 -0
  5. package/dist/common.js +18 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +27 -0
  8. package/dist/master/get-bundle-url.browser.d.ts +3 -0
  9. package/dist/master/get-bundle-url.browser.js +29 -0
  10. package/dist/master/implementation.browser.d.ts +4 -0
  11. package/dist/master/implementation.browser.js +69 -0
  12. package/dist/master/implementation.d.ts +6 -0
  13. package/dist/master/implementation.js +41 -0
  14. package/dist/master/implementation.node.d.ts +5 -0
  15. package/dist/master/implementation.node.js +255 -0
  16. package/dist/master/index.d.ts +13 -0
  17. package/dist/master/index.js +16 -0
  18. package/dist/master/invocation-proxy.d.ts +3 -0
  19. package/dist/master/invocation-proxy.js +130 -0
  20. package/dist/master/pool-types.d.ts +65 -0
  21. package/dist/master/pool-types.js +15 -0
  22. package/dist/master/pool.d.ts +90 -0
  23. package/dist/master/pool.js +281 -0
  24. package/dist/master/register.d.ts +1 -0
  25. package/dist/master/register.js +12 -0
  26. package/dist/master/spawn.d.ts +20 -0
  27. package/dist/master/spawn.js +130 -0
  28. package/dist/master/thread.d.ts +12 -0
  29. package/dist/master/thread.js +22 -0
  30. package/dist/observable-promise.d.ts +38 -0
  31. package/dist/observable-promise.js +156 -0
  32. package/dist/observable.d.ts +19 -0
  33. package/dist/observable.js +43 -0
  34. package/dist/ponyfills.d.ts +8 -0
  35. package/dist/ponyfills.js +22 -0
  36. package/dist/promise.d.ts +5 -0
  37. package/dist/promise.js +29 -0
  38. package/dist/serializers.d.ts +16 -0
  39. package/dist/serializers.js +41 -0
  40. package/dist/symbols.d.ts +5 -0
  41. package/dist/symbols.js +8 -0
  42. package/dist/transferable.d.ts +42 -0
  43. package/dist/transferable.js +28 -0
  44. package/dist/types/master.d.ts +99 -0
  45. package/dist/types/master.js +14 -0
  46. package/dist/types/messages.d.ts +62 -0
  47. package/dist/types/messages.js +20 -0
  48. package/dist/types/worker.d.ts +11 -0
  49. package/dist/types/worker.js +2 -0
  50. package/dist/worker/bundle-entry.d.ts +1 -0
  51. package/dist/worker/bundle-entry.js +27 -0
  52. package/dist/worker/implementation.browser.d.ts +7 -0
  53. package/dist/worker/implementation.browser.js +28 -0
  54. package/dist/worker/implementation.d.ts +3 -0
  55. package/dist/worker/implementation.js +24 -0
  56. package/dist/worker/implementation.tiny-worker.d.ts +7 -0
  57. package/dist/worker/implementation.tiny-worker.js +38 -0
  58. package/dist/worker/implementation.worker_threads.d.ts +8 -0
  59. package/dist/worker/implementation.worker_threads.js +42 -0
  60. package/dist/worker/index.d.ts +13 -0
  61. package/dist/worker/index.js +195 -0
  62. package/dist/worker_threads.d.ts +8 -0
  63. package/dist/worker_threads.js +17 -0
  64. package/dist-esm/common.js +12 -0
  65. package/dist-esm/index.js +6 -0
  66. package/dist-esm/master/get-bundle-url.browser.js +25 -0
  67. package/dist-esm/master/implementation.browser.js +64 -0
  68. package/dist-esm/master/implementation.js +15 -0
  69. package/dist-esm/master/implementation.node.js +224 -0
  70. package/dist-esm/master/index.js +9 -0
  71. package/dist-esm/master/invocation-proxy.js +122 -0
  72. package/dist-esm/master/pool-types.js +12 -0
  73. package/dist-esm/master/pool.js +273 -0
  74. package/dist-esm/master/register.js +10 -0
  75. package/dist-esm/master/spawn.js +123 -0
  76. package/dist-esm/master/thread.js +19 -0
  77. package/dist-esm/observable-promise.js +152 -0
  78. package/dist-esm/observable.js +38 -0
  79. package/dist-esm/ponyfills.js +18 -0
  80. package/dist-esm/promise.js +25 -0
  81. package/dist-esm/serializers.js +37 -0
  82. package/dist-esm/symbols.js +5 -0
  83. package/dist-esm/transferable.js +23 -0
  84. package/dist-esm/types/master.js +11 -0
  85. package/dist-esm/types/messages.js +17 -0
  86. package/dist-esm/types/worker.js +1 -0
  87. package/dist-esm/worker/bundle-entry.js +11 -0
  88. package/dist-esm/worker/implementation.browser.js +26 -0
  89. package/dist-esm/worker/implementation.js +19 -0
  90. package/dist-esm/worker/implementation.tiny-worker.js +36 -0
  91. package/dist-esm/worker/implementation.worker_threads.js +37 -0
  92. package/dist-esm/worker/index.js +186 -0
  93. package/dist-esm/worker_threads.js +14 -0
  94. package/index.mjs +11 -0
  95. package/observable.d.ts +2 -0
  96. package/observable.js +3 -0
  97. package/observable.mjs +5 -0
  98. package/package.json +141 -0
  99. package/register.d.ts +3 -0
  100. package/register.js +3 -0
  101. package/register.mjs +2 -0
  102. package/rollup.config.js +16 -0
  103. package/src/common.ts +16 -0
  104. package/src/index.ts +8 -0
  105. package/src/master/get-bundle-url.browser.ts +31 -0
  106. package/src/master/implementation.browser.ts +80 -0
  107. package/src/master/implementation.node.ts +284 -0
  108. package/src/master/implementation.ts +21 -0
  109. package/src/master/index.ts +20 -0
  110. package/src/master/invocation-proxy.ts +146 -0
  111. package/src/master/pool-types.ts +83 -0
  112. package/src/master/pool.ts +391 -0
  113. package/src/master/register.ts +10 -0
  114. package/src/master/spawn.ts +172 -0
  115. package/src/master/thread.ts +26 -0
  116. package/src/observable-promise.ts +181 -0
  117. package/src/observable.ts +43 -0
  118. package/src/ponyfills.ts +31 -0
  119. package/src/promise.ts +26 -0
  120. package/src/serializers.ts +67 -0
  121. package/src/symbols.ts +5 -0
  122. package/src/transferable.ts +68 -0
  123. package/src/types/master.ts +130 -0
  124. package/src/types/messages.ts +81 -0
  125. package/src/types/worker.ts +14 -0
  126. package/src/worker/bundle-entry.ts +10 -0
  127. package/src/worker/implementation.browser.ts +40 -0
  128. package/src/worker/implementation.tiny-worker.ts +52 -0
  129. package/src/worker/implementation.ts +23 -0
  130. package/src/worker/implementation.worker_threads.ts +50 -0
  131. package/src/worker/index.ts +228 -0
  132. package/src/worker_threads.ts +28 -0
  133. package/test/lib/serialization.ts +38 -0
  134. package/test/observable-promise.test.ts +189 -0
  135. package/test/observable.test.ts +86 -0
  136. package/test/pool.test.ts +173 -0
  137. package/test/serialization.test.ts +21 -0
  138. package/test/spawn.chromium.mocha.ts +49 -0
  139. package/test/spawn.test.ts +71 -0
  140. package/test/streaming.test.ts +27 -0
  141. package/test/transferables.test.ts +69 -0
  142. package/test/workers/arraybuffer-xor.ts +11 -0
  143. package/test/workers/count-to-five.ts +13 -0
  144. package/test/workers/counter.ts +20 -0
  145. package/test/workers/faulty-function.ts +6 -0
  146. package/test/workers/hello-world.ts +6 -0
  147. package/test/workers/increment.ts +9 -0
  148. package/test/workers/minmax.ts +25 -0
  149. package/test/workers/serialization.ts +12 -0
  150. package/test/workers/top-level-throw.ts +1 -0
  151. package/test-tooling/rollup/app.js +20 -0
  152. package/test-tooling/rollup/rollup.config.ts +15 -0
  153. package/test-tooling/rollup/rollup.test.ts +44 -0
  154. package/test-tooling/rollup/worker.js +7 -0
  155. package/test-tooling/tsconfig/minimal-tsconfig.test.ts +7 -0
  156. package/test-tooling/tsconfig/minimal.ts +10 -0
  157. package/test-tooling/webpack/addition-worker.ts +10 -0
  158. package/test-tooling/webpack/app-with-inlined-worker.ts +29 -0
  159. package/test-tooling/webpack/app.ts +58 -0
  160. package/test-tooling/webpack/pool-worker.ts +6 -0
  161. package/test-tooling/webpack/raw-loader.d.ts +4 -0
  162. package/test-tooling/webpack/webpack.chromium.mocha.ts +21 -0
  163. package/test-tooling/webpack/webpack.node.config.js +38 -0
  164. package/test-tooling/webpack/webpack.test.ts +90 -0
  165. package/test-tooling/webpack/webpack.web.config.js +35 -0
  166. package/types/is-observable.d.ts +7 -0
  167. package/types/tiny-worker.d.ts +4 -0
  168. package/types/webworker.d.ts +9 -0
  169. package/worker.d.ts +2 -0
  170. package/worker.js +3 -0
  171. package/worker.mjs +7 -0
@@ -0,0 +1,69 @@
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /* eslint-disable import/no-internal-modules */
4
+ import test from 'ava'
5
+
6
+ import { spawn, Thread, Transfer, Worker } from '../src/index'
7
+ import { XorBuffer } from './workers/arraybuffer-xor'
8
+
9
+ type SpyInit<Args extends any[], OriginalReturn, NewReturn> = (originalFn: (...args: Args) => OriginalReturn) => (...args: Args) => NewReturn
10
+
11
+ const arrayBufferPlaceholder = Symbol('ArrayBufferPlaceholder')
12
+
13
+ function spyOn<Args extends any[], OriginalReturn, NewReturn>(
14
+ target: (...args: Args) => OriginalReturn,
15
+ spy: SpyInit<Args, OriginalReturn, NewReturn>,
16
+ ): (...args: Args) => NewReturn {
17
+ return spy(target)
18
+ }
19
+
20
+ function replaceArrayBufferWithPlaceholder<In extends any>(
21
+ obj: In,
22
+ arrayBuffer: ArrayBuffer,
23
+ ): In extends ArrayBuffer ? In | typeof arrayBufferPlaceholder : In {
24
+ if ((obj as any) === arrayBuffer) {
25
+ return arrayBufferPlaceholder as any
26
+ } else if (Array.isArray(obj)) {
27
+ return (obj as any[]).map((element) => replaceArrayBufferWithPlaceholder(element, arrayBuffer)) as any
28
+ } else if (obj && typeof obj === 'object') {
29
+ const result: In = Object.create(Object.getPrototypeOf(obj))
30
+
31
+ for (const key of Object.getOwnPropertyNames(obj)) {
32
+ ;(result as any)[key] = replaceArrayBufferWithPlaceholder((obj as any)[key], arrayBuffer)
33
+ }
34
+ return result as any
35
+ } else {
36
+ return obj as any
37
+ }
38
+ }
39
+
40
+ test('can pass transferable objects on thread call', async (t) => {
41
+ const testData = new ArrayBuffer(64)
42
+
43
+ const worker = new Worker('./workers/arraybuffer-xor')
44
+ const postMessageCalls: Array<any[]> = []
45
+
46
+ worker.postMessage = spyOn(worker.postMessage.bind(worker), (postMessage) => (...args) => {
47
+ postMessageCalls.push(replaceArrayBufferWithPlaceholder(args, testData))
48
+ return postMessage(...args)
49
+ })
50
+
51
+ const xorBuffer = await spawn<XorBuffer>(worker)
52
+ const returnedBuffer = await xorBuffer(Transfer(testData), 15)
53
+
54
+ t.is(returnedBuffer.byteLength, 64)
55
+
56
+ t.is(postMessageCalls.length, 1)
57
+ t.is(postMessageCalls[0].length, 2)
58
+ t.deepEqual(postMessageCalls[0][0], {
59
+ args: [arrayBufferPlaceholder, 15],
60
+ method: undefined,
61
+ type: 'run',
62
+ uid: postMessageCalls[0][0].uid,
63
+ })
64
+ t.deepEqual(postMessageCalls[0][1], [arrayBufferPlaceholder])
65
+
66
+ await Thread.terminate(xorBuffer)
67
+ })
68
+
69
+ test.todo('can pass transferable objects as observable values')
@@ -0,0 +1,11 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose, Transfer } from '../../src/worker'
3
+
4
+ function xor(buffer: ArrayBuffer, value: number) {
5
+ const view = new Uint8Array(buffer)
6
+ for (const [offset, byte] of view.entries()) view.set([byte ^ value], offset)
7
+ return Transfer(buffer)
8
+ }
9
+
10
+ expose(xor)
11
+ export type XorBuffer = typeof xor
@@ -0,0 +1,13 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { Observable } from 'observable-fns'
3
+
4
+ import { expose } from '../../src/worker'
5
+
6
+ expose(function countToFive() {
7
+ return new Observable((observer) => {
8
+ for (let counter = 1; counter <= 5; counter++) {
9
+ observer.next(counter)
10
+ }
11
+ observer.complete()
12
+ })
13
+ })
@@ -0,0 +1,20 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose } from '../../src/worker'
3
+
4
+ let currentCount = 0
5
+
6
+ const counter = {
7
+ decrement() {
8
+ return --currentCount
9
+ },
10
+ getCount() {
11
+ return currentCount
12
+ },
13
+ increment() {
14
+ return ++currentCount
15
+ },
16
+ }
17
+
18
+ expose(counter)
19
+
20
+ export type Counter = typeof counter
@@ -0,0 +1,6 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose } from '../../src/worker'
3
+
4
+ expose(function fail() {
5
+ throw new Error('I am supposed to fail.')
6
+ })
@@ -0,0 +1,6 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose } from '../../src/worker'
3
+
4
+ expose(function helloWorld() {
5
+ return 'Hello World'
6
+ })
@@ -0,0 +1,9 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose } from '../../src/worker'
3
+
4
+ let counter = 0
5
+
6
+ expose(function increment(by: number = 1) {
7
+ counter += by
8
+ return counter
9
+ })
@@ -0,0 +1,25 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { Observable, Subject } from '../../src/observable'
3
+ import { expose } from '../../src/worker'
4
+
5
+ let max = Number.NEGATIVE_INFINITY
6
+ let min = Number.POSITIVE_INFINITY
7
+
8
+ let subject = new Subject()
9
+
10
+ const minmax = {
11
+ finish() {
12
+ subject.complete()
13
+ subject = new Subject()
14
+ },
15
+ push(value) {
16
+ max = Math.max(max, value)
17
+ min = Math.min(min, value)
18
+ subject.next({ max, min })
19
+ },
20
+ values() {
21
+ return Observable.from(subject)
22
+ },
23
+ }
24
+
25
+ expose(minmax)
@@ -0,0 +1,12 @@
1
+ /* eslint-disable require-await */
2
+ /* eslint-disable import/no-internal-modules */
3
+ import { expose, registerSerializer } from '../../src/worker'
4
+ import { Foo, fooSerializer } from '../lib/serialization'
5
+
6
+ registerSerializer(fooSerializer)
7
+
8
+ async function run(foo: Foo<string>) {
9
+ return new Foo(foo.getValue() + foo.getValue())
10
+ }
11
+
12
+ expose(run)
@@ -0,0 +1 @@
1
+ throw new Error('Top-level worker error')
@@ -0,0 +1,20 @@
1
+ /* eslint-disable no-undef */
2
+ /* eslint-disable no-restricted-imports */
3
+ import { spawn, Thread, Worker } from '../..'
4
+
5
+ async function run() {
6
+ const add = await spawn(new Worker('./worker.js'))
7
+ const result = await add(2, 3)
8
+ await Thread.terminate(add)
9
+ return result
10
+ }
11
+
12
+ run()
13
+ .then((result) => {
14
+ console.log(`Result: ${result}`)
15
+ puppet.exit(0)
16
+ })
17
+ .catch((error) => {
18
+ console.error(error)
19
+ puppet.exit(1)
20
+ })
@@ -0,0 +1,15 @@
1
+ /* eslint-disable import/no-default-export */
2
+ import commonjs from '@rollup/plugin-commonjs'
3
+ import { nodeResolve } from '@rollup/plugin-node-resolve'
4
+
5
+ export default {
6
+ plugins: [
7
+ nodeResolve({
8
+ browser: true,
9
+ mainFields: ['module', 'main'],
10
+ preferBuiltins: true,
11
+ }),
12
+
13
+ commonjs(),
14
+ ],
15
+ }
@@ -0,0 +1,44 @@
1
+ import * as path from 'node:path'
2
+
3
+ import test from 'ava'
4
+ import execa from 'execa'
5
+ import { rollup } from 'rollup'
6
+
7
+ import config from './rollup.config'
8
+
9
+ test('can be bundled using rollup', async (t) => {
10
+ t.timeout(2_000_000) // milliseconds
11
+
12
+ const appBundleP = rollup({
13
+ input: path.resolve(__dirname, 'app.js'),
14
+ ...config,
15
+ })
16
+
17
+ const workerBundleP = rollup({
18
+ input: path.resolve(__dirname, 'worker.js'),
19
+ ...config,
20
+ })
21
+
22
+ const appBundleWriteP = (await appBundleP).write({
23
+ dir: path.resolve(__dirname, 'dist'),
24
+ format: 'iife',
25
+ })
26
+
27
+ const workerBundleWriteP = (await workerBundleP).write({
28
+ dir: path.resolve(__dirname, 'dist'),
29
+ format: 'iife',
30
+ })
31
+
32
+ await Promise.all([appBundleWriteP, workerBundleWriteP])
33
+
34
+ if (process.platform === 'win32') {
35
+ // Quick-fix for weird Windows issue in CI
36
+ return t.pass()
37
+ }
38
+
39
+ const result = await execa.command('puppet-run --serve ./dist/worker.js:/worker.js ./dist/app.js', {
40
+ cwd: __dirname,
41
+ stderr: process.stderr,
42
+ })
43
+ t.is(result.exitCode, 0)
44
+ })
@@ -0,0 +1,7 @@
1
+ /* eslint-disable require-await */
2
+ /* eslint-disable import/no-internal-modules */
3
+ import { expose } from '../../dist-esm/worker'
4
+
5
+ expose(async function add(a, b) {
6
+ return a + b
7
+ })
@@ -0,0 +1,7 @@
1
+ import test from 'ava'
2
+ import execa from 'execa'
3
+
4
+ test('can compile with a minimal TypeScript config', async (t) => {
5
+ const result = await execa('tsc', ['--project', require.resolve('./minimal-tsconfig.json')])
6
+ t.is(result.exitCode, 0, `tsc exited with non-zero exit code.\nStderr:\n${result.stderr}`)
7
+ })
@@ -0,0 +1,10 @@
1
+ /* eslint-disable @typescript-eslint/no-floating-promises */
2
+ /* eslint-disable no-restricted-imports */
3
+ import { spawn, Thread, Worker } from '../..'
4
+
5
+ async function main() {
6
+ const helloWorld = await spawn(new Worker('./workers/hello-world'))
7
+ await Thread.terminate(helloWorld)
8
+ }
9
+
10
+ main()
@@ -0,0 +1,10 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose, isWorkerRuntime } from '../../src/worker'
3
+
4
+ if (!isWorkerRuntime()) {
5
+ throw new Error('isWorkerRuntime() says we are not in a worker.')
6
+ }
7
+
8
+ expose(function add(a: number, b: number) {
9
+ return a + b
10
+ })
@@ -0,0 +1,29 @@
1
+ /* eslint-disable import/no-default-export */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /* eslint-disable import/no-internal-modules */
4
+ /* eslint-disable import/no-unresolved */
5
+ /// <reference types="./raw-loader" />
6
+
7
+ import AdditionWorkerNodeBundle from 'raw-loader!./dist/addition-worker.node/worker.js'
8
+ import AdditionWorkerWebBundle from 'raw-loader!./dist/addition-worker.web/worker.js'
9
+
10
+ import { BlobWorker, spawn } from '../../src/index'
11
+
12
+ const AdditionWorkerBundle = (process as any).browser ? AdditionWorkerWebBundle : AdditionWorkerNodeBundle
13
+ type AdditionWorker = (a: number, b: number) => number
14
+
15
+ async function test() {
16
+ // We also want to test if referencing multiple different workers in a module
17
+ // built using webpack works
18
+
19
+ const add = await spawn<AdditionWorker>(BlobWorker.fromText(AdditionWorkerBundle))
20
+ const result = await add(2, 3)
21
+
22
+ if (result !== 5) {
23
+ throw new Error('Unexpected result returned by addition worker: ' + result)
24
+ }
25
+
26
+ return 'test succeeded'
27
+ }
28
+
29
+ export default test
@@ -0,0 +1,58 @@
1
+ /* eslint-disable import/no-default-export */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /* eslint-disable import/no-internal-modules */
4
+ import { isWorkerRuntime, Pool, spawn, Worker } from '../../src/index'
5
+
6
+ type AdditionWorker = (a: number, b: number) => number
7
+ type HelloWorker = (text: string) => string
8
+
9
+ async function test() {
10
+ const pool = Pool(() => spawn<HelloWorker>(new Worker('./pool-worker')))
11
+ const results = await Promise.all([
12
+ pool.queue((hello) => hello('World')),
13
+ pool.queue((hello) => hello('World')),
14
+ pool.queue((hello) => hello('World')),
15
+ pool.queue((hello) => hello('World')),
16
+ ])
17
+ await pool.terminate()
18
+
19
+ for (const result of results) {
20
+ if (result !== 'Hello, World') {
21
+ throw new Error('Unexpected result returned by pool worker: ' + result)
22
+ }
23
+ }
24
+ }
25
+
26
+ async function test2() {
27
+ // We also want to test if referencing multiple different workers in a module
28
+ // built using webpack works
29
+
30
+ const add = await spawn<AdditionWorker>(new Worker('./addition-worker'))
31
+ const result = await add(2, 3)
32
+
33
+ if (result !== 5) {
34
+ throw new Error('Unexpected result returned by addition worker: ' + result)
35
+ }
36
+ }
37
+
38
+ async function test3() {
39
+ if (!(process as any).browser) {
40
+ // Running workers from remote URLs is disabled in node.js
41
+ return
42
+ }
43
+
44
+ const hello = await spawn<HelloWorker>(new Worker('https://infallible-turing-115958.netlify.com/hello-worker.js'))
45
+ const result = await hello('World')
46
+
47
+ if (result !== 'Hello, World') {
48
+ throw new Error('Unexpected result returned by hello worker: ' + result)
49
+ }
50
+ }
51
+
52
+ function test4() {
53
+ if (isWorkerRuntime() !== false) {
54
+ throw new Error('Expected isWorkerRuntime() to return false. Got: ' + isWorkerRuntime())
55
+ }
56
+ }
57
+
58
+ export default () => Promise.all([test(), test2(), test3(), test4()])
@@ -0,0 +1,6 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import { expose } from '../../src/worker'
3
+
4
+ expose(function hello(text: string) {
5
+ return `Hello, ${text}`
6
+ })
@@ -0,0 +1,4 @@
1
+ declare module 'raw-loader!*' {
2
+ const content: string
3
+ export = content
4
+ }
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ // NOTE:
3
+ // We are gonna test the bundles previously built by the AVA tests (see webpack.test.ts)
4
+
5
+ describe('threads webpack browser bundle', function () {
6
+ this.timeout(80_000)
7
+
8
+ it('works fine', async function () {
9
+ const bundle = require('./dist/app.web/main')
10
+ await bundle.test()
11
+ })
12
+ })
13
+
14
+ describe('threads webpack browser bundle with inlined worker', function () {
15
+ this.timeout(80_000)
16
+
17
+ it('works fine', async function () {
18
+ const bundle = require('./dist/app-inlined.web/main')
19
+ await bundle.test()
20
+ })
21
+ })
@@ -0,0 +1,38 @@
1
+ /* eslint-disable no-undef */
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+ const path = require('node:path')
4
+ const ThreadsPlugin = require('threads-plugin')
5
+
6
+ module.exports = {
7
+ context: __dirname,
8
+ devtool: false,
9
+ entry: require.resolve('./app.ts'),
10
+ externals: {
11
+ 'tiny-worker': 'tiny-worker',
12
+ },
13
+ mode: 'development',
14
+ module: {
15
+ rules: [
16
+ {
17
+ loader: 'ts-loader',
18
+ options: {
19
+ compilerOptions: {
20
+ module: 'esnext',
21
+ },
22
+ },
23
+ test: /\.ts$/,
24
+ },
25
+ ],
26
+ },
27
+ output: {
28
+ library: 'test',
29
+ libraryExport: 'default',
30
+ libraryTarget: 'commonjs',
31
+ path: path.resolve(__dirname, './dist/app.node'),
32
+ },
33
+ plugins: [new ThreadsPlugin()],
34
+ resolve: {
35
+ extensions: ['.js', '.ts'],
36
+ },
37
+ target: 'node',
38
+ }
@@ -0,0 +1,90 @@
1
+ /* eslint-disable require-await */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ /* eslint-disable @typescript-eslint/no-var-requires */
4
+ import * as path from 'node:path'
5
+
6
+ import test from 'ava'
7
+ import Webpack from 'webpack'
8
+
9
+ const browserConfig = require('./webpack.web.config')
10
+ const serverConfig = require('./webpack.node.config')
11
+
12
+ const stringifyWebpackError = (error: any) =>
13
+ error ?
14
+ typeof error.stack === 'string' ? error.stack
15
+ : typeof error.message === 'string' ? error.message
16
+ : error
17
+ : ''
18
+
19
+ async function runWebpack(config: any) {
20
+ return new Promise<Webpack.Stats>((resolve, reject) => {
21
+ Webpack(config).run((error, stats) => {
22
+ error ? reject(error) : resolve(stats)
23
+ })
24
+ })
25
+ }
26
+
27
+ test('can create a browser bundle with webpack', async (t) => {
28
+ const stats = await runWebpack(browserConfig)
29
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
30
+ })
31
+
32
+ test('can create a working server bundle with webpack', async (t) => {
33
+ const stats = await runWebpack(serverConfig)
34
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
35
+
36
+ const bundle = require('./dist/app.node/main')
37
+ await bundle.test()
38
+ })
39
+
40
+ test('can inline a worker into an app bundle', async (t) => {
41
+ // Bundle browser worker
42
+ let stats = await runWebpack({
43
+ ...browserConfig,
44
+ entry: require.resolve('./addition-worker'),
45
+ output: {
46
+ filename: 'worker.js',
47
+ path: path.resolve(__dirname, 'dist/addition-worker.web'),
48
+ },
49
+ target: 'webworker',
50
+ })
51
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
52
+
53
+ // Bundle server worker
54
+ stats = await runWebpack({
55
+ ...serverConfig,
56
+ entry: require.resolve('./addition-worker'),
57
+ output: {
58
+ filename: 'worker.js',
59
+ path: path.resolve(__dirname, 'dist/addition-worker.node'),
60
+ },
61
+ })
62
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
63
+
64
+ // Bundle browser app
65
+ stats = await runWebpack({
66
+ ...browserConfig,
67
+ entry: require.resolve('./app-with-inlined-worker'),
68
+ output: {
69
+ ...serverConfig.output,
70
+ path: path.resolve(__dirname, 'dist/app-inlined.web'),
71
+ },
72
+ })
73
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
74
+
75
+ // Bundle server app
76
+ stats = await runWebpack({
77
+ ...serverConfig,
78
+ entry: require.resolve('./app-with-inlined-worker'),
79
+ output: {
80
+ ...serverConfig.output,
81
+ path: path.resolve(__dirname, 'dist/app-inlined.node'),
82
+ },
83
+ })
84
+ t.deepEqual(stats.compilation.errors, [], stringifyWebpackError(stats.compilation.errors[0]))
85
+
86
+ const bundle = require('./dist/app-inlined.node/main')
87
+ const result = await bundle.test()
88
+
89
+ t.is(result, 'test succeeded')
90
+ })
@@ -0,0 +1,35 @@
1
+ /* eslint-disable no-undef */
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+ const path = require('node:path')
4
+ const ThreadsPlugin = require('threads-plugin')
5
+
6
+ module.exports = {
7
+ context: __dirname,
8
+ devtool: false,
9
+ entry: require.resolve('./app.ts'),
10
+ mode: 'development',
11
+ module: {
12
+ rules: [
13
+ {
14
+ loader: 'ts-loader',
15
+ options: {
16
+ compilerOptions: {
17
+ module: 'esnext',
18
+ },
19
+ },
20
+ test: /\.ts$/,
21
+ },
22
+ ],
23
+ },
24
+ output: {
25
+ library: 'test',
26
+ libraryExport: 'default',
27
+ libraryTarget: 'commonjs',
28
+ path: path.resolve(__dirname, './dist/app.web'),
29
+ },
30
+ plugins: [new ThreadsPlugin()],
31
+ resolve: {
32
+ extensions: ['.js', '.ts'],
33
+ },
34
+ target: 'web',
35
+ }
@@ -0,0 +1,7 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ declare module 'is-observable' {
3
+ import { Observable } from 'observable-fns'
4
+
5
+ function isObservable(thing: any): thing is Observable<any>
6
+ export = isObservable
7
+ }
@@ -0,0 +1,4 @@
1
+ declare module 'tiny-worker' {
2
+ const TinyWorker: typeof Worker
3
+ export = TinyWorker
4
+ }
@@ -0,0 +1,9 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /// <reference no-default-lib="true"/>
3
+ /// <reference lib="webworker" />
4
+
5
+ interface WorkerGlobalScope {
6
+ addEventListener(eventName: string, listener: (event: Event) => void): void
7
+ postMessage(message: any, transferables?: any[]): void
8
+ removeEventListener(eventName: string, listener: (event: Event) => void): void
9
+ }
package/worker.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ export * from './dist/worker/index'
package/worker.js ADDED
@@ -0,0 +1,3 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ /* eslint-disable no-undef */
3
+ module.exports = require('./dist/worker/index')
package/worker.mjs ADDED
@@ -0,0 +1,7 @@
1
+ /* eslint-disable import/no-internal-modules */
2
+ import * as WorkerContext from './dist/worker/index.js'
3
+
4
+ export const expose = WorkerContext.expose
5
+ export const registerSerializer = WorkerContext.registerSerializer
6
+ export const Transfer = WorkerContext.Transfer
7
+ export const isWorkerRuntime = WorkerContext.isWorkerRuntime