@neurodevs/node-lsl 2.0.2 → 3.2.1

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 (43) hide show
  1. package/README.md +1 -1
  2. package/build/Liblsl.d.ts +33 -25
  3. package/build/Liblsl.js +29 -30
  4. package/build/Liblsl.js.map +1 -1
  5. package/build/LslOutlet.d.ts +8 -7
  6. package/build/LslOutlet.js +28 -21
  7. package/build/LslOutlet.js.map +1 -1
  8. package/build/TimeMarkerOutlet.d.ts +4 -0
  9. package/build/TimeMarkerOutlet.js +26 -0
  10. package/build/TimeMarkerOutlet.js.map +1 -0
  11. package/build/__tests__/behavioral/LslOutlet.test.d.ts +10 -12
  12. package/build/__tests__/behavioral/LslOutlet.test.js +47 -98
  13. package/build/__tests__/behavioral/LslOutlet.test.js.map +1 -1
  14. package/build/__tests__/behavioral/TimeMarkerOutlet.test.d.ts +8 -0
  15. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js +85 -0
  16. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js.map +1 -0
  17. package/build/__tests__/implementation/Liblsl.test.d.ts +9 -9
  18. package/build/__tests__/implementation/Liblsl.test.js +76 -66
  19. package/build/__tests__/implementation/Liblsl.test.js.map +1 -1
  20. package/build/__tests__/support/SpyLiblsl.d.ts +14 -0
  21. package/build/__tests__/support/SpyLiblsl.js +27 -0
  22. package/build/__tests__/support/SpyLiblsl.js.map +1 -0
  23. package/build/__tests__/support/consts.d.ts +2 -0
  24. package/build/__tests__/support/consts.js +14 -0
  25. package/build/__tests__/support/consts.js.map +1 -0
  26. package/build/__tests__/support/generateRandomOutletOptions.d.ts +2 -0
  27. package/build/__tests__/support/generateRandomOutletOptions.js +21 -0
  28. package/build/__tests__/support/generateRandomOutletOptions.js.map +1 -0
  29. package/build/errors/SpruceError.js.map +1 -1
  30. package/build/index.d.ts +1 -0
  31. package/build/index.js +3 -1
  32. package/build/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/Liblsl.ts +76 -73
  35. package/src/LslOutlet.ts +39 -37
  36. package/src/TimeMarkerOutlet.ts +19 -0
  37. package/src/__tests__/behavioral/LslOutlet.test.ts +50 -119
  38. package/src/__tests__/behavioral/TimeMarkerOutlet.test.ts +54 -0
  39. package/src/__tests__/implementation/Liblsl.test.ts +96 -84
  40. package/src/__tests__/support/SpyLiblsl.ts +41 -0
  41. package/src/__tests__/support/consts.ts +12 -0
  42. package/src/__tests__/support/generateRandomOutletOptions.ts +19 -0
  43. package/src/index.ts +2 -0
@@ -0,0 +1,54 @@
1
+ import { randomInt } from 'crypto'
2
+ import AbstractSpruceTest, { assert, test } from '@sprucelabs/test-utils'
3
+ import LiblslImpl from '../../Liblsl'
4
+ import { LslOutletOptions } from '../../LslOutlet'
5
+ import TimeMarkerOutlet from '../../TimeMarkerOutlet'
6
+ import generateRandomOutletOptions from '../support/generateRandomOutletOptions'
7
+ import { SpyLiblsl } from '../support/SpyLiblsl'
8
+
9
+ export default class TimeMarkerOutletTest extends AbstractSpruceTest {
10
+ private static spyLiblsl: SpyLiblsl
11
+
12
+ protected static async beforeEach() {
13
+ await super.beforeEach()
14
+ TimeMarkerOutlet.Class = SpyTimeMarkerOutlet
15
+ this.spyLiblsl = new SpyLiblsl()
16
+ LiblslImpl.setInstance(this.spyLiblsl)
17
+ }
18
+
19
+ @test()
20
+ protected static async loadsWithTimeMarkerSpecificOptions() {
21
+ const outlet = this.Outlet()
22
+ assert.isEqualDeep(outlet.options, {
23
+ name: 'Time markers',
24
+ type: 'Markers',
25
+ channelNames: ['Markers'],
26
+ sampleRate: 0,
27
+ channelFormat: 'string',
28
+ sourceId: 'time-markers',
29
+ manufacturer: 'N/A',
30
+ unit: 'N/A',
31
+ chunkSize: 0,
32
+ maxBuffered: 0,
33
+ })
34
+ }
35
+
36
+ @test()
37
+ protected static async canOverrideDefaultOptions() {
38
+ const options = generateRandomOutletOptions(randomInt(8))
39
+ const outlet = this.Outlet(options)
40
+ assert.isEqualDeep(outlet.options, options)
41
+ }
42
+
43
+ private static Outlet(options?: Partial<LslOutletOptions>) {
44
+ return TimeMarkerOutlet.Outlet(options) as SpyTimeMarkerOutlet
45
+ }
46
+ }
47
+
48
+ class SpyTimeMarkerOutlet extends TimeMarkerOutlet {
49
+ public options: LslOutletOptions
50
+ public constructor(options: LslOutletOptions) {
51
+ super(options)
52
+ this.options = options
53
+ }
54
+ }
@@ -8,16 +8,17 @@ import AbstractSpruceTest, {
8
8
  import ArrayType from 'ref-array-napi'
9
9
  import ref from 'ref-napi'
10
10
  import LiblslImpl, {
11
- CreateStreamInfoOptions,
12
- LiblslBindings,
13
11
  Liblsl,
14
- LslSample,
12
+ LiblslBindings,
13
+ CreateStreamInfoOptions,
14
+ AppendChannelsToStreamInfoOptions,
15
15
  CreateOutletOptions,
16
- LslBindingsOutlet,
17
- LslBindingsStreamInfo,
16
+ PushSampleOptions,
18
17
  LslChannel,
19
- LslBindingsDesc,
20
- LslBindingsChild,
18
+ BoundOutlet,
19
+ BoundStreamInfo,
20
+ BoundDesc,
21
+ BoundChild,
21
22
  } from '../../Liblsl'
22
23
 
23
24
  export default class LiblslTest extends AbstractSpruceTest {
@@ -25,10 +26,10 @@ export default class LiblslTest extends AbstractSpruceTest {
25
26
  private static libraryPath?: string
26
27
  private static libraryOptions?: Record<string, any>
27
28
  private static fakeBindings: LiblslBindings
28
- private static fakeStreamInfo: LslBindingsStreamInfo
29
- private static fakeOutlet: LslBindingsOutlet
30
- private static fakeDesc: LslBindingsDesc
31
- private static fakeChildNamedChannels: LslBindingsChild
29
+ private static fakeStreamInfo: BoundStreamInfo
30
+ private static fakeOutlet: BoundOutlet
31
+ private static fakeDesc: BoundDesc
32
+ private static fakeChildNamedChannels: BoundChild
32
33
  private static createStreamInfoParams?: any[]
33
34
  private static appendChildParams: any[] = []
34
35
  private static createOutletParams?: any[]
@@ -36,8 +37,8 @@ export default class LiblslTest extends AbstractSpruceTest {
36
37
  private static appendChildValueParams: any[]
37
38
  private static localClock: number
38
39
  private static shouldThrowWhenCreatingBindings: boolean
39
- private static getDescriptionParams?: [LslBindingsStreamInfo]
40
- private static fakeChildNamedChannel: LslBindingsChild
40
+ private static getDescriptionParams?: [BoundStreamInfo]
41
+ private static fakeChildNamedChannel: BoundChild
41
42
  private static appendChildHitCount: number
42
43
 
43
44
  protected static async beforeEach(): Promise<void> {
@@ -79,7 +80,7 @@ export default class LiblslTest extends AbstractSpruceTest {
79
80
  return 0
80
81
  },
81
82
  lsl_local_clock: () => this.localClock,
82
- lsl_get_desc: (info: LslBindingsStreamInfo) => {
83
+ lsl_get_desc: (info: BoundStreamInfo) => {
83
84
  this.getDescriptionParams = [info]
84
85
  return this.fakeDesc
85
86
  },
@@ -108,12 +109,12 @@ export default class LiblslTest extends AbstractSpruceTest {
108
109
  },
109
110
  }
110
111
 
111
- LiblslImpl.reset()
112
+ LiblslImpl.resetInstance()
112
113
  this.lsl = LiblslImpl.getInstance()
113
114
  }
114
115
 
115
116
  @test()
116
- protected static async throwsIfNoEnvSet() {
117
+ protected static async throwsWithMissingEnv() {
117
118
  delete process.env.LIBLSL_PATH
118
119
  const err = assert.doesThrow(() => new LiblslImpl())
119
120
  errorAssert.assertError(err, 'MISSING_PARAMETERS', {
@@ -122,7 +123,7 @@ export default class LiblslTest extends AbstractSpruceTest {
122
123
  }
123
124
 
124
125
  @test()
125
- protected static async throwsHelpfulErrorWhenBindingsFailToLoad() {
126
+ protected static async throwsWhenBindingsFailToLoad() {
126
127
  this.shouldThrowWhenCreatingBindings = true
127
128
  const err = assert.doesThrow(() => new LiblslImpl())
128
129
  errorAssert.assertError(err, 'FAILED_TO_LOAD_LIBLSL', {
@@ -130,6 +131,49 @@ export default class LiblslTest extends AbstractSpruceTest {
130
131
  })
131
132
  }
132
133
 
134
+ @test()
135
+ protected static throwsWhenCreateStreamInfoIsMissingRequiredParams() {
136
+ //@ts-ignore
137
+ const err = assert.doesThrow(() => this.lsl.createStreamInfo())
138
+ errorAssert.assertError(err, 'MISSING_PARAMETERS', {
139
+ parameters: [
140
+ 'name',
141
+ 'type',
142
+ 'channelCount',
143
+ 'sampleRate',
144
+ 'channelFormat',
145
+ 'sourceId',
146
+ ],
147
+ })
148
+ }
149
+
150
+ @test()
151
+ protected static async throwsWhenAppendChannelsIsMissingRequiredParams() {
152
+ //@ts-ignore
153
+ const err = assert.doesThrow(() => this.lsl.appendChannelsToStreamInfo({}))
154
+ errorAssert.assertError(err, 'MISSING_PARAMETERS', {
155
+ parameters: ['info', 'channels'],
156
+ })
157
+ }
158
+
159
+ @test()
160
+ protected static async throwsWhenCreateOutletIsMissingRequiredParams() {
161
+ //@ts-ignore
162
+ const err = assert.doesThrow(() => this.lsl.createOutlet({}))
163
+ errorAssert.assertError(err, 'MISSING_PARAMETERS', {
164
+ parameters: ['info', 'chunkSize', 'maxBuffered'],
165
+ })
166
+ }
167
+
168
+ @test()
169
+ protected static async throwsWhenPushSampleIsMissingRequiredParams() {
170
+ //@ts-ignore
171
+ const err = assert.doesThrow(() => this.lsl.pushSample())
172
+ errorAssert.assertError(err, 'MISSING_PARAMETERS', {
173
+ parameters: ['outlet', 'sample'],
174
+ })
175
+ }
176
+
133
177
  @test()
134
178
  protected static async worksAsASingleton() {
135
179
  const liblsl = LiblslImpl.getInstance()
@@ -172,23 +216,7 @@ export default class LiblslTest extends AbstractSpruceTest {
172
216
  }
173
217
 
174
218
  @test()
175
- protected static createStreamInfoThrowsWithMissingRequiredParams() {
176
- //@ts-ignore
177
- const err = assert.doesThrow(() => this.lsl.createStreamInfo())
178
- errorAssert.assertError(err, 'MISSING_PARAMETERS', {
179
- parameters: [
180
- 'name',
181
- 'type',
182
- 'channelCount',
183
- 'sampleRate',
184
- 'channelFormat',
185
- 'sourceId',
186
- ],
187
- })
188
- }
189
-
190
- @test()
191
- protected static async canGetStreamInfoWithRequiredParams() {
219
+ protected static async canCreateStreamInfoWithRequiredParams() {
192
220
  const options = this.generateRandomCreateStreamInfoOptions()
193
221
  const actual = this.lsl.createStreamInfo(options)
194
222
 
@@ -196,15 +224,6 @@ export default class LiblslTest extends AbstractSpruceTest {
196
224
  assert.isEqualDeep(this.createStreamInfoParams, Object.values(options))
197
225
  }
198
226
 
199
- @test()
200
- protected static async createOutletThrowsWhenMissingRequiredParams() {
201
- //@ts-ignore
202
- const err = assert.doesThrow(() => this.lsl.createOutlet({}))
203
- errorAssert.assertError(err, 'MISSING_PARAMETERS', {
204
- parameters: ['info', 'chunkSize', 'maxBuffered'],
205
- })
206
- }
207
-
208
227
  @test()
209
228
  protected static async canCreateOutletWithRequiredParams() {
210
229
  const info = this.createRandomStreamInfo()
@@ -219,37 +238,27 @@ export default class LiblslTest extends AbstractSpruceTest {
219
238
  }
220
239
 
221
240
  @test()
222
- protected static async pushSampleThrowsWhenMissingRequiredParams() {
223
- //@ts-ignore
224
- const err = assert.doesThrow(() => this.lsl.pushSample())
225
- errorAssert.assertError(err, 'MISSING_PARAMETERS', {
226
- parameters: ['outlet', 'sample'],
227
- })
228
- }
229
-
230
- @test()
231
- protected static async pushSampleCallsBindings() {
241
+ protected static async canPushSampleWithRequiredParams() {
232
242
  const expected = [1, 2, 3]
233
- this.lsl.pushSample(this.fakeOutlet, expected)
243
+ const options = {
244
+ outlet: this.fakeOutlet,
245
+ sample: expected,
246
+ }
247
+ this.lsl.pushSample(options)
234
248
  assert.isEqual(this.pushSampleParams?.[0], this.fakeOutlet)
235
249
  assert.isEqual(this.pushSampleParams?.[1], expected)
236
250
  assert.isEqual(this.pushSampleParams?.[2], this.localClock)
237
251
  }
238
252
 
239
- @test()
240
- protected static async appendChannelsThrowsWithMissingParams() {
241
- //@ts-ignore
242
- const err = assert.doesThrow(() => this.lsl.appendChannelsToStreamInfo())
243
- errorAssert.assertError(err, 'MISSING_PARAMETERS', {
244
- parameters: ['info', 'channels'],
245
- })
246
- }
247
-
248
253
  @test()
249
254
  protected static async addingSingleChannelGetsDescription() {
250
255
  const info = this.createRandomStreamInfo()
251
256
  const channel: LslChannel = this.generateRandomChannelValues()
252
- this.lsl.appendChannelsToStreamInfo(info, [channel])
257
+
258
+ this.lsl.appendChannelsToStreamInfo({
259
+ info,
260
+ channels: [channel],
261
+ })
253
262
  assert.isEqual(this.getDescriptionParams?.[0], info)
254
263
 
255
264
  assert.isEqual(this.appendChildParams?.[0][0], this.fakeDesc)
@@ -280,7 +289,10 @@ export default class LiblslTest extends AbstractSpruceTest {
280
289
  const channel1 = this.generateRandomChannelValues()
281
290
  const channel2 = this.generateRandomChannelValues()
282
291
 
283
- this.lsl.appendChannelsToStreamInfo(info, [channel1, channel2])
292
+ this.lsl.appendChannelsToStreamInfo({
293
+ info,
294
+ channels: [channel1, channel2],
295
+ })
284
296
 
285
297
  assert.isEqual(this.appendChildParams?.[2][0], this.fakeChildNamedChannels)
286
298
  assert.isEqual(this.appendChildParams?.[2][1], 'channel')
@@ -290,6 +302,12 @@ export default class LiblslTest extends AbstractSpruceTest {
290
302
  assert.isEqual(this.appendChildValueParams[5][2], channel2.type)
291
303
  }
292
304
 
305
+ private static createRandomStreamInfo() {
306
+ return this.lsl.createStreamInfo(
307
+ this.generateRandomCreateStreamInfoOptions()
308
+ )
309
+ }
310
+
293
311
  private static generateRandomChannelValues(): LslChannel {
294
312
  return {
295
313
  label: generateId(),
@@ -298,38 +316,32 @@ export default class LiblslTest extends AbstractSpruceTest {
298
316
  }
299
317
  }
300
318
 
301
- private static createRandomStreamInfo() {
302
- return this.lsl.createStreamInfo(
303
- this.generateRandomCreateStreamInfoOptions()
304
- )
305
- }
306
-
307
319
  private static generateRandomCreateStreamInfoOptions() {
308
320
  return {
309
321
  name: generateId(),
310
322
  type: generateId(),
311
- channelCount: randomInt(10),
312
- sampleRate: randomInt(10),
313
- channelFormat: randomInt(10),
323
+ channelCount: randomInt(1, 10),
324
+ sampleRate: randomInt(100),
325
+ channelFormat: randomInt(7),
314
326
  sourceId: generateId(),
315
327
  }
316
328
  }
317
329
  }
318
330
 
319
331
  class FakeLiblsl implements Liblsl {
332
+ public createStreamInfo(_options: CreateStreamInfoOptions): BoundStreamInfo {
333
+ return {} as BoundStreamInfo
334
+ }
335
+
320
336
  public appendChannelsToStreamInfo(
321
- _info: LslBindingsStreamInfo,
322
- _channels: LslChannel[]
337
+ _options: AppendChannelsToStreamInfoOptions
323
338
  ): void {}
324
- public createOutlet(_options: CreateOutletOptions): LslBindingsOutlet {
325
- return {} as LslBindingsOutlet
326
- }
327
- public createStreamInfo(
328
- _options: CreateStreamInfoOptions
329
- ): LslBindingsStreamInfo {
330
- return {} as LslBindingsStreamInfo
339
+
340
+ public createOutlet(_options: CreateOutletOptions): BoundOutlet {
341
+ return {} as BoundOutlet
331
342
  }
332
- public pushSample(_sample: LslSample): void {}
343
+
344
+ public pushSample(_options: PushSampleOptions): void {}
333
345
  }
334
346
 
335
347
  const streamInfo = ref.refType(ref.types.void)
@@ -0,0 +1,41 @@
1
+ import {
2
+ CreateOutletOptions,
3
+ CreateStreamInfoOptions,
4
+ Liblsl,
5
+ BoundOutlet,
6
+ BoundStreamInfo,
7
+ AppendChannelsToStreamInfoOptions,
8
+ PushSampleOptions,
9
+ } from '../../Liblsl'
10
+
11
+ export class SpyLiblsl implements Liblsl {
12
+ public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
13
+ public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
14
+ public lastCreateOutletOptions?: CreateOutletOptions
15
+ public lastPushSampleOptions?: PushSampleOptions
16
+
17
+ public outlet: BoundOutlet = {} as BoundOutlet
18
+ public streamInfo: BoundStreamInfo = {} as BoundStreamInfo
19
+ public createStreamInfoHitCount = 0
20
+
21
+ public createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo {
22
+ this.createStreamInfoHitCount++
23
+ this.lastCreateStreamInfoOptions = options
24
+ return this.streamInfo
25
+ }
26
+
27
+ public appendChannelsToStreamInfo(
28
+ options: AppendChannelsToStreamInfoOptions
29
+ ): void {
30
+ this.lastAppendChannelsToStreamInfoOptions = options
31
+ }
32
+
33
+ public createOutlet(options: CreateOutletOptions): BoundOutlet {
34
+ this.lastCreateOutletOptions = options
35
+ return this.outlet
36
+ }
37
+
38
+ public pushSample(options: PushSampleOptions): void {
39
+ this.lastPushSampleOptions = options
40
+ }
41
+ }
@@ -0,0 +1,12 @@
1
+ export const TEST_CHANNEL_FORMATS = [
2
+ 'undefined',
3
+ 'float32',
4
+ 'double64',
5
+ 'string',
6
+ 'int32',
7
+ 'int16',
8
+ 'int8',
9
+ 'int64',
10
+ ]
11
+
12
+ export type TestChannelFormat = (typeof TEST_CHANNEL_FORMATS)[number]
@@ -0,0 +1,19 @@
1
+ import { randomInt } from 'crypto'
2
+ import { generateId } from '@sprucelabs/test-utils'
3
+ import { LslOutletOptions } from '../../LslOutlet'
4
+ import { TEST_CHANNEL_FORMATS } from './consts'
5
+
6
+ export default function generateRandomOutletOptions(randomChannelIdx: number) {
7
+ return {
8
+ name: generateId(),
9
+ type: generateId(),
10
+ channelNames: new Array(randomInt(1, 10)).fill(generateId()),
11
+ sampleRate: Math.random() * 10,
12
+ channelFormat: TEST_CHANNEL_FORMATS[randomChannelIdx],
13
+ sourceId: generateId(),
14
+ manufacturer: generateId(),
15
+ unit: generateId(),
16
+ chunkSize: randomInt(0, 10),
17
+ maxBuffered: randomInt(0, 10),
18
+ } as LslOutletOptions
19
+ }
package/src/index.ts CHANGED
@@ -3,3 +3,5 @@ export * from './Liblsl'
3
3
 
4
4
  export { default as LslOutletImpl } from './LslOutlet'
5
5
  export * from './LslOutlet'
6
+
7
+ export { default as TimeMarkerOutlet } from './TimeMarkerOutlet'