@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.
Files changed (178) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.regressionproof.json +7 -0
  3. package/.vscode/launch.json +3 -2
  4. package/README.md +9 -35
  5. package/build/.spruce/settings.json +5 -1
  6. package/build/__tests__/AbstractPackageTest.d.ts +4 -4
  7. package/build/__tests__/AbstractPackageTest.js +9 -11
  8. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  9. package/build/__tests__/impl/LslBackpressureMonitor.test.d.ts +7 -0
  10. package/build/__tests__/impl/LslBackpressureMonitor.test.js +26 -0
  11. package/build/__tests__/impl/LslBackpressureMonitor.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 +22 -8
  19. package/build/__tests__/impl/LslStreamInlet.test.js +240 -66
  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 +36 -46
  23. package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
  24. package/build/__tests__/impl/LslWebSocketBridge.test.js +16 -17
  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/LslBackpressureMonitor.d.ts +8 -0
  29. package/build/impl/LslBackpressureMonitor.js +8 -0
  30. package/build/impl/LslBackpressureMonitor.js.map +1 -0
  31. package/build/impl/LslEventMarkerEmitter.d.ts +35 -0
  32. package/build/impl/LslEventMarkerEmitter.js +86 -0
  33. package/build/impl/LslEventMarkerEmitter.js.map +1 -0
  34. package/build/impl/LslStreamInfo.d.ts +4 -8
  35. package/build/impl/LslStreamInfo.js +8 -21
  36. package/build/impl/LslStreamInfo.js.map +1 -1
  37. package/build/impl/LslStreamInlet.d.ts +31 -47
  38. package/build/impl/LslStreamInlet.js +82 -143
  39. package/build/impl/LslStreamInlet.js.map +1 -1
  40. package/build/impl/LslStreamOutlet.d.ts +15 -18
  41. package/build/impl/LslStreamOutlet.js +39 -47
  42. package/build/impl/LslStreamOutlet.js.map +1 -1
  43. package/build/impl/LslWebSocketBridge.d.ts +4 -5
  44. package/build/impl/LslWebSocketBridge.js +8 -8
  45. package/build/impl/LslWebSocketBridge.js.map +1 -1
  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 +12 -14
  56. package/build/index.js +14 -16
  57. package/build/index.js.map +1 -1
  58. package/build/scripts/finalizeHandles.d.ts +2 -0
  59. package/build/scripts/finalizeHandles.js +48 -0
  60. package/build/scripts/finalizeHandles.js.map +1 -0
  61. package/build/scripts/runLslEventMarkerEmitter.js +11 -0
  62. package/build/scripts/runLslEventMarkerEmitter.js.map +1 -0
  63. package/build/scripts/runOutletToInlet.js +7 -6
  64. package/build/scripts/runOutletToInlet.js.map +1 -1
  65. package/build/scripts/testDestroyOutlet.d.ts +1 -0
  66. package/build/scripts/testDestroyOutlet.js +4 -0
  67. package/build/scripts/testDestroyOutlet.js.map +1 -0
  68. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.d.ts +6 -0
  69. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js +10 -0
  70. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js.map +1 -0
  71. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.d.ts +17 -0
  72. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js +30 -0
  73. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js.map +1 -0
  74. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.d.ts +10 -0
  75. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js +23 -0
  76. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js.map +1 -0
  77. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.d.ts +9 -0
  78. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js +19 -0
  79. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js.map +1 -0
  80. package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +3 -3
  81. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +3 -3
  82. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  83. package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +8 -10
  84. package/build/testDoubles/StreamInlet/FakeStreamInlet.js +6 -7
  85. package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
  86. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -5
  87. package/build/testDoubles/StreamInlet/SpyStreamInlet.js +2 -8
  88. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  89. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +7 -9
  90. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +5 -8
  91. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
  92. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.d.ts +1 -1
  93. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js +1 -1
  94. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js.map +1 -1
  95. package/build/testDoubles/WorkerThreads/FakeWorker.d.ts +27 -0
  96. package/build/testDoubles/WorkerThreads/FakeWorker.js +67 -0
  97. package/build/testDoubles/WorkerThreads/FakeWorker.js.map +1 -0
  98. package/build/validations.js +0 -2
  99. package/build/validations.js.map +1 -1
  100. package/package.json +24 -8
  101. package/src/.spruce/settings.json +5 -1
  102. package/src/__tests__/AbstractPackageTest.ts +13 -15
  103. package/src/__tests__/impl/LslBackpressureMonitor.test.ts +25 -0
  104. package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +322 -0
  105. package/src/__tests__/impl/LslStreamInfo.test.ts +3 -40
  106. package/src/__tests__/impl/LslStreamInlet.test.ts +366 -95
  107. package/src/__tests__/impl/LslStreamOutlet.test.ts +61 -69
  108. package/src/__tests__/impl/LslWebSocketBridge.test.ts +18 -18
  109. package/src/assertions.ts +2 -1
  110. package/src/impl/LslBackpressureMonitor.ts +13 -0
  111. package/src/impl/LslEventMarkerEmitter.ts +133 -0
  112. package/src/impl/LslStreamInfo.ts +17 -29
  113. package/src/impl/LslStreamInlet.ts +105 -208
  114. package/src/impl/LslStreamOutlet.ts +54 -78
  115. package/src/impl/LslWebSocketBridge.ts +12 -12
  116. package/src/impl/workers/inlet/LslStreamInlet.worker.ts +74 -0
  117. package/src/impl/workers/inlet/StreamInletWorker.ts +348 -0
  118. package/src/impl/workers/outlet/LslStreamOutlet.worker.ts +144 -0
  119. package/src/index.ts +14 -17
  120. package/src/scripts/finalizeHandles.ts +63 -0
  121. package/src/scripts/runLslEventMarkerEmitter.ts +15 -0
  122. package/src/scripts/runOutletToInlet.ts +10 -8
  123. package/src/scripts/testDestroyOutlet.ts +5 -0
  124. package/src/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.ts +13 -0
  125. package/src/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.ts +46 -0
  126. package/src/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.ts +31 -0
  127. package/src/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.ts +29 -0
  128. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +4 -4
  129. package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +11 -17
  130. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +3 -16
  131. package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +12 -15
  132. package/src/testDoubles/WebSocketBridge/FakeWebSocketBridge.ts +1 -1
  133. package/src/testDoubles/WorkerThreads/FakeWorker.ts +99 -0
  134. package/src/validations.ts +0 -2
  135. package/build/__tests__/impl/LiblslAdapter.test.d.ts +0 -79
  136. package/build/__tests__/impl/LiblslAdapter.test.js +0 -688
  137. package/build/__tests__/impl/LiblslAdapter.test.js.map +0 -1
  138. package/build/__tests__/impl/LslEventMarkerOutlet.test.d.ts +0 -21
  139. package/build/__tests__/impl/LslEventMarkerOutlet.test.js +0 -160
  140. package/build/__tests__/impl/LslEventMarkerOutlet.test.js.map +0 -1
  141. package/build/handleError.d.ts +0 -8
  142. package/build/handleError.js +0 -25
  143. package/build/handleError.js.map +0 -1
  144. package/build/impl/LiblslAdapter.d.ts +0 -159
  145. package/build/impl/LiblslAdapter.js +0 -317
  146. package/build/impl/LiblslAdapter.js.map +0 -1
  147. package/build/impl/LslEventMarkerOutlet.d.ts +0 -28
  148. package/build/impl/LslEventMarkerOutlet.js +0 -63
  149. package/build/impl/LslEventMarkerOutlet.js.map +0 -1
  150. package/build/scripts/runLslEventMarkerOutlet.js +0 -18
  151. package/build/scripts/runLslEventMarkerOutlet.js.map +0 -1
  152. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.d.ts +0 -13
  153. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js +0 -26
  154. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js.map +0 -1
  155. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.d.ts +0 -17
  156. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js +0 -46
  157. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js.map +0 -1
  158. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.d.ts +0 -9
  159. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js +0 -19
  160. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js.map +0 -1
  161. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.d.ts +0 -9
  162. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js +0 -19
  163. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js.map +0 -1
  164. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +0 -50
  165. package/build/testDoubles/Liblsl/FakeLiblsl.js +0 -126
  166. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +0 -1
  167. package/src/__tests__/impl/LiblslAdapter.test.ts +0 -817
  168. package/src/__tests__/impl/LslEventMarkerOutlet.test.ts +0 -195
  169. package/src/handleError.ts +0 -24
  170. package/src/impl/LiblslAdapter.ts +0 -548
  171. package/src/impl/LslEventMarkerOutlet.ts +0 -97
  172. package/src/scripts/runLslEventMarkerOutlet.ts +0 -23
  173. package/src/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.ts +0 -36
  174. package/src/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.ts +0 -75
  175. package/src/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.ts +0 -24
  176. package/src/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.ts +0 -28
  177. package/src/testDoubles/Liblsl/FakeLiblsl.ts +0 -182
  178. /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?.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
  })
@@ -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?.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,
@@ -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 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(info: StreamInfo, options: StreamOutletOptions) {
390
- super(info, options)
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 info = call?.info
40
- const chunkSize = call?.options?.chunkSize
39
+ const sourceId = call.options!.sourceId
40
+ const chunkSize = call.options!.chunkSize
41
41
 
42
42
  assert.isEqualDeep(
43
43
  {
44
- info,
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.doesThrow(() => {
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
- info: new FakeStreamInfo(),
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
@@ -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,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
- 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
  }