@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.
Files changed (198) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.vscode/launch.json +54 -57
  3. package/.vscode/settings.json +61 -66
  4. package/.vscode/tasks.json +98 -129
  5. package/README.md +20 -44
  6. package/build/__tests__/AbstractPackageTest.d.ts +7 -5
  7. package/build/__tests__/AbstractPackageTest.js +22 -15
  8. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  9. package/build/__tests__/impl/BleDeviceController.test.d.ts +54 -0
  10. package/build/__tests__/impl/BleDeviceController.test.js +333 -0
  11. package/build/__tests__/impl/BleDeviceController.test.js.map +1 -0
  12. package/build/__tests__/impl/LslEventMarkerEmitter.test.d.ts +34 -0
  13. package/build/__tests__/impl/LslEventMarkerEmitter.test.js +243 -0
  14. package/build/__tests__/impl/LslEventMarkerEmitter.test.js.map +1 -0
  15. package/build/__tests__/impl/LslStreamInfo.test.d.ts +0 -3
  16. package/build/__tests__/impl/LslStreamInfo.test.js +2 -27
  17. package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
  18. package/build/__tests__/impl/LslStreamInlet.test.d.ts +20 -8
  19. package/build/__tests__/impl/LslStreamInlet.test.js +224 -69
  20. package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
  21. package/build/__tests__/impl/LslStreamOutlet.test.d.ts +3 -7
  22. package/build/__tests__/impl/LslStreamOutlet.test.js +40 -50
  23. package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
  24. package/build/__tests__/impl/LslWebSocketBridge.test.js +19 -20
  25. package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
  26. package/build/assertions.d.ts +1 -1
  27. package/build/assertions.js.map +1 -1
  28. package/build/impl/LslEventMarkerEmitter.d.ts +35 -0
  29. package/build/impl/LslEventMarkerEmitter.js +86 -0
  30. package/build/impl/LslEventMarkerEmitter.js.map +1 -0
  31. package/build/impl/LslStreamInfo.d.ts +4 -8
  32. package/build/impl/LslStreamInfo.js +8 -21
  33. package/build/impl/LslStreamInfo.js.map +1 -1
  34. package/build/impl/LslStreamInlet.d.ts +31 -50
  35. package/build/impl/LslStreamInlet.js +79 -149
  36. package/build/impl/LslStreamInlet.js.map +1 -1
  37. package/build/impl/LslStreamOutlet.d.ts +15 -18
  38. package/build/impl/LslStreamOutlet.js +39 -47
  39. package/build/impl/LslStreamOutlet.js.map +1 -1
  40. package/build/impl/LslWebSocketBridge.d.ts +4 -5
  41. package/build/impl/LslWebSocketBridge.js +10 -10
  42. package/build/impl/LslWebSocketBridge.js.map +1 -1
  43. package/build/impl/controllers/BleDeviceController.d.ts +58 -0
  44. package/build/impl/controllers/BleDeviceController.js +145 -0
  45. package/build/impl/controllers/BleDeviceController.js.map +1 -0
  46. package/build/impl/workers/inlet/LslStreamInlet.worker.d.ts +22 -0
  47. package/build/impl/workers/inlet/LslStreamInlet.worker.js +42 -0
  48. package/build/impl/workers/inlet/LslStreamInlet.worker.js.map +1 -0
  49. package/build/impl/workers/inlet/StreamInletWorker.d.ts +73 -0
  50. package/build/impl/workers/inlet/StreamInletWorker.js +253 -0
  51. package/build/impl/workers/inlet/StreamInletWorker.js.map +1 -0
  52. package/build/impl/workers/outlet/LslStreamOutlet.worker.d.ts +32 -0
  53. package/build/impl/workers/outlet/LslStreamOutlet.worker.js +82 -0
  54. package/build/impl/workers/outlet/LslStreamOutlet.worker.js.map +1 -0
  55. package/build/index.d.ts +14 -14
  56. package/build/index.js +16 -16
  57. package/build/index.js.map +1 -1
  58. package/build/scripts/discoverMuse.js +40 -0
  59. package/build/scripts/discoverMuse.js.map +1 -0
  60. package/build/scripts/finalizeHandles.d.ts +2 -0
  61. package/build/scripts/finalizeHandles.js +48 -0
  62. package/build/scripts/finalizeHandles.js.map +1 -0
  63. package/build/scripts/runLslEventMarkerEmitter.d.ts +1 -0
  64. package/build/scripts/runLslEventMarkerEmitter.js +11 -0
  65. package/build/scripts/runLslEventMarkerEmitter.js.map +1 -0
  66. package/build/scripts/runMuse.d.ts +1 -0
  67. package/build/scripts/runMuse.js +40 -0
  68. package/build/scripts/runMuse.js.map +1 -0
  69. package/build/scripts/runOutletToInlet.js +7 -6
  70. package/build/scripts/runOutletToInlet.js.map +1 -1
  71. package/build/scripts/testDestroyOutlet.d.ts +1 -0
  72. package/build/scripts/testDestroyOutlet.js +4 -0
  73. package/build/scripts/testDestroyOutlet.js.map +1 -0
  74. package/build/testDoubles/BleController/FakeBleController.d.ts +24 -0
  75. package/build/testDoubles/BleController/FakeBleController.js +42 -0
  76. package/build/testDoubles/BleController/FakeBleController.js.map +1 -0
  77. package/build/testDoubles/BleController/SpyBleController.d.ts +14 -0
  78. package/build/testDoubles/BleController/SpyBleController.js +48 -0
  79. package/build/testDoubles/BleController/SpyBleController.js.map +1 -0
  80. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.d.ts +17 -0
  81. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js +30 -0
  82. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js.map +1 -0
  83. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.d.ts +10 -0
  84. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js +23 -0
  85. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js.map +1 -0
  86. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.d.ts +9 -0
  87. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js +19 -0
  88. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js.map +1 -0
  89. package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +3 -3
  90. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +3 -3
  91. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  92. package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +8 -10
  93. package/build/testDoubles/StreamInlet/FakeStreamInlet.js +6 -7
  94. package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
  95. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -5
  96. package/build/testDoubles/StreamInlet/SpyStreamInlet.js +2 -8
  97. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  98. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +7 -9
  99. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +8 -8
  100. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
  101. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.d.ts +1 -1
  102. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js +1 -1
  103. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js.map +1 -1
  104. package/build/testDoubles/WorkerThreads/FakeWorker.d.ts +27 -0
  105. package/build/testDoubles/WorkerThreads/FakeWorker.js +67 -0
  106. package/build/testDoubles/WorkerThreads/FakeWorker.js.map +1 -0
  107. package/build/testDoubles/generateRandomOutletOptions.js +1 -1
  108. package/build/testDoubles/generateRandomOutletOptions.js.map +1 -1
  109. package/build/validations.js +0 -2
  110. package/build/validations.js.map +1 -1
  111. package/eslint.config.js +3 -0
  112. package/package.json +27 -49
  113. package/prettier.config.js +3 -0
  114. package/src/__tests__/AbstractPackageTest.ts +38 -21
  115. package/src/__tests__/impl/BleDeviceController.test.ts +492 -0
  116. package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +323 -0
  117. package/src/__tests__/impl/LslStreamInfo.test.ts +3 -40
  118. package/src/__tests__/impl/LslStreamInlet.test.ts +344 -97
  119. package/src/__tests__/impl/LslStreamOutlet.test.ts +65 -73
  120. package/src/__tests__/impl/LslWebSocketBridge.test.ts +22 -21
  121. package/src/assertions.ts +2 -1
  122. package/src/impl/LslEventMarkerEmitter.ts +133 -0
  123. package/src/impl/LslStreamInfo.ts +17 -29
  124. package/src/impl/LslStreamInlet.ts +102 -215
  125. package/src/impl/LslStreamOutlet.ts +54 -78
  126. package/src/impl/LslWebSocketBridge.ts +14 -14
  127. package/src/impl/controllers/BleDeviceController.ts +223 -0
  128. package/src/impl/workers/inlet/LslStreamInlet.worker.ts +74 -0
  129. package/src/impl/workers/inlet/StreamInletWorker.ts +348 -0
  130. package/src/impl/workers/outlet/LslStreamOutlet.worker.ts +144 -0
  131. package/src/index.ts +16 -16
  132. package/src/scripts/discoverMuse.ts +50 -0
  133. package/src/scripts/finalizeHandles.ts +63 -0
  134. package/src/scripts/runLslEventMarkerEmitter.ts +15 -0
  135. package/src/scripts/runMuse.ts +50 -0
  136. package/src/scripts/runOutletToInlet.ts +10 -8
  137. package/src/scripts/testDestroyOutlet.ts +5 -0
  138. package/src/testDoubles/BleController/FakeBleController.ts +70 -0
  139. package/src/testDoubles/BleController/SpyBleController.ts +61 -0
  140. package/src/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.ts +46 -0
  141. package/src/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.ts +31 -0
  142. package/src/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.ts +29 -0
  143. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +4 -4
  144. package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +11 -17
  145. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +3 -16
  146. package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +15 -15
  147. package/src/testDoubles/WebSocketBridge/FakeWebSocketBridge.ts +1 -1
  148. package/src/testDoubles/WorkerThreads/FakeWorker.ts +99 -0
  149. package/src/testDoubles/generateRandomOutletOptions.ts +2 -1
  150. package/src/validations.ts +0 -2
  151. package/tsconfig.json +23 -27
  152. package/build/.spruce/settings.json +0 -11
  153. package/build/__tests__/impl/LiblslAdapter.test.d.ts +0 -79
  154. package/build/__tests__/impl/LiblslAdapter.test.js +0 -688
  155. package/build/__tests__/impl/LiblslAdapter.test.js.map +0 -1
  156. package/build/__tests__/impl/LslEventMarkerOutlet.test.d.ts +0 -21
  157. package/build/__tests__/impl/LslEventMarkerOutlet.test.js +0 -160
  158. package/build/__tests__/impl/LslEventMarkerOutlet.test.js.map +0 -1
  159. package/build/handleError.d.ts +0 -8
  160. package/build/handleError.js +0 -25
  161. package/build/handleError.js.map +0 -1
  162. package/build/impl/LiblslAdapter.d.ts +0 -154
  163. package/build/impl/LiblslAdapter.js +0 -317
  164. package/build/impl/LiblslAdapter.js.map +0 -1
  165. package/build/impl/LslEventMarkerOutlet.d.ts +0 -28
  166. package/build/impl/LslEventMarkerOutlet.js +0 -63
  167. package/build/impl/LslEventMarkerOutlet.js.map +0 -1
  168. package/build/scripts/runLslEventMarkerOutlet.js +0 -18
  169. package/build/scripts/runLslEventMarkerOutlet.js.map +0 -1
  170. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.d.ts +0 -13
  171. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js +0 -26
  172. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js.map +0 -1
  173. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.d.ts +0 -17
  174. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js +0 -46
  175. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js.map +0 -1
  176. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.d.ts +0 -9
  177. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js +0 -19
  178. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js.map +0 -1
  179. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.d.ts +0 -9
  180. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js +0 -19
  181. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js.map +0 -1
  182. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +0 -50
  183. package/build/testDoubles/Liblsl/FakeLiblsl.js +0 -126
  184. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +0 -1
  185. package/eslint.config.mjs +0 -3
  186. package/src/.spruce/settings.json +0 -11
  187. package/src/__tests__/impl/LiblslAdapter.test.ts +0 -817
  188. package/src/__tests__/impl/LslEventMarkerOutlet.test.ts +0 -195
  189. package/src/handleError.ts +0 -24
  190. package/src/impl/LiblslAdapter.ts +0 -543
  191. package/src/impl/LslEventMarkerOutlet.ts +0 -97
  192. package/src/scripts/runLslEventMarkerOutlet.ts +0 -23
  193. package/src/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.ts +0 -36
  194. package/src/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.ts +0 -75
  195. package/src/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.ts +0 -24
  196. package/src/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.ts +0 -28
  197. package/src/testDoubles/Liblsl/FakeLiblsl.ts +0 -182
  198. /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?.outlet,
110
- this.fakeLiblsl.outlet
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
- info: this.fakeLiblsl.streamInfo,
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?.timestamp
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?.outlet,
135
- this.fakeLiblsl.outlet
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?.timestamp
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?.timestamp
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?.timestamp
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.lastDestroyStreamInfoOptions,
203
- { info: this.fakeLiblsl.streamInfo },
204
- 'Did not call destroyStreamInfo on destroy!'
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(info: StreamInfo, options: StreamOutletOptions) {
390
- super(info, options)
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 info = call?.info
40
- const chunkSize = call?.options?.chunkSize
40
+ const sourceId = call.options!.sourceId
41
+ const chunkSize = call.options!.chunkSize
41
42
 
42
43
  assert.isEqualDeep(
43
44
  {
44
- info,
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.doesThrow(() => {
154
- this.activate()
155
- }, `\n\n Cannot activate bridge after destroying it! \n\n Please create and activate a new instance. \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\n Cannot deactivate bridge after destroying it! \n\n Please create and activate a new instance. \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
- info: new FakeStreamInfo(),
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
@@ -1,4 +1,5 @@
1
- import { ChannelFormat } from 'impl/LiblslAdapter.js'
1
+ import { ChannelFormat } from '@neurodevs/ndx-native'
2
+
2
3
  import { CHANNEL_FORMATS } from './consts.js'
3
4
  import {
4
5
  isGreaterThanOrEqualToZero,
@@ -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
- import { CHANNEL_FORMATS_MAP } from '../consts.js'
4
- import { BoundStreamInfo, ChannelFormat } from './LiblslAdapter.js'
5
- import LiblslAdapter from './LiblslAdapter.js'
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
- public readonly boundInfo: BoundStreamInfo
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
- if (!boundInfo) {
46
- this.boundInfo = this.createStreamInfo()
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
- const instance = new (this.Class ?? this)(options)
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
- info: this.boundInfo,
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({ info: this.boundInfo })
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 boundInfo: BoundStreamInfo
94
+ readonly infoHandle: InfoHandle
108
95
  }
109
96
 
110
- type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
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
  }