@neurodevs/node-lsl 16.1.0 → 17.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 (61) hide show
  1. package/build/__tests__/AbstractPackageTest.d.ts +3 -1
  2. package/build/__tests__/AbstractPackageTest.js +3 -7
  3. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  4. package/build/__tests__/impl/LslStreamInfo.test.d.ts +3 -2
  5. package/build/__tests__/impl/LslStreamInfo.test.js +19 -12
  6. package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
  7. package/build/__tests__/impl/LslStreamInlet.test.d.ts +1 -3
  8. package/build/__tests__/impl/LslStreamInlet.test.js +6 -27
  9. package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
  10. package/build/__tests__/impl/LslWebSocketBridge.test.js +10 -16
  11. package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
  12. package/build/impl/LslStreamInfo.d.ts +5 -8
  13. package/build/impl/LslStreamInfo.js +13 -13
  14. package/build/impl/LslStreamInfo.js.map +1 -1
  15. package/build/impl/LslStreamInlet.d.ts +6 -17
  16. package/build/impl/LslStreamInlet.js +5 -21
  17. package/build/impl/LslStreamInlet.js.map +1 -1
  18. package/build/impl/LslStreamOutlet.d.ts +3 -3
  19. package/build/impl/LslStreamOutlet.js +5 -8
  20. package/build/impl/LslStreamOutlet.js.map +1 -1
  21. package/build/impl/LslWebSocketBridge.d.ts +3 -8
  22. package/build/impl/LslWebSocketBridge.js +3 -3
  23. package/build/impl/LslWebSocketBridge.js.map +1 -1
  24. package/build/scripts/runLslStreamOutlet.js +15 -10
  25. package/build/scripts/runLslStreamOutlet.js.map +1 -1
  26. package/build/scripts/runOutletToInlet.js +2 -7
  27. package/build/scripts/runOutletToInlet.js.map +1 -1
  28. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +1 -1
  29. package/build/testDoubles/Liblsl/FakeLiblsl.js +2 -2
  30. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -1
  31. package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +4 -4
  32. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +4 -4
  33. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  34. package/build/testDoubles/StreamInfo/SpyStreamInfo.d.ts +0 -1
  35. package/build/testDoubles/StreamInfo/SpyStreamInfo.js +0 -3
  36. package/build/testDoubles/StreamInfo/SpyStreamInfo.js.map +1 -1
  37. package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +3 -3
  38. package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
  39. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -3
  40. package/build/testDoubles/StreamInlet/SpyStreamInlet.js +0 -3
  41. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  42. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +1 -0
  43. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +3 -0
  44. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
  45. package/package.json +11 -11
  46. package/src/__tests__/AbstractPackageTest.ts +3 -7
  47. package/src/__tests__/impl/LslStreamInfo.test.ts +22 -11
  48. package/src/__tests__/impl/LslStreamInlet.test.ts +9 -34
  49. package/src/__tests__/impl/LslWebSocketBridge.test.ts +11 -25
  50. package/src/impl/LslStreamInfo.ts +24 -23
  51. package/src/impl/LslStreamInlet.ts +15 -46
  52. package/src/impl/LslStreamOutlet.ts +6 -9
  53. package/src/impl/LslWebSocketBridge.ts +6 -12
  54. package/src/scripts/runLslStreamOutlet.ts +16 -10
  55. package/src/scripts/runOutletToInlet.ts +7 -9
  56. package/src/testDoubles/Liblsl/FakeLiblsl.ts +2 -2
  57. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +6 -6
  58. package/src/testDoubles/StreamInfo/SpyStreamInfo.ts +0 -4
  59. package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +6 -3
  60. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +2 -6
  61. package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurodevs/node-lsl",
3
- "version": "16.1.0",
3
+ "version": "17.1.0",
4
4
  "description": "Lab Streaming Layer (LSL) bindings for Node",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -45,8 +45,8 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@neurodevs/generate-id": "^1.0.9",
48
- "ffi-rs": "^1.3.0",
49
- "node-gyp": "^10.2.0",
48
+ "ffi-rs": "^1.3.1",
49
+ "node-gyp": "^12.1.0",
50
50
  "ref-array-napi": "^1.2.2",
51
51
  "ref-napi": "^3.0.3",
52
52
  "ws": "^8.18.3"
@@ -54,18 +54,18 @@
54
54
  "devDependencies": {
55
55
  "@neurodevs/node-tdd": "^0.2.2",
56
56
  "@types/ffi-napi": "^4.0.10",
57
- "@types/node": "^22.9.0",
57
+ "@types/node": "^24.10.1",
58
58
  "@types/ws": "^8.18.1",
59
59
  "chokidar-cli": "^3.0.0",
60
- "concurrently": "^9.1.0",
61
- "eslint": "^9.15.0",
60
+ "concurrently": "^9.2.1",
61
+ "eslint": "^9.39.1",
62
62
  "eslint-config-spruce": "^11.2.26",
63
- "jest": "^29.7.0",
64
- "jest-circus": "^29.7.0",
65
- "prettier": "^3.3.3",
63
+ "jest": "^30.2.0",
64
+ "jest-circus": "^30.2.0",
65
+ "prettier": "^3.7.4",
66
66
  "ts-node": "^10.9.2",
67
- "tsc-watch": "^6.2.1",
68
- "typescript": "^5.6.3"
67
+ "tsc-watch": "^7.2.0",
68
+ "typescript": "^5.9.3"
69
69
  },
70
70
  "jest": {
71
71
  "testRunner": "jest-circus/runner",
@@ -78,6 +78,7 @@ export default class AbstractPackageTest extends AbstractModuleTest {
78
78
  FakeWebSocketServer.resetTestDouble()
79
79
  }
80
80
 
81
+ protected static readonly info = new FakeStreamInfo()
81
82
  protected static readonly name_ = this.generateId()
82
83
  protected static readonly type = this.generateId()
83
84
  protected static readonly sourceId = this.generateId()
@@ -97,19 +98,14 @@ export default class AbstractPackageTest extends AbstractModuleTest {
97
98
  protected static readonly sampleRateHz = Math.floor(Math.random() * 100)
98
99
  protected static readonly maxBufferedMs = Math.floor(Math.random() * 1000)
99
100
 
100
- protected static LslStreamInlet(
101
+ protected static async LslStreamInlet(
101
102
  options: Partial<StreamInletOptions>,
102
103
  onData: OnDataCallback
103
104
  ) {
104
105
  const defaultOptions = {
105
- sampleRateHz: 0,
106
- channelNames: this.channelNames,
107
- channelFormat: 'float32',
106
+ info: this.info,
108
107
  chunkSize: this.chunkSize,
109
108
  maxBufferedMs: this.maxBufferedMs,
110
- name: this.name_,
111
- type: this.type,
112
- sourceId: this.sourceId,
113
109
  ...options,
114
110
  } as StreamInletOptions
115
111
 
@@ -196,7 +196,7 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
196
196
  assert.isEqualDeep(
197
197
  this.fakeLiblsl.lastAppendChannelsToStreamInfoOptions,
198
198
  {
199
- info: this.instance.getBoundInfo(),
199
+ info: this.instance.boundInfo,
200
200
  channels: this.channelNames.map((label: string) => ({
201
201
  label,
202
202
  units: this.units,
@@ -213,15 +213,15 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
213
213
 
214
214
  assert.isEqualDeep(
215
215
  this.fakeLiblsl.lastDestroyStreamInfoOptions?.info,
216
- this.instance.getBoundInfo(),
216
+ this.instance.boundInfo,
217
217
  'Should have called destroyStreamInfo!'
218
218
  )
219
219
  }
220
220
 
221
221
  @test()
222
- protected static async doesNotCreateNewStreamInfoIfPassedBoundStreamInfo() {
222
+ protected static async doesNotCreateNewStreamInfoIfPassedBoundInfo() {
223
223
  this.LslStreamInfo({
224
- boundStreamInfo: this.instance.getBoundInfo(),
224
+ boundInfo: this.instance.boundInfo,
225
225
  })
226
226
 
227
227
  assert.isEqual(
@@ -232,22 +232,33 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
232
232
  }
233
233
 
234
234
  @test()
235
- protected static async setsBoundStreamInfoWhenPassed() {
235
+ protected static async setsBoundInfoWhenPassed() {
236
236
  const instance = this.LslStreamInfo({
237
- boundStreamInfo: this.instance.getBoundInfo(),
237
+ boundInfo: this.instance.boundInfo,
238
238
  })
239
239
 
240
240
  assert.isEqualDeep(
241
- instance.getBoundInfo(),
242
- this.instance.getBoundInfo(),
241
+ instance.boundInfo,
242
+ this.instance.boundInfo,
243
+ 'Should have set bound stream info!'
244
+ )
245
+ }
246
+
247
+ @test()
248
+ protected static async cachesInfoAttributesByBoundInfo() {
249
+ const cached = LslStreamInfo.From(this.instance.boundInfo)
250
+
251
+ assert.isEqualDeep(
252
+ cached,
253
+ this.instance,
243
254
  'Should have set bound stream info!'
244
255
  )
245
256
  }
246
257
 
247
258
  private static readonly defaultOptions = {
248
- name: this.generateId(),
249
- type: this.generateId(),
250
- sourceId: this.generateId(),
259
+ name: this.name,
260
+ type: this.type,
261
+ sourceId: this.sourceId,
251
262
  channelNames: this.channelNames,
252
263
  channelFormat: 'float32',
253
264
  sampleRateHz: 100 * Math.random(),
@@ -1,6 +1,6 @@
1
1
  import { test, assert } from '@neurodevs/node-tdd'
2
+
2
3
  import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
3
- import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
4
4
  import { SpyStreamInlet } from '../../testDoubles/StreamInlet/SpyStreamInlet.js'
5
5
  import AbstractPackageTest from '../AbstractPackageTest.js'
6
6
 
@@ -21,7 +21,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
21
21
 
22
22
  this.callsToOnData = []
23
23
 
24
- this.instance = this.LslStreamInlet()
24
+ this.instance = await this.LslStreamInlet()
25
25
  }
26
26
 
27
27
  @test()
@@ -29,33 +29,6 @@ export default class LslStreamInletTest extends AbstractPackageTest {
29
29
  assert.isTruthy(this.instance, 'Failed to create instance!')
30
30
  }
31
31
 
32
- @test()
33
- protected static async createsStreamInfoWithExpectedOptions() {
34
- assert.isEqualDeep(
35
- FakeStreamInfo.callsToConstructor[0],
36
- {
37
- channelNames: this.channelNames,
38
- channelFormat: 'float32',
39
- sampleRateHz: 0,
40
- name: this.name_,
41
- type: this.type,
42
- sourceId: this.sourceId,
43
- },
44
- 'Stream info should have expected options!'
45
- )
46
- }
47
-
48
- @test()
49
- protected static async uniqueNameHasSetPrefix() {
50
- const instance = this.LslStreamInlet({ name: undefined })
51
-
52
- assert.doesInclude(
53
- instance.getName(),
54
- 'lsl-inlet-',
55
- 'Name should have set prefix!'
56
- )
57
- }
58
-
59
32
  @test()
60
33
  protected static async callsBindingsToCreateStreamInlet() {
61
34
  const fakeInfo = this.instance.getStreamInfo()
@@ -65,7 +38,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
65
38
  assert.isEqualDeep(
66
39
  this.fakeLiblsl.lastCreateInletOptions,
67
40
  {
68
- info: fakeInfo.boundStreamInfo,
41
+ info: fakeInfo.boundInfo,
69
42
  maxBufferedMs: this.maxBufferedMs,
70
43
  },
71
44
  'Should have called createInlet!'
@@ -282,7 +255,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
282
255
  private static async runInletWithOptions(
283
256
  options?: Partial<StreamInletOptions>
284
257
  ) {
285
- const inlet = this.LslStreamInlet(options)
258
+ const inlet = await this.LslStreamInlet(options)
286
259
  await this.startThenStop(inlet)
287
260
  return inlet
288
261
  }
@@ -317,12 +290,14 @@ export default class LslStreamInletTest extends AbstractPackageTest {
317
290
  this.callsToOnData.push({ samples, timestamps })
318
291
  }
319
292
 
320
- protected static LslStreamInlet(options?: Partial<StreamInletOptions>) {
321
- return AbstractPackageTest.LslStreamInlet(
293
+ protected static async LslStreamInlet(
294
+ options?: Partial<StreamInletOptions>
295
+ ) {
296
+ return (await AbstractPackageTest.LslStreamInlet(
322
297
  {
323
298
  ...options,
324
299
  },
325
300
  this.onData
326
- ) as SpyStreamInlet
301
+ )) as SpyStreamInlet
327
302
  }
328
303
  }
@@ -1,10 +1,10 @@
1
1
  import { randomInt } from 'crypto'
2
-
3
2
  import { test, assert } from '@neurodevs/node-tdd'
4
3
 
5
4
  import LslWebSocketBridge, {
6
5
  WebSocketBridgeOptions,
7
6
  } 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'
@@ -23,7 +23,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
23
23
  this.setFakeWebSocketServer()
24
24
  this.setSpyLslWebSocketBridge()
25
25
 
26
- this.instance = this.LslWebSocketBridge() as SpyLslWebSocketBridge
26
+ this.instance =
27
+ (await this.LslWebSocketBridge()) as SpyLslWebSocketBridge
27
28
  }
28
29
 
29
30
  @test()
@@ -33,25 +34,15 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
33
34
 
34
35
  @test()
35
36
  protected static async createsLslStreamInlet() {
36
- const {
37
- channelNames,
38
- channelFormat,
39
- sampleRateHz,
40
- chunkSize,
41
- name,
42
- type,
43
- sourceId,
44
- } = FakeStreamInlet.callsToConstructor[0]?.options ?? {}
37
+ const call = FakeStreamInlet.callsToConstructor[0]
38
+
39
+ const info = call?.info
40
+ const chunkSize = call?.options?.chunkSize
45
41
 
46
42
  assert.isEqualDeep(
47
43
  {
48
- channelNames,
49
- channelFormat,
50
- sampleRateHz,
44
+ info,
51
45
  chunkSize,
52
- name,
53
- type,
54
- sourceId,
55
46
  },
56
47
  this.inletOptions
57
48
  )
@@ -254,8 +245,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
254
245
 
255
246
  @test()
256
247
  protected static async throwsIfNeitherPortNorRemoteUrlsPassed() {
257
- assert.doesThrow(() => {
258
- this.LslWebSocketBridge({
248
+ await assert.doesThrowAsync(async () => {
249
+ await this.LslWebSocketBridge({
259
250
  listenPort: undefined,
260
251
  connectUrls: undefined,
261
252
  })
@@ -287,13 +278,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
287
278
  private static readonly connectUrls = [this.generateId(), this.generateId()]
288
279
 
289
280
  private static readonly inletOptions = {
290
- channelNames: this.channelNames,
291
- channelFormat: 'float32' as const,
292
- sampleRateHz: 100 * Math.random(),
281
+ info: new FakeStreamInfo(),
293
282
  chunkSize: randomInt(1, 100),
294
- name: this.generateId(),
295
- type: this.generateId(),
296
- sourceId: this.generateId(),
297
283
  }
298
284
 
299
285
  private static readonly baseOptions = {
@@ -7,6 +7,8 @@ import LiblslAdapter from './LiblslAdapter.js'
7
7
  export default class LslStreamInfo implements StreamInfo {
8
8
  public static Class?: StreamInfoConstructor
9
9
 
10
+ private static instanceCache = new Map<BoundStreamInfo, StreamInfo>()
11
+
10
12
  public readonly name: string
11
13
  public readonly type: string
12
14
  public readonly sourceId: string
@@ -15,26 +17,20 @@ export default class LslStreamInfo implements StreamInfo {
15
17
  public readonly channelFormat: ChannelFormat
16
18
  public readonly sampleRateHz: number
17
19
  public readonly units: string
18
-
19
- protected boundInfo!: BoundStreamInfo
20
-
21
- private readonly defaultName = `lsl-stream-info-${generateId()}`
22
- private readonly defaultType = generateId()
23
- private readonly defaultSourceId = generateId()
24
- private readonly defaultUnits = 'N/A'
20
+ public readonly boundInfo: BoundStreamInfo
25
21
 
26
22
  private lsl = LiblslAdapter.getInstance()
27
23
 
28
24
  protected constructor(options: StreamInfoOptions) {
29
25
  const {
30
- name = this.defaultName,
31
- type = this.defaultType,
32
- sourceId = this.defaultSourceId,
26
+ name = `lsl-stream-info-${generateId()}`,
27
+ type = generateId(),
28
+ sourceId = generateId(),
33
29
  channelNames,
34
30
  channelFormat,
35
31
  sampleRateHz,
36
- units = this.defaultUnits,
37
- boundStreamInfo,
32
+ units = 'N/A',
33
+ boundInfo,
38
34
  } = options
39
35
 
40
36
  this.name = name
@@ -46,20 +42,29 @@ export default class LslStreamInfo implements StreamInfo {
46
42
  this.sampleRateHz = sampleRateHz
47
43
  this.units = units
48
44
 
49
- if (!boundStreamInfo) {
50
- this.createStreamInfo()
45
+ if (!boundInfo) {
46
+ this.boundInfo = this.createStreamInfo()
51
47
  this.appendChannelsToStreamInfo()
52
48
  } else {
53
- this.boundInfo = boundStreamInfo
49
+ this.boundInfo = boundInfo
54
50
  }
55
51
  }
56
52
 
57
53
  public static Create(options: StreamInfoOptions) {
58
- return new (this.Class ?? this)(options)
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)!
59
64
  }
60
65
 
61
66
  private createStreamInfo() {
62
- this.boundInfo = this.lsl.createStreamInfo({
67
+ return this.lsl.createStreamInfo({
63
68
  name: this.name,
64
69
  type: this.type,
65
70
  sourceId: this.sourceId,
@@ -87,10 +92,6 @@ export default class LslStreamInfo implements StreamInfo {
87
92
  public destroy() {
88
93
  this.lsl.destroyStreamInfo({ info: this.boundInfo })
89
94
  }
90
-
91
- public get boundStreamInfo() {
92
- return this.boundInfo
93
- }
94
95
  }
95
96
 
96
97
  export interface StreamInfo {
@@ -103,7 +104,7 @@ export interface StreamInfo {
103
104
  readonly channelCount: number
104
105
  readonly channelFormat: ChannelFormat
105
106
  readonly sampleRateHz: number
106
- readonly boundStreamInfo: BoundStreamInfo
107
+ readonly boundInfo: BoundStreamInfo
107
108
  }
108
109
 
109
110
  type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
@@ -116,5 +117,5 @@ export interface StreamInfoOptions {
116
117
  type?: string
117
118
  sourceId?: string
118
119
  units?: string
119
- boundStreamInfo?: BoundStreamInfo
120
+ boundInfo?: BoundStreamInfo
120
121
  }
@@ -1,13 +1,9 @@
1
- import generateId from '@neurodevs/generate-id'
2
1
  import { createPointer, DataType, JsExternal, unwrapPointer } from 'ffi-rs'
3
2
 
4
3
  import handleError from '../handleError.js'
5
- import { BoundInlet, ChannelFormat } from './LiblslAdapter.js'
4
+ import { BoundInlet } from './LiblslAdapter.js'
6
5
  import LiblslAdapter from './LiblslAdapter.js'
7
- import LslStreamInfo, {
8
- StreamInfo,
9
- StreamInfoOptions,
10
- } from './LslStreamInfo.js'
6
+ import { StreamInfo } from './LslStreamInfo.js'
11
7
 
12
8
  export default class LslStreamInlet implements StreamInlet {
13
9
  public static Class?: StreamInletConstructor
@@ -15,13 +11,10 @@ export default class LslStreamInlet implements StreamInlet {
15
11
  public isRunning = false
16
12
 
17
13
  protected info: StreamInfo
18
- protected name: string
19
- private channelNames: string[]
20
14
  private channelCount: number
21
15
  private chunkSize: number
22
16
  private maxBufferedMs: number
23
17
  private timeoutMs: number
24
-
25
18
  private onData: OnDataCallback
26
19
 
27
20
  protected inlet!: BoundInlet
@@ -40,28 +33,19 @@ export default class LslStreamInlet implements StreamInlet {
40
33
  private errorCodeBuffer!: Buffer<ArrayBuffer>
41
34
  private errorCodeBufferPtr!: JsExternal
42
35
 
43
- private readonly defaultName = `lsl-inlet-${generateId()}`
44
36
  private readonly sixMinutesInMs = 360 * 1000
45
37
 
46
38
  private lsl = LiblslAdapter.getInstance()
47
39
 
48
40
  protected constructor(
49
41
  info: StreamInfo,
50
- options: StreamInletOptions,
42
+ options: StreamInletConstructorOptions,
51
43
  onData: OnDataCallback
52
44
  ) {
53
- const {
54
- name = this.defaultName,
55
- channelNames,
56
- chunkSize,
57
- maxBufferedMs,
58
- timeoutMs,
59
- } = options ?? {}
45
+ const { chunkSize, maxBufferedMs, timeoutMs } = options ?? {}
60
46
 
61
47
  this.info = info
62
- this.name = name
63
- this.channelNames = channelNames
64
- this.channelCount = this.channelNames.length
48
+ this.channelCount = this.info.channelCount
65
49
  this.chunkSize = chunkSize
66
50
  this.maxBufferedMs = maxBufferedMs ?? this.sixMinutesInMs
67
51
  this.timeoutMs = timeoutMs ?? 0
@@ -71,13 +55,11 @@ export default class LslStreamInlet implements StreamInlet {
71
55
  this.createBoundInlet()
72
56
  }
73
57
 
74
- public static Create(options: StreamInletOptions, onData: OnDataCallback) {
75
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
- const { maxBufferedMs, chunkSize, ...rest } = options
77
- const infoOptions: StreamInfoOptions = rest
78
-
79
- const info = this.LslStreamInfo(infoOptions)
80
-
58
+ public static async Create(
59
+ options: StreamInletOptions,
60
+ onData: OnDataCallback
61
+ ) {
62
+ const { info } = options
81
63
  return new (this.Class ?? this)(info, options, onData)
82
64
  }
83
65
 
@@ -91,15 +73,11 @@ export default class LslStreamInlet implements StreamInlet {
91
73
 
92
74
  private createBoundInlet() {
93
75
  this.inlet = this.lsl.createInlet({
94
- info: this.boundStreamInfo,
76
+ info: this.info.boundInfo,
95
77
  maxBufferedMs: this.maxBufferedMs,
96
78
  })
97
79
  }
98
80
 
99
- private get boundStreamInfo() {
100
- return this.info.boundStreamInfo
101
- }
102
-
103
81
  public startPulling() {
104
82
  this.isRunning = true
105
83
 
@@ -266,10 +244,6 @@ export default class LslStreamInlet implements StreamInlet {
266
244
  private destroyBoundInlet() {
267
245
  this.lsl.destroyInlet({ inlet: this.inlet })
268
246
  }
269
-
270
- private static LslStreamInfo(options: StreamInfoOptions) {
271
- return LslStreamInfo.Create(options)
272
- }
273
247
  }
274
248
 
275
249
  export interface StreamInlet {
@@ -282,24 +256,19 @@ export interface StreamInlet {
282
256
 
283
257
  export type StreamInletConstructor = new (
284
258
  info: StreamInfo,
285
- options: StreamInletOptions,
259
+ options: StreamInletConstructorOptions,
286
260
  onData: OnDataCallback
287
261
  ) => StreamInlet
288
262
 
289
263
  export interface StreamInletOptions {
290
- channelNames: string[]
291
- channelFormat: ChannelFormat
292
- sampleRateHz: number
264
+ info: StreamInfo
293
265
  chunkSize: number
294
266
  maxBufferedMs?: number
295
267
  timeoutMs?: number
296
- name?: string
297
- type?: string
298
- sourceId?: string
299
- manufacturer?: string
300
- units?: string
301
268
  }
302
269
 
270
+ export type StreamInletConstructorOptions = Omit<StreamInletOptions, 'info'>
271
+
303
272
  export type OnDataCallback = (
304
273
  samples: Float32Array,
305
274
  timestamps: Float64Array
@@ -21,6 +21,7 @@ import LslStreamInfo, {
21
21
  export default class LslStreamOutlet implements StreamOutlet {
22
22
  public static Class?: StreamOutletConstructor
23
23
 
24
+ public readonly info: StreamInfo
24
25
  public readonly name: string
25
26
  public readonly type: string
26
27
  public readonly sourceId: string
@@ -33,7 +34,6 @@ export default class LslStreamOutlet implements StreamOutlet {
33
34
  public readonly manufacturer: string = 'N/A'
34
35
  public readonly units: string = 'N/A'
35
36
 
36
- private info: StreamInfo
37
37
  private boundOutlet!: BoundOutlet
38
38
  private pushSampleMethod!: (options: unknown) => LslErrorCode
39
39
 
@@ -109,16 +109,12 @@ export default class LslStreamOutlet implements StreamOutlet {
109
109
 
110
110
  private createStreamOutlet() {
111
111
  this.boundOutlet = this.lsl.createOutlet({
112
- info: this.boundStreamInfo,
112
+ info: this.info.boundInfo,
113
113
  chunkSize: this.chunkSize,
114
114
  maxBufferedMs: this.maxBufferedMs,
115
115
  })
116
116
  }
117
117
 
118
- private get boundStreamInfo() {
119
- return this.info.boundStreamInfo
120
- }
121
-
122
118
  private setPushSampleMethod() {
123
119
  this.pushSampleMethod = (
124
120
  this.lsl[this.pushMethod] as (options: unknown) => LslErrorCode
@@ -146,12 +142,12 @@ export default class LslStreamOutlet implements StreamOutlet {
146
142
  }
147
143
 
148
144
  public destroy() {
149
- this.destroyBoundStreamInfo()
145
+ this.destroyBoundInfo()
150
146
  this.destroyBoundOutlet()
151
147
  }
152
148
 
153
- private destroyBoundStreamInfo() {
154
- this.lsl.destroyStreamInfo({ info: this.boundStreamInfo })
149
+ private destroyBoundInfo() {
150
+ this.lsl.destroyStreamInfo({ info: this.info.boundInfo })
155
151
  }
156
152
 
157
153
  private destroyBoundOutlet() {
@@ -190,6 +186,7 @@ export default class LslStreamOutlet implements StreamOutlet {
190
186
  export interface StreamOutlet {
191
187
  pushSample(sample: LslSample): void
192
188
  destroy(): void
189
+ readonly info: StreamInfo
193
190
  readonly name: string
194
191
  readonly type: string
195
192
  readonly sourceId: string
@@ -1,5 +1,5 @@
1
1
  import WebSocket, { WebSocketServer } from 'ws'
2
- import { ChannelFormat } from './LiblslAdapter.js'
2
+ import { StreamInfo } from './LslStreamInfo.js'
3
3
  import LslStreamInlet, {
4
4
  StreamInlet,
5
5
  StreamInletOptions,
@@ -26,14 +26,14 @@ export default class LslWebSocketBridge implements WebSocketBridge {
26
26
  this.throwIfNoLocalServerOrRemoteSockets()
27
27
  }
28
28
 
29
- public static Create(options: WebSocketBridgeOptions) {
29
+ public static async Create(options: WebSocketBridgeOptions) {
30
30
  const { listenPort, connectUrls, ...rest } = options
31
31
  const inletOptions: StreamInletOptions = rest
32
32
 
33
33
  const localServer = this.WebSocketServer(listenPort)
34
34
  const remoteSockets = this.createSocketsFrom(connectUrls)
35
35
 
36
- const inlet = this.LslStreamInlet(
36
+ const inlet = await this.LslStreamInlet(
37
37
  inletOptions,
38
38
  localServer,
39
39
  remoteSockets
@@ -154,7 +154,7 @@ export default class LslWebSocketBridge implements WebSocketBridge {
154
154
  return listenPort ? new this.WSS({ port: listenPort }) : undefined
155
155
  }
156
156
 
157
- private static LslStreamInlet(
157
+ private static async LslStreamInlet(
158
158
  options: StreamInletOptions,
159
159
  localServer?: WebSocketServer,
160
160
  remoteSockets?: WebSocket[]
@@ -175,19 +175,13 @@ export type WebSocketBridgeConstructor = new (
175
175
  ) => WebSocketBridge
176
176
 
177
177
  export interface WebSocketBridgeOptions {
178
- channelNames: string[]
179
- channelFormat: ChannelFormat
180
- sampleRateHz: number
178
+ info: StreamInfo
181
179
  chunkSize: number
182
- name?: string
183
- type?: string
184
- sourceId?: string
185
180
  listenPort?: number
186
181
  connectUrls?: string | string[]
187
182
  }
188
183
 
189
- export interface WebSocketBridgeConstructorOptions
190
- extends WebSocketBridgeOptions {
184
+ export interface WebSocketBridgeConstructorOptions extends WebSocketBridgeOptions {
191
185
  inlet: StreamInlet
192
186
  localServer?: WebSocketServer
193
187
  remoteSockets?: WebSocket[]