@neurodevs/node-lsl 16.0.3 → 17.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 (50) hide show
  1. package/build/__tests__/AbstractPackageTest.d.ts +1 -1
  2. package/build/__tests__/AbstractPackageTest.js +1 -3
  3. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  4. package/build/__tests__/impl/LiblslAdapter.test.d.ts +13 -0
  5. package/build/__tests__/impl/LiblslAdapter.test.js +87 -1
  6. package/build/__tests__/impl/LiblslAdapter.test.js.map +1 -1
  7. package/build/__tests__/impl/LslStreamInfo.test.d.ts +3 -1
  8. package/build/__tests__/impl/LslStreamInfo.test.js +26 -9
  9. package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
  10. package/build/__tests__/impl/LslStreamInlet.test.d.ts +3 -2
  11. package/build/__tests__/impl/LslStreamInlet.test.js +27 -21
  12. package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
  13. package/build/__tests__/impl/LslWebSocketBridge.test.js +8 -16
  14. package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
  15. package/build/impl/LiblslAdapter.d.ts +14 -4
  16. package/build/impl/LiblslAdapter.js +49 -5
  17. package/build/impl/LiblslAdapter.js.map +1 -1
  18. package/build/impl/LslStreamInfo.d.ts +4 -1
  19. package/build/impl/LslStreamInfo.js +18 -19
  20. package/build/impl/LslStreamInfo.js.map +1 -1
  21. package/build/impl/LslStreamInlet.d.ts +3 -13
  22. package/build/impl/LslStreamInlet.js +12 -15
  23. package/build/impl/LslStreamInlet.js.map +1 -1
  24. package/build/impl/LslWebSocketBridge.d.ts +2 -8
  25. package/build/impl/LslWebSocketBridge.js +2 -2
  26. package/build/impl/LslWebSocketBridge.js.map +1 -1
  27. package/build/scripts/runOutletToInlet.js +1 -1
  28. package/build/scripts/runOutletToInlet.js.map +1 -1
  29. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +8 -4
  30. package/build/testDoubles/Liblsl/FakeLiblsl.js +12 -4
  31. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -1
  32. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +1 -1
  33. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  34. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +0 -1
  35. package/build/testDoubles/StreamInlet/SpyStreamInlet.js +0 -3
  36. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/__tests__/AbstractPackageTest.ts +1 -5
  39. package/src/__tests__/impl/LiblslAdapter.test.ts +118 -2
  40. package/src/__tests__/impl/LslStreamInfo.test.ts +34 -9
  41. package/src/__tests__/impl/LslStreamInlet.test.ts +36 -26
  42. package/src/__tests__/impl/LslWebSocketBridge.test.ts +9 -24
  43. package/src/impl/LiblslAdapter.ts +84 -11
  44. package/src/impl/LslStreamInfo.ts +21 -28
  45. package/src/impl/LslStreamInlet.ts +20 -37
  46. package/src/impl/LslWebSocketBridge.ts +3 -9
  47. package/src/scripts/runOutletToInlet.ts +6 -3
  48. package/src/testDoubles/Liblsl/FakeLiblsl.ts +18 -6
  49. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +1 -1
  50. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +0 -4
@@ -7,7 +7,7 @@ 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: Record<string, StreamInfo> = {}
10
+ private static instanceCache = new Map<BoundStreamInfo, StreamInfo>()
11
11
 
12
12
  public readonly name: string
13
13
  public readonly type: string
@@ -36,6 +36,7 @@ export default class LslStreamInfo implements StreamInfo {
36
36
  channelFormat,
37
37
  sampleRateHz,
38
38
  units = this.defaultUnits,
39
+ boundStreamInfo,
39
40
  } = options
40
41
 
41
42
  this.name = name
@@ -47,40 +48,27 @@ export default class LslStreamInfo implements StreamInfo {
47
48
  this.sampleRateHz = sampleRateHz
48
49
  this.units = units
49
50
 
50
- this.createStreamInfo()
51
- this.appendChannelsToStreamInfo()
51
+ if (!boundStreamInfo) {
52
+ this.createStreamInfo()
53
+ this.appendChannelsToStreamInfo()
54
+ } else {
55
+ this.boundInfo = boundStreamInfo
56
+ }
52
57
  }
53
58
 
54
59
  public static Create(options: StreamInfoOptions) {
55
- const {
56
- name,
57
- type,
58
- sourceId,
59
- channelNames,
60
- channelFormat,
61
- sampleRateHz,
62
- } = options
63
-
64
- const channelCount = channelNames.length
65
-
66
- const key = JSON.stringify({
67
- name,
68
- type,
69
- sourceId,
70
- channelCount,
71
- channelFormat,
72
- sampleRateHz,
73
- })
60
+ const instance = new (this.Class ?? this)(options)
74
61
 
75
- let instance = this.instanceCache[key]
62
+ const { boundStreamInfo: handle } = instance
63
+ this.instanceCache.set(handle, instance)
76
64
 
77
- if (instance === undefined) {
78
- instance = new (this.Class ?? this)(options)
79
- this.instanceCache[key] = instance
80
- }
81
65
  return instance
82
66
  }
83
67
 
68
+ public static From(handle: BoundStreamInfo) {
69
+ return this.instanceCache.get(handle)!
70
+ }
71
+
84
72
  private createStreamInfo() {
85
73
  this.boundInfo = this.lsl.createStreamInfo({
86
74
  name: this.name,
@@ -114,6 +102,10 @@ export default class LslStreamInfo implements StreamInfo {
114
102
  public get boundStreamInfo() {
115
103
  return this.boundInfo
116
104
  }
105
+
106
+ public set boundStreamInfo(boundInfo: BoundStreamInfo) {
107
+ this.boundInfo = boundInfo
108
+ }
117
109
  }
118
110
 
119
111
  export interface StreamInfo {
@@ -126,7 +118,7 @@ export interface StreamInfo {
126
118
  readonly channelCount: number
127
119
  readonly channelFormat: ChannelFormat
128
120
  readonly sampleRateHz: number
129
- readonly boundStreamInfo: BoundStreamInfo
121
+ boundStreamInfo: BoundStreamInfo
130
122
  }
131
123
 
132
124
  type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
@@ -139,4 +131,5 @@ export interface StreamInfoOptions {
139
131
  type?: string
140
132
  sourceId?: string
141
133
  units?: string
134
+ boundStreamInfo?: BoundStreamInfo
142
135
  }
@@ -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 LslStreamInfo, { 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,7 +33,6 @@ 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()
@@ -50,18 +42,10 @@ export default class LslStreamInlet implements StreamInlet {
50
42
  options: StreamInletOptions,
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,12 @@ 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 { sourceId } = options
63
+ const info = await this.LslStreamInfo(sourceId)
81
64
  return new (this.Class ?? this)(info, options, onData)
82
65
  }
83
66
 
@@ -267,8 +250,15 @@ export default class LslStreamInlet implements StreamInlet {
267
250
  this.lsl.destroyInlet({ inlet: this.inlet })
268
251
  }
269
252
 
270
- private static LslStreamInfo(options: StreamInfoOptions) {
271
- return LslStreamInfo.Create(options)
253
+ private static async LslStreamInfo(sourceId: string) {
254
+ const lsl = LiblslAdapter.getInstance()
255
+
256
+ const handles = await lsl.resolveByProp({
257
+ prop: 'source_id',
258
+ value: sourceId,
259
+ })
260
+
261
+ return LslStreamInfo.From(handles[0])
272
262
  }
273
263
  }
274
264
 
@@ -287,17 +277,10 @@ export type StreamInletConstructor = new (
287
277
  ) => StreamInlet
288
278
 
289
279
  export interface StreamInletOptions {
290
- channelNames: string[]
291
- channelFormat: ChannelFormat
292
- sampleRateHz: number
280
+ sourceId: string
293
281
  chunkSize: number
294
282
  maxBufferedMs?: number
295
283
  timeoutMs?: number
296
- name?: string
297
- type?: string
298
- sourceId?: string
299
- manufacturer?: string
300
- units?: string
301
284
  }
302
285
 
303
286
  export type OnDataCallback = (
@@ -1,5 +1,4 @@
1
1
  import WebSocket, { WebSocketServer } from 'ws'
2
- import { ChannelFormat } from './LiblslAdapter.js'
3
2
  import LslStreamInlet, {
4
3
  StreamInlet,
5
4
  StreamInletOptions,
@@ -26,14 +25,14 @@ export default class LslWebSocketBridge implements WebSocketBridge {
26
25
  this.throwIfNoLocalServerOrRemoteSockets()
27
26
  }
28
27
 
29
- public static Create(options: WebSocketBridgeOptions) {
28
+ public static async Create(options: WebSocketBridgeOptions) {
30
29
  const { listenPort, connectUrls, ...rest } = options
31
30
  const inletOptions: StreamInletOptions = rest
32
31
 
33
32
  const localServer = this.WebSocketServer(listenPort)
34
33
  const remoteSockets = this.createSocketsFrom(connectUrls)
35
34
 
36
- const inlet = this.LslStreamInlet(
35
+ const inlet = await this.LslStreamInlet(
37
36
  inletOptions,
38
37
  localServer,
39
38
  remoteSockets
@@ -175,13 +174,8 @@ export type WebSocketBridgeConstructor = new (
175
174
  ) => WebSocketBridge
176
175
 
177
176
  export interface WebSocketBridgeOptions {
178
- channelNames: string[]
179
- channelFormat: ChannelFormat
180
- sampleRateHz: number
177
+ sourceId: string
181
178
  chunkSize: number
182
- name?: string
183
- type?: string
184
- sourceId?: string
185
179
  listenPort?: number
186
180
  connectUrls?: string | string[]
187
181
  }
@@ -26,9 +26,12 @@ const inletOptions = {
26
26
  chunkSize: 1,
27
27
  }
28
28
 
29
- const inlet = LslStreamInlet.Create(inletOptions, (samples, timestamps) => {
30
- console.log('Received samples:', samples, 'at timestamps:', timestamps)
31
- })
29
+ const inlet = await LslStreamInlet.Create(
30
+ inletOptions,
31
+ (samples, timestamps) => {
32
+ console.log('Received samples:', samples, 'at timestamps:', timestamps)
33
+ }
34
+ )
32
35
 
33
36
  console.log('Start pulling...')
34
37
  inlet.startPulling()
@@ -16,9 +16,13 @@ import {
16
16
  PullChunkOptions,
17
17
  PullSampleOptions,
18
18
  DestroyStreamInfoOptions,
19
+ ResolveByPropOptions,
20
+ DestroyOutletOptions,
19
21
  } from 'impl/LiblslAdapter.js'
20
22
 
21
23
  export default class FakeLiblsl implements Liblsl {
24
+ public static fakeStreamInfoHandles: BoundStreamInfo[] = []
25
+
22
26
  public static fakeSamples: Float32Array[] = [
23
27
  new Float32Array([1, 2, 3]),
24
28
  new Float32Array([4, 5, 6]),
@@ -46,12 +50,14 @@ export default class FakeLiblsl implements Liblsl {
46
50
  public streamInfo: BoundStreamInfo = {} as BoundStreamInfo
47
51
 
48
52
  public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
49
- public lastDestroyStreamInfoOptions?: DestroyStreamInfoOptions
50
53
  public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
54
+ public lastDestroyStreamInfoOptions?: DestroyStreamInfoOptions
55
+ public lastResolveByPropOptions?: ResolveByPropOptions
51
56
  public lastCreateOutletOptions?: CreateOutletOptions
52
57
  public lastPushSampleFloatTimestampOptions?: PushSampleFloatTimestampOptions
53
58
  public lastPushSampleStringTimestampOptions?: PushSampleStringTimestampOptions
54
59
  public lastCreateInletOptions?: CreateInletOptions
60
+ public lastDestroyOutletOptions?: DestroyOutletOptions
55
61
  public lastPullSampleOptions?: PullSampleOptions
56
62
  public lastPullChunkOptions?: PullChunkOptions
57
63
  public lastFlushInletOptions?: FlushInletOptions
@@ -72,16 +78,21 @@ export default class FakeLiblsl implements Liblsl {
72
78
  return this.streamInfo
73
79
  }
74
80
 
75
- public destroyStreamInfo(options: DestroyStreamInfoOptions) {
76
- this.lastDestroyStreamInfoOptions = options
77
- }
78
-
79
81
  public appendChannelsToStreamInfo(
80
82
  options: AppendChannelsToStreamInfoOptions
81
83
  ) {
82
84
  this.lastAppendChannelsToStreamInfoOptions = options
83
85
  }
84
86
 
87
+ public destroyStreamInfo(options: DestroyStreamInfoOptions) {
88
+ this.lastDestroyStreamInfoOptions = options
89
+ }
90
+
91
+ public async resolveByProp(options: ResolveByPropOptions) {
92
+ this.lastResolveByPropOptions = options
93
+ return FakeLiblsl.fakeStreamInfoHandles as bigint[]
94
+ }
95
+
85
96
  public pushSampleFloatTimestamp(options: PushSampleFloatTimestampOptions) {
86
97
  this.lastPushSampleFloatTimestampOptions = options
87
98
  return FakeLiblsl.fakeErrorCode
@@ -101,8 +112,9 @@ export default class FakeLiblsl implements Liblsl {
101
112
  return this.outlet
102
113
  }
103
114
 
104
- public destroyOutlet() {
115
+ public destroyOutlet(options: DestroyOutletOptions) {
105
116
  this.destroyOutletHitCount++
117
+ this.lastDestroyOutletOptions = options
106
118
  }
107
119
 
108
120
  public createInlet(options: CreateInletOptions) {
@@ -12,7 +12,7 @@ export default class FakeStreamInfo implements StreamInfo {
12
12
  public name = generateId()
13
13
  public type = generateId()
14
14
  public sourceId = generateId()
15
- public channelNames: string[] = [generateId(), generateId()]
15
+ public channelNames: string[] = [generateId(), generateId(), generateId()]
16
16
  public channelFormat = 'float32' as const
17
17
  public sampleRateHz = 0
18
18
  public units = 'N/A'
@@ -13,10 +13,6 @@ export class SpyStreamInlet extends LslStreamInlet {
13
13
  super(info, options, onData)
14
14
  }
15
15
 
16
- public getName() {
17
- return this.name
18
- }
19
-
20
16
  public getStreamInfo() {
21
17
  return this.info
22
18
  }