@neurodevs/node-lsl 17.1.1 → 18.1.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/.vscode/launch.json +54 -57
- package/.vscode/settings.json +61 -66
- package/.vscode/tasks.json +98 -129
- package/README.md +20 -44
- package/build/__tests__/AbstractPackageTest.d.ts +7 -5
- package/build/__tests__/AbstractPackageTest.js +22 -15
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/BleDeviceController.test.d.ts +54 -0
- package/build/__tests__/impl/BleDeviceController.test.js +333 -0
- package/build/__tests__/impl/BleDeviceController.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 +20 -8
- package/build/__tests__/impl/LslStreamInlet.test.js +224 -69
- 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 +40 -50
- package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
- package/build/__tests__/impl/LslWebSocketBridge.test.js +19 -20
- 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/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 -50
- package/build/impl/LslStreamInlet.js +79 -149
- 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 +10 -10
- package/build/impl/LslWebSocketBridge.js.map +1 -1
- package/build/impl/controllers/BleDeviceController.d.ts +58 -0
- package/build/impl/controllers/BleDeviceController.js +145 -0
- package/build/impl/controllers/BleDeviceController.js.map +1 -0
- 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 +14 -14
- package/build/index.js +16 -16
- package/build/index.js.map +1 -1
- package/build/scripts/discoverMuse.js +40 -0
- package/build/scripts/discoverMuse.js.map +1 -0
- 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.d.ts +1 -0
- package/build/scripts/runLslEventMarkerEmitter.js +11 -0
- package/build/scripts/runLslEventMarkerEmitter.js.map +1 -0
- package/build/scripts/runMuse.d.ts +1 -0
- package/build/scripts/runMuse.js +40 -0
- package/build/scripts/runMuse.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/BleController/FakeBleController.d.ts +24 -0
- package/build/testDoubles/BleController/FakeBleController.js +42 -0
- package/build/testDoubles/BleController/FakeBleController.js.map +1 -0
- package/build/testDoubles/BleController/SpyBleController.d.ts +14 -0
- package/build/testDoubles/BleController/SpyBleController.js +48 -0
- package/build/testDoubles/BleController/SpyBleController.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 +8 -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/testDoubles/generateRandomOutletOptions.js +1 -1
- package/build/testDoubles/generateRandomOutletOptions.js.map +1 -1
- package/build/validations.js +0 -2
- package/build/validations.js.map +1 -1
- package/eslint.config.js +3 -0
- package/package.json +27 -49
- package/prettier.config.js +3 -0
- package/src/__tests__/AbstractPackageTest.ts +38 -21
- package/src/__tests__/impl/BleDeviceController.test.ts +492 -0
- package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +323 -0
- package/src/__tests__/impl/LslStreamInfo.test.ts +3 -40
- package/src/__tests__/impl/LslStreamInlet.test.ts +344 -97
- package/src/__tests__/impl/LslStreamOutlet.test.ts +65 -73
- package/src/__tests__/impl/LslWebSocketBridge.test.ts +22 -21
- package/src/assertions.ts +2 -1
- package/src/impl/LslEventMarkerEmitter.ts +133 -0
- package/src/impl/LslStreamInfo.ts +17 -29
- package/src/impl/LslStreamInlet.ts +102 -215
- package/src/impl/LslStreamOutlet.ts +54 -78
- package/src/impl/LslWebSocketBridge.ts +14 -14
- package/src/impl/controllers/BleDeviceController.ts +223 -0
- 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 +16 -16
- package/src/scripts/discoverMuse.ts +50 -0
- package/src/scripts/finalizeHandles.ts +63 -0
- package/src/scripts/runLslEventMarkerEmitter.ts +15 -0
- package/src/scripts/runMuse.ts +50 -0
- package/src/scripts/runOutletToInlet.ts +10 -8
- package/src/scripts/testDestroyOutlet.ts +5 -0
- package/src/testDoubles/BleController/FakeBleController.ts +70 -0
- package/src/testDoubles/BleController/SpyBleController.ts +61 -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 +15 -15
- package/src/testDoubles/WebSocketBridge/FakeWebSocketBridge.ts +1 -1
- package/src/testDoubles/WorkerThreads/FakeWorker.ts +99 -0
- package/src/testDoubles/generateRandomOutletOptions.ts +2 -1
- package/src/validations.ts +0 -2
- package/tsconfig.json +23 -27
- package/build/.spruce/settings.json +0 -11
- 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 -154
- 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/eslint.config.mjs +0 -3
- package/src/.spruce/settings.json +0 -11
- 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 -543
- 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 → discoverMuse.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
|
+
setOutletHandleLslError,
|
|
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,20 +115,20 @@ 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
|
})
|
|
121
130
|
assert.isNumber(
|
|
122
|
-
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.
|
|
131
|
+
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.timestampSec
|
|
123
132
|
)
|
|
124
133
|
}
|
|
125
134
|
|
|
@@ -131,15 +140,15 @@ 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,
|
|
139
148
|
sample
|
|
140
149
|
)
|
|
141
150
|
assert.isNumber(
|
|
142
|
-
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.
|
|
151
|
+
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.timestampSec
|
|
143
152
|
)
|
|
144
153
|
}
|
|
145
154
|
|
|
@@ -150,13 +159,13 @@ export default class LslStreamOutletTest extends AbstractPackageTest {
|
|
|
150
159
|
|
|
151
160
|
outlet.pushSample(sample)
|
|
152
161
|
const t1 =
|
|
153
|
-
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.
|
|
162
|
+
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.timestampSec
|
|
154
163
|
|
|
155
164
|
await this.wait(10)
|
|
156
165
|
|
|
157
166
|
outlet.pushSample(sample)
|
|
158
167
|
const t2 =
|
|
159
|
-
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.
|
|
168
|
+
this.fakeLiblsl.lastPushSampleStringTimestampOptions?.timestampSec
|
|
160
169
|
|
|
161
170
|
assert.isNotEqual(t1, t2)
|
|
162
171
|
assert.isEqual(this.fakeLiblsl.localClockHitCount, 2)
|
|
@@ -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 timestampSec = Math.random() * 10000
|
|
231
|
+
|
|
232
|
+
outlet.pushSample([1.0], timestampSec)
|
|
233
|
+
|
|
234
|
+
assert.isEqual(
|
|
235
|
+
this.fakeLiblsl.lastPushSampleFloatTimestampOptions?.timestampSec,
|
|
236
|
+
timestampSec
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@test()
|
|
241
|
+
protected static async pushSampleHandlesErrorCode() {
|
|
242
|
+
let passedErrorCode: number | undefined
|
|
243
|
+
|
|
244
|
+
setOutletHandleLslError((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,11 @@
|
|
|
1
|
-
import { randomInt } from 'crypto'
|
|
1
|
+
import { randomInt } from 'node:crypto'
|
|
2
|
+
|
|
2
3
|
import { test, assert } from '@neurodevs/node-tdd'
|
|
3
4
|
|
|
5
|
+
import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
|
|
4
6
|
import LslWebSocketBridge, {
|
|
5
7
|
WebSocketBridgeOptions,
|
|
6
8
|
} from '../../impl/LslWebSocketBridge.js'
|
|
7
|
-
import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
|
|
8
9
|
import FakeStreamInlet from '../../testDoubles/StreamInlet/FakeStreamInlet.js'
|
|
9
10
|
import SpyLslWebSocketBridge from '../../testDoubles/WebSocketBridge/SpyLslWebSocketBridge.js'
|
|
10
11
|
import FakeWebSocket from '../../testDoubles/WebSockets/FakeWebSocket.js'
|
|
@@ -36,12 +37,12 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
36
37
|
protected static async createsLslStreamInlet() {
|
|
37
38
|
const call = FakeStreamInlet.callsToConstructor[0]
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
-
const chunkSize = call
|
|
40
|
+
const sourceId = call.options!.sourceId
|
|
41
|
+
const chunkSize = call.options!.chunkSize
|
|
41
42
|
|
|
42
43
|
assert.isEqualDeep(
|
|
43
44
|
{
|
|
44
|
-
|
|
45
|
+
sourceId,
|
|
45
46
|
chunkSize,
|
|
46
47
|
},
|
|
47
48
|
this.inletOptions
|
|
@@ -50,7 +51,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
50
51
|
|
|
51
52
|
@test()
|
|
52
53
|
protected static async activateCallsStartPullingOnInlet() {
|
|
53
|
-
this.activate()
|
|
54
|
+
await this.activate()
|
|
54
55
|
|
|
55
56
|
assert.isEqual(
|
|
56
57
|
FakeStreamInlet.numCallsToStartPulling,
|
|
@@ -61,7 +62,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
61
62
|
|
|
62
63
|
@test()
|
|
63
64
|
protected static async deactivateCallsStopPullingOnInlet() {
|
|
64
|
-
this.activate()
|
|
65
|
+
await this.activate()
|
|
65
66
|
this.deactivate()
|
|
66
67
|
|
|
67
68
|
assert.isEqual(
|
|
@@ -93,7 +94,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
93
94
|
|
|
94
95
|
@test()
|
|
95
96
|
protected static async activateSendsDataToWebSocketClients() {
|
|
96
|
-
this.activate()
|
|
97
|
+
await this.activate()
|
|
97
98
|
|
|
98
99
|
const { samples, timestamps } = this.simulateOnDataCallback()
|
|
99
100
|
|
|
@@ -119,7 +120,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
119
120
|
client.readyState = WebSocket.CONNECTING
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
this.activate()
|
|
123
|
+
await this.activate()
|
|
123
124
|
this.simulateOnDataCallback()
|
|
124
125
|
|
|
125
126
|
const calls = FakeWebSocket.callsToSend.filter((call) =>
|
|
@@ -150,9 +151,9 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
150
151
|
protected static async throwsIfActivateIsCalledAfterDestroy() {
|
|
151
152
|
this.destroy()
|
|
152
153
|
|
|
153
|
-
assert.
|
|
154
|
-
this.activate()
|
|
155
|
-
}, `\n\
|
|
154
|
+
await assert.doesThrowAsync(async () => {
|
|
155
|
+
await this.activate()
|
|
156
|
+
}, `\n\nCannot activate bridge after destroying it! \n\nPlease create and activate a new instance. \n`)
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
@test()
|
|
@@ -161,7 +162,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
161
162
|
|
|
162
163
|
assert.doesThrow(() => {
|
|
163
164
|
this.deactivate()
|
|
164
|
-
}, `\n\
|
|
165
|
+
}, `\n\nCannot deactivate bridge after destroying it! \n\nPlease create and activate a new instance. \n`)
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
@test()
|
|
@@ -184,7 +185,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
184
185
|
|
|
185
186
|
@test()
|
|
186
187
|
protected static async activateSendsDataToRemoteSockets() {
|
|
187
|
-
this.activate()
|
|
188
|
+
await this.activate()
|
|
188
189
|
|
|
189
190
|
const { samples, timestamps } = this.simulateOnDataCallback()
|
|
190
191
|
|
|
@@ -218,7 +219,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
218
219
|
socket.readyState = WebSocket.CONNECTING
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
this.activate()
|
|
222
|
+
await this.activate()
|
|
222
223
|
this.simulateOnDataCallback()
|
|
223
224
|
|
|
224
225
|
const calls = FakeWebSocket.callsToSend.filter((call) =>
|
|
@@ -253,8 +254,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
253
254
|
}, `At least one of listenPort or connectUrls must be provided!`)
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
private static activate() {
|
|
257
|
-
this.instance.activate()
|
|
257
|
+
private static async activate() {
|
|
258
|
+
await this.instance.activate()
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
private static deactivate() {
|
|
@@ -266,8 +267,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
private static simulateOnDataCallback() {
|
|
269
|
-
const samples = new Float32Array([1, 2, 3, 4, 5, 6])
|
|
270
|
-
const timestamps = new Float64Array([7, 8])
|
|
270
|
+
const samples = [...new Float32Array([1, 2, 3, 4, 5, 6])]
|
|
271
|
+
const timestamps = [...new Float64Array([7, 8])]
|
|
271
272
|
|
|
272
273
|
FakeStreamInlet.callsToConstructor[0]?.onData!(samples, timestamps)
|
|
273
274
|
return { samples, timestamps }
|
|
@@ -277,8 +278,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
277
278
|
|
|
278
279
|
private static readonly connectUrls = [this.generateId(), this.generateId()]
|
|
279
280
|
|
|
280
|
-
private static readonly inletOptions = {
|
|
281
|
-
|
|
281
|
+
private static readonly inletOptions: StreamInletOptions = {
|
|
282
|
+
sourceId: this.sourceId,
|
|
282
283
|
chunkSize: randomInt(1, 100),
|
|
283
284
|
}
|
|
284
285
|
|
package/src/assertions.ts
CHANGED
|
@@ -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
|
}
|