@neurodevs/node-lsl 17.1.0 → 18.0.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.
- package/.claude/settings.local.json +10 -0
- package/.regressionproof.json +7 -0
- package/.vscode/launch.json +3 -2
- package/README.md +9 -35
- package/build/.spruce/settings.json +5 -1
- package/build/__tests__/AbstractPackageTest.d.ts +4 -4
- package/build/__tests__/AbstractPackageTest.js +9 -11
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/LslBackpressureMonitor.test.d.ts +7 -0
- package/build/__tests__/impl/LslBackpressureMonitor.test.js +26 -0
- package/build/__tests__/impl/LslBackpressureMonitor.test.js.map +1 -0
- package/build/__tests__/impl/LslEventMarkerEmitter.test.d.ts +34 -0
- package/build/__tests__/impl/LslEventMarkerEmitter.test.js +243 -0
- package/build/__tests__/impl/LslEventMarkerEmitter.test.js.map +1 -0
- package/build/__tests__/impl/LslStreamInfo.test.d.ts +0 -3
- package/build/__tests__/impl/LslStreamInfo.test.js +2 -27
- package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInlet.test.d.ts +22 -8
- package/build/__tests__/impl/LslStreamInlet.test.js +240 -66
- package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamOutlet.test.d.ts +3 -7
- package/build/__tests__/impl/LslStreamOutlet.test.js +36 -46
- package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
- package/build/__tests__/impl/LslWebSocketBridge.test.js +16 -17
- package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
- package/build/assertions.d.ts +1 -1
- package/build/assertions.js.map +1 -1
- package/build/impl/LslBackpressureMonitor.d.ts +8 -0
- package/build/impl/LslBackpressureMonitor.js +8 -0
- package/build/impl/LslBackpressureMonitor.js.map +1 -0
- package/build/impl/LslEventMarkerEmitter.d.ts +35 -0
- package/build/impl/LslEventMarkerEmitter.js +86 -0
- package/build/impl/LslEventMarkerEmitter.js.map +1 -0
- package/build/impl/LslStreamInfo.d.ts +4 -8
- package/build/impl/LslStreamInfo.js +8 -21
- package/build/impl/LslStreamInfo.js.map +1 -1
- package/build/impl/LslStreamInlet.d.ts +31 -47
- package/build/impl/LslStreamInlet.js +82 -143
- package/build/impl/LslStreamInlet.js.map +1 -1
- package/build/impl/LslStreamOutlet.d.ts +15 -18
- package/build/impl/LslStreamOutlet.js +39 -47
- package/build/impl/LslStreamOutlet.js.map +1 -1
- package/build/impl/LslWebSocketBridge.d.ts +4 -5
- package/build/impl/LslWebSocketBridge.js +8 -8
- package/build/impl/LslWebSocketBridge.js.map +1 -1
- package/build/impl/workers/inlet/LslStreamInlet.worker.d.ts +22 -0
- package/build/impl/workers/inlet/LslStreamInlet.worker.js +42 -0
- package/build/impl/workers/inlet/LslStreamInlet.worker.js.map +1 -0
- package/build/impl/workers/inlet/StreamInletWorker.d.ts +73 -0
- package/build/impl/workers/inlet/StreamInletWorker.js +253 -0
- package/build/impl/workers/inlet/StreamInletWorker.js.map +1 -0
- package/build/impl/workers/outlet/LslStreamOutlet.worker.d.ts +32 -0
- package/build/impl/workers/outlet/LslStreamOutlet.worker.js +82 -0
- package/build/impl/workers/outlet/LslStreamOutlet.worker.js.map +1 -0
- package/build/index.d.ts +12 -14
- package/build/index.js +14 -16
- package/build/index.js.map +1 -1
- package/build/scripts/finalizeHandles.d.ts +2 -0
- package/build/scripts/finalizeHandles.js +48 -0
- package/build/scripts/finalizeHandles.js.map +1 -0
- package/build/scripts/runLslEventMarkerEmitter.js +11 -0
- package/build/scripts/runLslEventMarkerEmitter.js.map +1 -0
- package/build/scripts/runOutletToInlet.js +7 -6
- package/build/scripts/runOutletToInlet.js.map +1 -1
- package/build/scripts/testDestroyOutlet.d.ts +1 -0
- package/build/scripts/testDestroyOutlet.js +4 -0
- package/build/scripts/testDestroyOutlet.js.map +1 -0
- package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.d.ts +6 -0
- package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js +10 -0
- package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js.map +1 -0
- package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.d.ts +17 -0
- package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js +30 -0
- package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js.map +1 -0
- package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.d.ts +10 -0
- package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js +23 -0
- package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js.map +1 -0
- package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.d.ts +9 -0
- package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js +19 -0
- package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js.map +1 -0
- package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +3 -3
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js +3 -3
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
- package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +8 -10
- package/build/testDoubles/StreamInlet/FakeStreamInlet.js +6 -7
- package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
- package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -5
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js +2 -8
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +7 -9
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +5 -8
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
- package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.d.ts +1 -1
- package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js +1 -1
- package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js.map +1 -1
- package/build/testDoubles/WorkerThreads/FakeWorker.d.ts +27 -0
- package/build/testDoubles/WorkerThreads/FakeWorker.js +67 -0
- package/build/testDoubles/WorkerThreads/FakeWorker.js.map +1 -0
- package/build/validations.js +0 -2
- package/build/validations.js.map +1 -1
- package/package.json +24 -8
- package/src/.spruce/settings.json +5 -1
- package/src/__tests__/AbstractPackageTest.ts +13 -15
- package/src/__tests__/impl/LslBackpressureMonitor.test.ts +25 -0
- package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +322 -0
- package/src/__tests__/impl/LslStreamInfo.test.ts +3 -40
- package/src/__tests__/impl/LslStreamInlet.test.ts +366 -95
- package/src/__tests__/impl/LslStreamOutlet.test.ts +61 -69
- package/src/__tests__/impl/LslWebSocketBridge.test.ts +18 -18
- package/src/assertions.ts +2 -1
- package/src/impl/LslBackpressureMonitor.ts +13 -0
- package/src/impl/LslEventMarkerEmitter.ts +133 -0
- package/src/impl/LslStreamInfo.ts +17 -29
- package/src/impl/LslStreamInlet.ts +105 -208
- package/src/impl/LslStreamOutlet.ts +54 -78
- package/src/impl/LslWebSocketBridge.ts +12 -12
- package/src/impl/workers/inlet/LslStreamInlet.worker.ts +74 -0
- package/src/impl/workers/inlet/StreamInletWorker.ts +348 -0
- package/src/impl/workers/outlet/LslStreamOutlet.worker.ts +144 -0
- package/src/index.ts +14 -17
- package/src/scripts/finalizeHandles.ts +63 -0
- package/src/scripts/runLslEventMarkerEmitter.ts +15 -0
- package/src/scripts/runOutletToInlet.ts +10 -8
- package/src/scripts/testDestroyOutlet.ts +5 -0
- package/src/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.ts +13 -0
- package/src/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.ts +46 -0
- package/src/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.ts +31 -0
- package/src/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.ts +29 -0
- package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +4 -4
- package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +11 -17
- package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +3 -16
- package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +12 -15
- package/src/testDoubles/WebSocketBridge/FakeWebSocketBridge.ts +1 -1
- package/src/testDoubles/WorkerThreads/FakeWorker.ts +99 -0
- package/src/validations.ts +0 -2
- package/build/__tests__/impl/LiblslAdapter.test.d.ts +0 -79
- package/build/__tests__/impl/LiblslAdapter.test.js +0 -688
- package/build/__tests__/impl/LiblslAdapter.test.js.map +0 -1
- package/build/__tests__/impl/LslEventMarkerOutlet.test.d.ts +0 -21
- package/build/__tests__/impl/LslEventMarkerOutlet.test.js +0 -160
- package/build/__tests__/impl/LslEventMarkerOutlet.test.js.map +0 -1
- package/build/handleError.d.ts +0 -8
- package/build/handleError.js +0 -25
- package/build/handleError.js.map +0 -1
- package/build/impl/LiblslAdapter.d.ts +0 -159
- package/build/impl/LiblslAdapter.js +0 -317
- package/build/impl/LiblslAdapter.js.map +0 -1
- package/build/impl/LslEventMarkerOutlet.d.ts +0 -28
- package/build/impl/LslEventMarkerOutlet.js +0 -63
- package/build/impl/LslEventMarkerOutlet.js.map +0 -1
- package/build/scripts/runLslEventMarkerOutlet.js +0 -18
- package/build/scripts/runLslEventMarkerOutlet.js.map +0 -1
- package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.d.ts +0 -13
- package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js +0 -26
- package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js.map +0 -1
- package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.d.ts +0 -17
- package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js +0 -46
- package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js.map +0 -1
- package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.d.ts +0 -9
- package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js +0 -19
- package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js.map +0 -1
- package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.d.ts +0 -9
- package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js +0 -19
- package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js.map +0 -1
- package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +0 -50
- package/build/testDoubles/Liblsl/FakeLiblsl.js +0 -126
- package/build/testDoubles/Liblsl/FakeLiblsl.js.map +0 -1
- package/src/__tests__/impl/LiblslAdapter.test.ts +0 -817
- package/src/__tests__/impl/LslEventMarkerOutlet.test.ts +0 -195
- package/src/handleError.ts +0 -24
- package/src/impl/LiblslAdapter.ts +0 -548
- package/src/impl/LslEventMarkerOutlet.ts +0 -97
- package/src/scripts/runLslEventMarkerOutlet.ts +0 -23
- package/src/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.ts +0 -36
- package/src/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.ts +0 -75
- package/src/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.ts +0 -24
- package/src/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.ts +0 -28
- package/src/testDoubles/Liblsl/FakeLiblsl.ts +0 -182
- /package/build/scripts/{runLslEventMarkerOutlet.d.ts → runLslEventMarkerEmitter.d.ts} +0 -0
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
import { Worker } from 'node:worker_threads'
|
|
2
|
+
import { ChannelFormat, LslSample, FakeLiblsl } from '@neurodevs/ndx-native'
|
|
1
3
|
import { test, assert } from '@neurodevs/node-tdd'
|
|
2
4
|
|
|
3
|
-
import { ChannelFormat, LslSample } from 'impl/LiblslAdapter.js'
|
|
4
5
|
import { CHANNEL_FORMATS } from '../../consts.js'
|
|
5
|
-
import { StreamInfo } from '../../impl/LslStreamInfo.js'
|
|
6
6
|
import LslStreamOutlet, {
|
|
7
7
|
StreamOutletOptions,
|
|
8
8
|
} from '../../impl/LslStreamOutlet.js'
|
|
9
|
+
import {
|
|
10
|
+
setOutletHandleError,
|
|
11
|
+
setOutletLiblslAdapter,
|
|
12
|
+
} from '../../impl/workers/outlet/LslStreamOutlet.worker.js'
|
|
9
13
|
import {
|
|
10
14
|
TEST_SUPPORTED_CHANNEL_FORMATS,
|
|
11
15
|
TEST_UNSUPPORTED_CHANNEL_FORMATS,
|
|
12
16
|
TestChannelFormat,
|
|
13
17
|
} from '../../testDoubles/consts.js'
|
|
14
18
|
import generateRandomOutletOptions from '../../testDoubles/generateRandomOutletOptions.js'
|
|
15
|
-
import FakeLiblsl from '../../testDoubles/Liblsl/FakeLiblsl.js'
|
|
16
19
|
import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
|
|
20
|
+
import FakeWorker from '../../testDoubles/WorkerThreads/FakeWorker.js'
|
|
17
21
|
import AbstractPackageTest from '../AbstractPackageTest.js'
|
|
18
22
|
|
|
19
23
|
export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
@@ -28,6 +32,11 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
28
32
|
|
|
29
33
|
this.setFakeLiblsl()
|
|
30
34
|
this.setFakeStreamInfo()
|
|
35
|
+
|
|
36
|
+
LslStreamOutlet.Worker = FakeWorker as unknown as typeof Worker
|
|
37
|
+
FakeWorker.resetTestDoubles()
|
|
38
|
+
|
|
39
|
+
setOutletLiblslAdapter(this.fakeLiblsl)
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
@test()
|
|
@@ -106,15 +115,15 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
106
115
|
outlet.pushSample(sample)
|
|
107
116
|
|
|
108
117
|
assert.isEqual(
|
|
109
|
-
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.
|
|
110
|
-
this.fakeLiblsl.
|
|
118
|
+
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.outletHandle,
|
|
119
|
+
this.fakeLiblsl.outletHandle
|
|
111
120
|
)
|
|
112
121
|
assert.isEqualDeep(
|
|
113
122
|
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.sample,
|
|
114
123
|
sample
|
|
115
124
|
)
|
|
116
125
|
assert.isEqualDeep(this.fakeLiblsl.lastCreateOutletOptions, {
|
|
117
|
-
|
|
126
|
+
infoHandle: this.fakeLiblsl.infoHandle,
|
|
118
127
|
chunkSize: this.randomOutletOptions.chunkSize,
|
|
119
128
|
maxBufferedMs: this.randomOutletOptions.maxBufferedMs,
|
|
120
129
|
})
|
|
@@ -131,8 +140,8 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
131
140
|
outlet.pushSample(sample)
|
|
132
141
|
|
|
133
142
|
assert.isEqual(
|
|
134
|
-
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.
|
|
135
|
-
this.fakeLiblsl.
|
|
143
|
+
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.outletHandle,
|
|
144
|
+
this.fakeLiblsl.outletHandle
|
|
136
145
|
)
|
|
137
146
|
assert.isEqualDeep(
|
|
138
147
|
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.sample,
|
|
@@ -199,9 +208,9 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
199
208
|
outlet.destroy()
|
|
200
209
|
|
|
201
210
|
assert.isEqualDeep(
|
|
202
|
-
this.fakeLiblsl.
|
|
203
|
-
{
|
|
204
|
-
'Did not call
|
|
211
|
+
this.fakeLiblsl.lastDestroyOutletOptions,
|
|
212
|
+
{ outletHandle: this.fakeLiblsl.outletHandle },
|
|
213
|
+
'Did not call destroy on outlet!'
|
|
205
214
|
)
|
|
206
215
|
}
|
|
207
216
|
|
|
@@ -215,6 +224,45 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
215
224
|
assert.isEqual(this.fakeLiblsl.createOutletHitCount, 1)
|
|
216
225
|
}
|
|
217
226
|
|
|
227
|
+
@test()
|
|
228
|
+
protected static async pushSampleAcceptsOptionalPreciseTimestamp() {
|
|
229
|
+
const outlet = await this.FloatOutlet()
|
|
230
|
+
const fakeTimestamp = Math.random() * 1000
|
|
231
|
+
|
|
232
|
+
outlet.pushSample([1.0], fakeTimestamp)
|
|
233
|
+
|
|
234
|
+
assert.isEqual(
|
|
235
|
+
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.timestamp,
|
|
236
|
+
fakeTimestamp
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@test()
|
|
241
|
+
protected static async pushSampleHandlesErrorCode() {
|
|
242
|
+
let passedErrorCode: number | undefined
|
|
243
|
+
|
|
244
|
+
setOutletHandleError((errorCode: number) => {
|
|
245
|
+
passedErrorCode = errorCode
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
FakeLiblsl.fakeErrorCode = [-4, -3, -2, -1, 0][
|
|
249
|
+
Math.floor(Math.random() * 5)
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
const outlet =
|
|
253
|
+
Math.random() > 0.5
|
|
254
|
+
? await this.FloatOutlet()
|
|
255
|
+
: await this.StringOutlet()
|
|
256
|
+
|
|
257
|
+
outlet.pushSample([1.0])
|
|
258
|
+
|
|
259
|
+
assert.isEqualDeep(
|
|
260
|
+
passedErrorCode,
|
|
261
|
+
FakeLiblsl.fakeErrorCode,
|
|
262
|
+
'Did not pass the expected error code to handleError!'
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
218
266
|
@test()
|
|
219
267
|
protected static async waitsForTenMsAfterConstructionBeforeReturning() {
|
|
220
268
|
const startMs = Date.now()
|
|
@@ -252,62 +300,6 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
252
300
|
assert.isEqualDeep(outlet['units'], 'N/A', 'Did not set units as N/A!')
|
|
253
301
|
}
|
|
254
302
|
|
|
255
|
-
@test()
|
|
256
|
-
protected static async throwsWithUnknownErrorCode() {
|
|
257
|
-
await this.assertThrowsWithErrorCode(
|
|
258
|
-
-999,
|
|
259
|
-
`An unknown liblsl error has occurred!`
|
|
260
|
-
)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
@test()
|
|
264
|
-
protected static async throwsWithErrorCodeNegativeOne() {
|
|
265
|
-
await this.assertThrowsWithErrorCode(
|
|
266
|
-
-1,
|
|
267
|
-
`The liblsl operation failed due to a timeout!`
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
@test()
|
|
272
|
-
protected static async throwsWithErrorCodeNegativeTwo() {
|
|
273
|
-
await this.assertThrowsWithErrorCode(
|
|
274
|
-
-2,
|
|
275
|
-
`The liblsl stream has been lost!`
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
@test()
|
|
280
|
-
protected static async throwsWithErrorCodeNegativeThree() {
|
|
281
|
-
await this.assertThrowsWithErrorCode(
|
|
282
|
-
-3,
|
|
283
|
-
'A liblsl argument was incorrectly specified!'
|
|
284
|
-
)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
@test()
|
|
288
|
-
protected static async throwsWithErrorCodeNegativeFour() {
|
|
289
|
-
await this.assertThrowsWithErrorCode(
|
|
290
|
-
-4,
|
|
291
|
-
'An internal liblsl error has occurred!'
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
private static async assertThrowsWithErrorCode(
|
|
296
|
-
errorCode: number,
|
|
297
|
-
message: string
|
|
298
|
-
) {
|
|
299
|
-
FakeLiblsl.fakeErrorCode = errorCode
|
|
300
|
-
|
|
301
|
-
const outlet =
|
|
302
|
-
Math.random() > 0.5
|
|
303
|
-
? await this.FloatOutlet()
|
|
304
|
-
: await this.StringOutlet()
|
|
305
|
-
|
|
306
|
-
assert.doesThrow(() => {
|
|
307
|
-
outlet.pushSample([1.0])
|
|
308
|
-
}, message)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
303
|
private static async assertThrowsWithEmptyChannelNames() {
|
|
312
304
|
await this.createAndAssertThrows(
|
|
313
305
|
'channelNames',
|
|
@@ -386,7 +378,7 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
386
378
|
}
|
|
387
379
|
|
|
388
380
|
class CheckingOutlet extends LslStreamOutlet {
|
|
389
|
-
public constructor(
|
|
390
|
-
super(
|
|
381
|
+
public constructor(options: StreamOutletOptions) {
|
|
382
|
+
super(options)
|
|
391
383
|
}
|
|
392
384
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { randomInt } from 'crypto'
|
|
2
2
|
import { test, assert } from '@neurodevs/node-tdd'
|
|
3
3
|
|
|
4
|
+
import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
|
|
4
5
|
import LslWebSocketBridge, {
|
|
5
6
|
WebSocketBridgeOptions,
|
|
6
7
|
} from '../../impl/LslWebSocketBridge.js'
|
|
7
|
-
import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
|
|
8
8
|
import FakeStreamInlet from '../../testDoubles/StreamInlet/FakeStreamInlet.js'
|
|
9
9
|
import SpyLslWebSocketBridge from '../../testDoubles/WebSocketBridge/SpyLslWebSocketBridge.js'
|
|
10
10
|
import FakeWebSocket from '../../testDoubles/WebSockets/FakeWebSocket.js'
|
|
@@ -36,12 +36,12 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
36
36
|
protected static async createsLslStreamInlet() {
|
|
37
37
|
const call = FakeStreamInlet.callsToConstructor[0]
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
const chunkSize = call
|
|
39
|
+
const sourceId = call.options!.sourceId
|
|
40
|
+
const chunkSize = call.options!.chunkSize
|
|
41
41
|
|
|
42
42
|
assert.isEqualDeep(
|
|
43
43
|
{
|
|
44
|
-
|
|
44
|
+
sourceId,
|
|
45
45
|
chunkSize,
|
|
46
46
|
},
|
|
47
47
|
this.inletOptions
|
|
@@ -50,7 +50,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
50
50
|
|
|
51
51
|
@test()
|
|
52
52
|
protected static async activateCallsStartPullingOnInlet() {
|
|
53
|
-
this.activate()
|
|
53
|
+
await this.activate()
|
|
54
54
|
|
|
55
55
|
assert.isEqual(
|
|
56
56
|
FakeStreamInlet.numCallsToStartPulling,
|
|
@@ -61,7 +61,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
61
61
|
|
|
62
62
|
@test()
|
|
63
63
|
protected static async deactivateCallsStopPullingOnInlet() {
|
|
64
|
-
this.activate()
|
|
64
|
+
await this.activate()
|
|
65
65
|
this.deactivate()
|
|
66
66
|
|
|
67
67
|
assert.isEqual(
|
|
@@ -93,7 +93,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
93
93
|
|
|
94
94
|
@test()
|
|
95
95
|
protected static async activateSendsDataToWebSocketClients() {
|
|
96
|
-
this.activate()
|
|
96
|
+
await this.activate()
|
|
97
97
|
|
|
98
98
|
const { samples, timestamps } = this.simulateOnDataCallback()
|
|
99
99
|
|
|
@@ -119,7 +119,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
119
119
|
client.readyState = WebSocket.CONNECTING
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
this.activate()
|
|
122
|
+
await this.activate()
|
|
123
123
|
this.simulateOnDataCallback()
|
|
124
124
|
|
|
125
125
|
const calls = FakeWebSocket.callsToSend.filter((call) =>
|
|
@@ -150,8 +150,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
150
150
|
protected static async throwsIfActivateIsCalledAfterDestroy() {
|
|
151
151
|
this.destroy()
|
|
152
152
|
|
|
153
|
-
assert.
|
|
154
|
-
this.activate()
|
|
153
|
+
await assert.doesThrowAsync(async () => {
|
|
154
|
+
await this.activate()
|
|
155
155
|
}, `\n\n Cannot activate bridge after destroying it! \n\n Please create and activate a new instance. \n`)
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -184,7 +184,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
184
184
|
|
|
185
185
|
@test()
|
|
186
186
|
protected static async activateSendsDataToRemoteSockets() {
|
|
187
|
-
this.activate()
|
|
187
|
+
await this.activate()
|
|
188
188
|
|
|
189
189
|
const { samples, timestamps } = this.simulateOnDataCallback()
|
|
190
190
|
|
|
@@ -218,7 +218,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
218
218
|
socket.readyState = WebSocket.CONNECTING
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
this.activate()
|
|
221
|
+
await this.activate()
|
|
222
222
|
this.simulateOnDataCallback()
|
|
223
223
|
|
|
224
224
|
const calls = FakeWebSocket.callsToSend.filter((call) =>
|
|
@@ -253,8 +253,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
253
253
|
}, `At least one of listenPort or connectUrls must be provided!`)
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
private static activate() {
|
|
257
|
-
this.instance.activate()
|
|
256
|
+
private static async activate() {
|
|
257
|
+
await this.instance.activate()
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
private static deactivate() {
|
|
@@ -266,8 +266,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
private static simulateOnDataCallback() {
|
|
269
|
-
const samples = new Float32Array([1, 2, 3, 4, 5, 6])
|
|
270
|
-
const timestamps = new Float64Array([7, 8])
|
|
269
|
+
const samples = [...new Float32Array([1, 2, 3, 4, 5, 6])]
|
|
270
|
+
const timestamps = [...new Float64Array([7, 8])]
|
|
271
271
|
|
|
272
272
|
FakeStreamInlet.callsToConstructor[0]?.onData!(samples, timestamps)
|
|
273
273
|
return { samples, timestamps }
|
|
@@ -277,8 +277,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
277
277
|
|
|
278
278
|
private static readonly connectUrls = [this.generateId(), this.generateId()]
|
|
279
279
|
|
|
280
|
-
private static readonly inletOptions = {
|
|
281
|
-
|
|
280
|
+
private static readonly inletOptions: StreamInletOptions = {
|
|
281
|
+
sourceId: this.sourceId,
|
|
282
282
|
chunkSize: randomInt(1, 100),
|
|
283
283
|
}
|
|
284
284
|
|
package/src/assertions.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default class LslBackpressureMonitor implements BackpressureMonitor {
|
|
2
|
+
public static Class?: BackpressureMonitorConstructor
|
|
3
|
+
|
|
4
|
+
protected constructor() {}
|
|
5
|
+
|
|
6
|
+
public static Create() {
|
|
7
|
+
return new (this.Class ?? this)()
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BackpressureMonitor {}
|
|
12
|
+
|
|
13
|
+
export type BackpressureMonitorConstructor = new () => BackpressureMonitor
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { generateShortId } from '@neurodevs/generate-id'
|
|
2
|
+
|
|
3
|
+
import LslStreamOutlet, {
|
|
4
|
+
StreamOutletOptions,
|
|
5
|
+
StreamOutlet,
|
|
6
|
+
} from './LslStreamOutlet.js'
|
|
7
|
+
|
|
8
|
+
export default class LslEventMarkerEmitter implements EventMarkerEmitter {
|
|
9
|
+
public static Class?: EventMarkerEmitterConstructor
|
|
10
|
+
|
|
11
|
+
protected outlet: StreamOutlet
|
|
12
|
+
private isRunning = false
|
|
13
|
+
private waitResolve?: () => void
|
|
14
|
+
private timeout?: any
|
|
15
|
+
|
|
16
|
+
protected constructor(outlet: StreamOutlet) {
|
|
17
|
+
this.outlet = outlet
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public static async Create(options?: EventMarkerEmitterOptions) {
|
|
21
|
+
const outlet = await this.LslStreamOutlet(options)
|
|
22
|
+
return new (this.Class ?? this)(outlet)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public async emit(markerName: string, options?: EventMarkerOptions) {
|
|
26
|
+
this.throwIfAlreadyRunning('emit')
|
|
27
|
+
this.isRunning = true
|
|
28
|
+
|
|
29
|
+
const { waitAfterMs } = options || {}
|
|
30
|
+
|
|
31
|
+
this.pushMarkerToOutlet(markerName)
|
|
32
|
+
|
|
33
|
+
if (waitAfterMs) {
|
|
34
|
+
await this.wait(waitAfterMs)
|
|
35
|
+
}
|
|
36
|
+
this.isRunning = false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private throwIfAlreadyRunning(methodName: string) {
|
|
40
|
+
if (this.isRunning) {
|
|
41
|
+
throw new Error(`Cannot call ${methodName} while already running!`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async emitMany(markers: TimedEventMarker[]) {
|
|
46
|
+
this.throwIfAlreadyRunning('emitMany')
|
|
47
|
+
this.isRunning = true
|
|
48
|
+
|
|
49
|
+
for (const marker of markers) {
|
|
50
|
+
const { name, waitAfterMs } = marker
|
|
51
|
+
this.pushMarkerToOutlet(name)
|
|
52
|
+
|
|
53
|
+
await this.wait(waitAfterMs)
|
|
54
|
+
|
|
55
|
+
if (!this.isRunning) {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.isRunning = false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private pushMarkerToOutlet(markerName: string) {
|
|
63
|
+
this.outlet.pushSample([markerName])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected async wait(forMs: number) {
|
|
67
|
+
return new Promise((resolve) => this.setTimeout(resolve, forMs))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private setTimeout(resolve: (value: unknown) => void, waitAfterMs: number) {
|
|
71
|
+
this.waitResolve = resolve as any
|
|
72
|
+
this.timeout = setTimeout(resolve, waitAfterMs)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public interrupt() {
|
|
76
|
+
this.waitResolve?.()
|
|
77
|
+
clearTimeout(this.timeout)
|
|
78
|
+
this.isRunning = false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public destroy() {
|
|
82
|
+
if (this.isRunning) {
|
|
83
|
+
this.interrupt()
|
|
84
|
+
}
|
|
85
|
+
this.outlet.destroy()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static generateDefaultOutletOptions() {
|
|
89
|
+
const uniqueId = generateShortId()
|
|
90
|
+
|
|
91
|
+
const name = `Event markers (${uniqueId})`
|
|
92
|
+
const sourceId = `event-markers-${uniqueId}`
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
name,
|
|
96
|
+
type: 'Markers',
|
|
97
|
+
sourceId,
|
|
98
|
+
channelNames: ['Markers'],
|
|
99
|
+
channelFormat: 'string',
|
|
100
|
+
sampleRateHz: 0,
|
|
101
|
+
chunkSize: 1,
|
|
102
|
+
} as StreamOutletOptions
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static LslStreamOutlet(options?: Partial<StreamOutletOptions>) {
|
|
106
|
+
return LslStreamOutlet.Create({
|
|
107
|
+
...this.generateDefaultOutletOptions(),
|
|
108
|
+
...options,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface EventMarkerEmitter {
|
|
114
|
+
emit(markerName: string, options?: EventMarkerOptions): Promise<void>
|
|
115
|
+
emitMany(markers: TimedEventMarker[]): Promise<void>
|
|
116
|
+
interrupt(): void
|
|
117
|
+
destroy(): void
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type EventMarkerEmitterOptions = Partial<StreamOutletOptions>
|
|
121
|
+
|
|
122
|
+
export type EventMarkerEmitterConstructor = new (
|
|
123
|
+
outlet: StreamOutlet
|
|
124
|
+
) => EventMarkerEmitter
|
|
125
|
+
|
|
126
|
+
export interface EventMarkerOptions {
|
|
127
|
+
waitAfterMs?: number
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface TimedEventMarker {
|
|
131
|
+
name: string
|
|
132
|
+
waitAfterMs: number
|
|
133
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import generateId from '@neurodevs/generate-id'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {
|
|
3
|
+
ChannelFormat,
|
|
4
|
+
InfoHandle,
|
|
5
|
+
LiblslAdapter,
|
|
6
|
+
CHANNEL_FORMATS_MAP,
|
|
7
|
+
} from '@neurodevs/ndx-native'
|
|
6
8
|
|
|
7
9
|
export default class LslStreamInfo implements StreamInfo {
|
|
8
10
|
public static Class?: StreamInfoConstructor
|
|
9
11
|
|
|
10
|
-
private static instanceCache = new Map<BoundStreamInfo, StreamInfo>()
|
|
11
|
-
|
|
12
12
|
public readonly name: string
|
|
13
13
|
public readonly type: string
|
|
14
14
|
public readonly sourceId: string
|
|
@@ -17,7 +17,8 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
17
17
|
public readonly channelFormat: ChannelFormat
|
|
18
18
|
public readonly sampleRateHz: number
|
|
19
19
|
public readonly units: string
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
public readonly infoHandle: InfoHandle
|
|
21
22
|
|
|
22
23
|
private lsl = LiblslAdapter.getInstance()
|
|
23
24
|
|
|
@@ -30,7 +31,6 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
30
31
|
channelFormat,
|
|
31
32
|
sampleRateHz,
|
|
32
33
|
units = 'N/A',
|
|
33
|
-
boundInfo,
|
|
34
34
|
} = options
|
|
35
35
|
|
|
36
36
|
this.name = name
|
|
@@ -42,25 +42,12 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
42
42
|
this.sampleRateHz = sampleRateHz
|
|
43
43
|
this.units = units
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.appendChannelsToStreamInfo()
|
|
48
|
-
} else {
|
|
49
|
-
this.boundInfo = boundInfo
|
|
50
|
-
}
|
|
45
|
+
this.infoHandle = this.createStreamInfo()
|
|
46
|
+
this.appendChannelsToStreamInfo()
|
|
51
47
|
}
|
|
52
48
|
|
|
53
49
|
public static Create(options: StreamInfoOptions) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const { boundInfo: handle } = instance
|
|
57
|
-
this.instanceCache.set(handle, instance)
|
|
58
|
-
|
|
59
|
-
return instance
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public static From(handle: BoundStreamInfo) {
|
|
63
|
-
return this.instanceCache.get(handle)!
|
|
50
|
+
return new (this.Class ?? this)(options)
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
private createStreamInfo() {
|
|
@@ -80,7 +67,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
80
67
|
|
|
81
68
|
private appendChannelsToStreamInfo() {
|
|
82
69
|
this.lsl.appendChannelsToStreamInfo({
|
|
83
|
-
|
|
70
|
+
infoHandle: this.infoHandle,
|
|
84
71
|
channels: this.channelNames.map((label: string) => ({
|
|
85
72
|
label,
|
|
86
73
|
units: this.units,
|
|
@@ -90,7 +77,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
90
77
|
}
|
|
91
78
|
|
|
92
79
|
public destroy() {
|
|
93
|
-
this.lsl.destroyStreamInfo({
|
|
80
|
+
this.lsl.destroyStreamInfo({ infoHandle: this.infoHandle })
|
|
94
81
|
}
|
|
95
82
|
}
|
|
96
83
|
|
|
@@ -104,10 +91,12 @@ export interface StreamInfo {
|
|
|
104
91
|
readonly channelCount: number
|
|
105
92
|
readonly channelFormat: ChannelFormat
|
|
106
93
|
readonly sampleRateHz: number
|
|
107
|
-
readonly
|
|
94
|
+
readonly infoHandle: InfoHandle
|
|
108
95
|
}
|
|
109
96
|
|
|
110
|
-
type StreamInfoConstructor = new (
|
|
97
|
+
export type StreamInfoConstructor = new (
|
|
98
|
+
options: StreamInfoOptions
|
|
99
|
+
) => StreamInfo
|
|
111
100
|
|
|
112
101
|
export interface StreamInfoOptions {
|
|
113
102
|
channelNames: string[]
|
|
@@ -117,5 +106,4 @@ export interface StreamInfoOptions {
|
|
|
117
106
|
type?: string
|
|
118
107
|
sourceId?: string
|
|
119
108
|
units?: string
|
|
120
|
-
boundInfo?: BoundStreamInfo
|
|
121
109
|
}
|