@neurodevs/ndx-native 0.0.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.
- package/.nvmrc +1 -0
- package/.vscode/launch.json +58 -0
- package/.vscode/settings.json +67 -0
- package/.vscode/tasks.json +130 -0
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/build/.spruce/settings.json +11 -0
- package/build/__tests__/AbstractPackageTest.d.ts +7 -0
- package/build/__tests__/AbstractPackageTest.js +14 -0
- package/build/__tests__/AbstractPackageTest.js.map +1 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.d.ts +30 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.js +164 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.js.map +1 -0
- package/build/__tests__/impl/LiblslAdapter.test.d.ts +84 -0
- package/build/__tests__/impl/LiblslAdapter.test.js +750 -0
- package/build/__tests__/impl/LiblslAdapter.test.js.map +1 -0
- package/build/__tests__/impl/LibxdfAdapter.test.d.ts +45 -0
- package/build/__tests__/impl/LibxdfAdapter.test.js +243 -0
- package/build/__tests__/impl/LibxdfAdapter.test.js.map +1 -0
- package/build/consts.d.ts +11 -0
- package/build/consts.js +21 -0
- package/build/consts.js.map +1 -0
- package/build/impl/LabrecorderAdapter.d.ts +30 -0
- package/build/impl/LabrecorderAdapter.js +69 -0
- package/build/impl/LabrecorderAdapter.js.map +1 -0
- package/build/impl/LiblslAdapter.d.ts +172 -0
- package/build/impl/LiblslAdapter.js +360 -0
- package/build/impl/LiblslAdapter.js.map +1 -0
- package/build/impl/LibxdfAdapter.d.ts +92 -0
- package/build/impl/LibxdfAdapter.js +130 -0
- package/build/impl/LibxdfAdapter.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.js +18 -0
- package/build/index.js.map +1 -0
- package/build/lib/handleError.d.ts +8 -0
- package/build/lib/handleError.js +25 -0
- package/build/lib/handleError.js.map +1 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.d.ts +15 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.js +26 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.js.map +1 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +57 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.js +140 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.d.ts +13 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.js +25 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.js.map +1 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.d.ts +6 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.js +10 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.js.map +1 -0
- package/eslint.config.mjs +3 -0
- package/package.json +84 -0
- package/src/.spruce/settings.json +11 -0
- package/src/__tests__/AbstractPackageTest.ts +16 -0
- package/src/__tests__/impl/LabrecorderAdapter.test.ts +197 -0
- package/src/__tests__/impl/LiblslAdapter.test.ts +907 -0
- package/src/__tests__/impl/LibxdfAdapter.test.ts +290 -0
- package/src/consts.ts +21 -0
- package/src/impl/LabrecorderAdapter.ts +100 -0
- package/src/impl/LiblslAdapter.ts +616 -0
- package/src/impl/LibxdfAdapter.ts +227 -0
- package/src/index.ts +26 -0
- package/src/lib/handleError.ts +24 -0
- package/src/testDoubles/Labrecorder/FakeLabrecorder.ts +37 -0
- package/src/testDoubles/Liblsl/FakeLiblsl.ts +204 -0
- package/src/testDoubles/Libxdf/FakeLibxdf.ts +33 -0
- package/src/testDoubles/Libxdf/SpyLibxdf.ts +13 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import {
|
|
3
|
+
MangledNameExtractor,
|
|
4
|
+
MangledNameMap,
|
|
5
|
+
} from '@neurodevs/node-mangled-names'
|
|
6
|
+
import { DataType, define, FieldType, FuncObj, open } from 'ffi-rs'
|
|
7
|
+
|
|
8
|
+
export default class LibxdfAdapter implements Libxdf {
|
|
9
|
+
public static Class?: LibxdfConstructor
|
|
10
|
+
public static ffiRsOpen = open
|
|
11
|
+
public static ffiRsDefine = define
|
|
12
|
+
private static readonly loadXdfName = 'load_xdf_to_json'
|
|
13
|
+
private static unmangledNames = [this.loadXdfName]
|
|
14
|
+
|
|
15
|
+
protected bindings!: LibxdfBindings
|
|
16
|
+
private libxdfPath: string
|
|
17
|
+
private mangledNameMap: MangledNameMap
|
|
18
|
+
|
|
19
|
+
protected constructor(libxdfPath: string, mangledNameMap: MangledNameMap) {
|
|
20
|
+
this.libxdfPath = libxdfPath
|
|
21
|
+
this.mangledNameMap = mangledNameMap
|
|
22
|
+
|
|
23
|
+
this.tryToLoadBindings()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public static async Create(
|
|
27
|
+
libxdfPath: string,
|
|
28
|
+
throwIfPathNotExists = true
|
|
29
|
+
) {
|
|
30
|
+
if (throwIfPathNotExists && !fs.existsSync(libxdfPath)) {
|
|
31
|
+
throw new Error(this.generateFailedMessage(libxdfPath))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const mangledNameMap = await this.loadMangledNameMap(libxdfPath)
|
|
35
|
+
return new (this.Class ?? this)(libxdfPath, mangledNameMap)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private tryToLoadBindings() {
|
|
39
|
+
try {
|
|
40
|
+
this.bindings = this.loadBindings()
|
|
41
|
+
} catch (err: unknown) {
|
|
42
|
+
this.throwFailedToLoadLiblsl(err)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private loadBindings() {
|
|
47
|
+
this.openLibxdf()
|
|
48
|
+
return this.defineBindings()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private openLibxdf() {
|
|
52
|
+
LibxdfAdapter.ffiRsOpen({
|
|
53
|
+
library: 'xdf',
|
|
54
|
+
path: this.libxdfPath,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private defineBindings() {
|
|
59
|
+
const funcs = this.unmangledNames.reduce((acc, name) => {
|
|
60
|
+
const mangledName = this.mangledNameMap[name].slice(1)
|
|
61
|
+
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
acc[mangledName] = {
|
|
64
|
+
library: 'xdf',
|
|
65
|
+
retType: DataType.String,
|
|
66
|
+
paramsType: [DataType.String],
|
|
67
|
+
}
|
|
68
|
+
return acc
|
|
69
|
+
}, {})
|
|
70
|
+
|
|
71
|
+
return LibxdfAdapter.ffiRsDefine(funcs)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private throwFailedToLoadLiblsl(err: unknown) {
|
|
75
|
+
throw new Error(this.generateFailedMessage(err))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private generateFailedMessage(err: unknown) {
|
|
79
|
+
return `
|
|
80
|
+
${LibxdfAdapter.generateFailedMessage(this.libxdfPath)}
|
|
81
|
+
\n ${err}
|
|
82
|
+
\n
|
|
83
|
+
`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public loadXdf(path: string) {
|
|
87
|
+
const mangledName = this.mangledNameMap[this.loadXdfName].slice(1)
|
|
88
|
+
const mangledFunc = this.bindings[mangledName]
|
|
89
|
+
|
|
90
|
+
const serializedData = mangledFunc([path])
|
|
91
|
+
const parsedData = JSON.parse(serializedData) as LibxdfFile
|
|
92
|
+
|
|
93
|
+
if (!parsedData.events) {
|
|
94
|
+
parsedData.events = []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const mappedStreams = parsedData.streams.map((stream) => ({
|
|
98
|
+
id: stream.stream_id,
|
|
99
|
+
name: stream.stream_info.stream_name,
|
|
100
|
+
type: stream.stream_info.stream_type,
|
|
101
|
+
channelCount: stream.stream_info.channel_count,
|
|
102
|
+
channelFormat: stream.stream_info.channel_format,
|
|
103
|
+
nominalSampleRateHz: stream.stream_info.nominal_srate,
|
|
104
|
+
data: stream.time_series,
|
|
105
|
+
timestamps: stream.time_stamps,
|
|
106
|
+
}))
|
|
107
|
+
|
|
108
|
+
const mappedEvents = parsedData.events.map((event) => ({
|
|
109
|
+
name: event.event_name,
|
|
110
|
+
timestamp: event.event_timestamp,
|
|
111
|
+
streamId: event.stream_id,
|
|
112
|
+
}))
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
path,
|
|
116
|
+
streams: mappedStreams,
|
|
117
|
+
events: mappedEvents,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private get unmangledNames() {
|
|
122
|
+
return LibxdfAdapter.unmangledNames
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private get loadXdfName() {
|
|
126
|
+
return LibxdfAdapter.loadXdfName
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private static generateFailedMessage(libxdfPath: string) {
|
|
130
|
+
return `
|
|
131
|
+
\n -----------------------------------
|
|
132
|
+
\n Failed to load libxdf! Tried to load from:
|
|
133
|
+
\n ${libxdfPath}
|
|
134
|
+
\n Instructions to save your day (on MacOS):
|
|
135
|
+
\n 1. git clone https://github.com/neurodevs/libxdf.git
|
|
136
|
+
\n 2. cd libxdf && cmake -S . -B build && cmake --build build
|
|
137
|
+
\n 3. sudo cp build/libxdf.dylib /opt/local/lib/
|
|
138
|
+
\n 4. Try whatever you were doing again!
|
|
139
|
+
\n Modify step 3 for your OS if you are not on MacOS.
|
|
140
|
+
\n Check the official repo for OS-specific instructions:
|
|
141
|
+
\n https://github.com/xdf-modules/libxdf
|
|
142
|
+
\n If you're still unsure, ask an LLM with this error and your OS.
|
|
143
|
+
\n You could also post an issue on the repo:
|
|
144
|
+
\n https://github.com/neurodevs/node-xdf/issues
|
|
145
|
+
\n Good luck!
|
|
146
|
+
\n @ericthecurious
|
|
147
|
+
\n -----------------------------------
|
|
148
|
+
`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private static async loadMangledNameMap(libxdfPath: string) {
|
|
152
|
+
const extractor = this.MangledNameExtractor()
|
|
153
|
+
return await extractor.extract(libxdfPath, this.unmangledNames)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private static MangledNameExtractor() {
|
|
157
|
+
return MangledNameExtractor.Create()
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface Libxdf {
|
|
162
|
+
loadXdf(path: string): XdfFile
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export type LibxdfConstructor = new (
|
|
166
|
+
libxdfPath: string,
|
|
167
|
+
mangledNameMap: MangledNameMap
|
|
168
|
+
) => Libxdf
|
|
169
|
+
|
|
170
|
+
export type LibxdfBindings = Record<string, (path: string[]) => string>
|
|
171
|
+
|
|
172
|
+
export type LibxdfStatusCode = 0 | Exclude<number, 0>
|
|
173
|
+
|
|
174
|
+
export type FfiRsDefineOptions = FuncObj<
|
|
175
|
+
FieldType,
|
|
176
|
+
boolean | undefined,
|
|
177
|
+
boolean | undefined
|
|
178
|
+
>
|
|
179
|
+
|
|
180
|
+
export interface XdfFile {
|
|
181
|
+
path: string
|
|
182
|
+
streams: XdfStream[]
|
|
183
|
+
events: XdfEvent[]
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface XdfStream {
|
|
187
|
+
id: number
|
|
188
|
+
name: string
|
|
189
|
+
type: string
|
|
190
|
+
channelCount: number
|
|
191
|
+
channelFormat: string
|
|
192
|
+
nominalSampleRateHz: number
|
|
193
|
+
data: number[][]
|
|
194
|
+
timestamps: number[]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface XdfEvent {
|
|
198
|
+
name: string
|
|
199
|
+
timestamp: number
|
|
200
|
+
streamId: number
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface LibxdfFile {
|
|
204
|
+
streams: LibxdfStream[]
|
|
205
|
+
events: LibxdfEvent[]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface LibxdfStream {
|
|
209
|
+
stream_id: number
|
|
210
|
+
stream_info: LibxdfStreamInfo
|
|
211
|
+
time_series: number[][]
|
|
212
|
+
time_stamps: number[]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface LibxdfStreamInfo {
|
|
216
|
+
channel_count: number
|
|
217
|
+
channel_format: string
|
|
218
|
+
nominal_srate: number
|
|
219
|
+
stream_name: string
|
|
220
|
+
stream_type: string
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface LibxdfEvent {
|
|
224
|
+
stream_id: number
|
|
225
|
+
event_name: string
|
|
226
|
+
event_timestamp: number
|
|
227
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Labrecorder
|
|
2
|
+
|
|
3
|
+
export { default as LabrecorderAdapter } from './impl/LabrecorderAdapter.js'
|
|
4
|
+
export * from './impl/LabrecorderAdapter.js'
|
|
5
|
+
|
|
6
|
+
export { default as FakeLabrecorder } from './testDoubles/Labrecorder/FakeLabrecorder.js'
|
|
7
|
+
export * from './testDoubles/Labrecorder/FakeLabrecorder.js'
|
|
8
|
+
|
|
9
|
+
// Liblsl
|
|
10
|
+
|
|
11
|
+
export { default as LiblslAdapter } from './impl/LiblslAdapter.js'
|
|
12
|
+
export * from './impl/LiblslAdapter.js'
|
|
13
|
+
|
|
14
|
+
export { default as FakeLiblsl } from './testDoubles/Liblsl/FakeLiblsl.js'
|
|
15
|
+
export * from './testDoubles/Liblsl/FakeLiblsl.js'
|
|
16
|
+
|
|
17
|
+
// Libxdf
|
|
18
|
+
|
|
19
|
+
export { default as LibxdfAdapter } from './impl/LibxdfAdapter.js'
|
|
20
|
+
export * from './impl/LibxdfAdapter.js'
|
|
21
|
+
|
|
22
|
+
export { default as FakeLibxdf } from './testDoubles/Libxdf/FakeLibxdf.js'
|
|
23
|
+
export * from './testDoubles/Libxdf/FakeLibxdf.js'
|
|
24
|
+
|
|
25
|
+
export { default as SpyLibxdf } from './testDoubles/Libxdf/SpyLibxdf.js'
|
|
26
|
+
export * from './testDoubles/Libxdf/SpyLibxdf.js'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export default function handleError(errorCode: number) {
|
|
2
|
+
switch (errorCode) {
|
|
3
|
+
case 0:
|
|
4
|
+
return
|
|
5
|
+
case -1:
|
|
6
|
+
throw new Error(`The liblsl operation failed due to a timeout!`)
|
|
7
|
+
case -2:
|
|
8
|
+
throw new Error(`The liblsl stream has been lost!`)
|
|
9
|
+
case -3:
|
|
10
|
+
throw new Error(`A liblsl argument was incorrectly specified!`)
|
|
11
|
+
case -4:
|
|
12
|
+
throw new Error(`An internal liblsl error has occurred!`)
|
|
13
|
+
default:
|
|
14
|
+
throw new Error(`An unknown liblsl error has occurred!`)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export enum LslErrorCode {
|
|
19
|
+
Ok = 0,
|
|
20
|
+
Timeout = -1,
|
|
21
|
+
Lost = -2,
|
|
22
|
+
BadArgument = -3,
|
|
23
|
+
InternalError = -4,
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RecordingHandle, Labrecorder } from '../../impl/LabrecorderAdapter.js'
|
|
2
|
+
|
|
3
|
+
export default class FakeLabrecorder implements Labrecorder {
|
|
4
|
+
public static constructorCalls: string[] = []
|
|
5
|
+
|
|
6
|
+
public static createRecordingCalls: {
|
|
7
|
+
filename: string
|
|
8
|
+
watchFor: string[]
|
|
9
|
+
}[] = []
|
|
10
|
+
|
|
11
|
+
public static stopRecordingCalls: RecordingHandle[] = []
|
|
12
|
+
public static deleteRecordingCalls: RecordingHandle[] = []
|
|
13
|
+
|
|
14
|
+
public constructor(labrecorderPath: string) {
|
|
15
|
+
FakeLabrecorder.constructorCalls.push(labrecorderPath)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public createRecording(filename: string, watchFor: string[]) {
|
|
19
|
+
FakeLabrecorder.createRecordingCalls.push({ filename, watchFor })
|
|
20
|
+
return {} as RecordingHandle
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public stopRecording(recording: RecordingHandle) {
|
|
24
|
+
FakeLabrecorder.stopRecordingCalls.push(recording)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public deleteRecording(recording: RecordingHandle) {
|
|
28
|
+
FakeLabrecorder.deleteRecordingCalls.push(recording)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static resetTestDouble() {
|
|
32
|
+
this.constructorCalls = []
|
|
33
|
+
this.createRecordingCalls = []
|
|
34
|
+
this.stopRecordingCalls = []
|
|
35
|
+
this.deleteRecordingCalls = []
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import generateId from '@neurodevs/generate-id'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
OutletHandle,
|
|
5
|
+
InfoHandle,
|
|
6
|
+
CreateStreamInfoOptions,
|
|
7
|
+
AppendChannelsToStreamInfoOptions,
|
|
8
|
+
CreateOutletOptions,
|
|
9
|
+
PushSampleFloatTimestampOptions,
|
|
10
|
+
PushSampleStringTimestampOptions,
|
|
11
|
+
Liblsl,
|
|
12
|
+
CreateInletOptions,
|
|
13
|
+
DestroyInletOptions,
|
|
14
|
+
InletHandle,
|
|
15
|
+
FlushInletOptions,
|
|
16
|
+
PullChunkOptions,
|
|
17
|
+
PullSampleOptions,
|
|
18
|
+
DestroyStreamInfoOptions,
|
|
19
|
+
ResolveByPropOptions,
|
|
20
|
+
DestroyOutletOptions,
|
|
21
|
+
OpenStreamOptions,
|
|
22
|
+
CloseStreamOptions,
|
|
23
|
+
GetChannelCountOptions,
|
|
24
|
+
} from 'impl/LiblslAdapter.js'
|
|
25
|
+
|
|
26
|
+
export default class FakeLiblsl implements Liblsl {
|
|
27
|
+
public static fakeInfoHandles: InfoHandle[] = []
|
|
28
|
+
|
|
29
|
+
public static fakeSamples: Float32Array[] = [
|
|
30
|
+
new Float32Array([1, 2, 3]),
|
|
31
|
+
new Float32Array([4, 5, 6]),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
public static fakeChunks: Float32Array[] = [
|
|
35
|
+
new Float32Array([1, 2, 3, 4, 5, 6]),
|
|
36
|
+
new Float32Array([1, 2, 3, 4, 5, 6]),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
public static fakeTimestamps: Float64Array[] = [
|
|
40
|
+
new Float64Array([7, 8]),
|
|
41
|
+
new Float64Array([7, 8]),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
public static fakeChannelCount = 3
|
|
45
|
+
public static fakeErrorCode = 0
|
|
46
|
+
|
|
47
|
+
public liblslPath: string = generateId()
|
|
48
|
+
|
|
49
|
+
public fakeSamples = FakeLiblsl.fakeSamples.slice()
|
|
50
|
+
public fakeChunks = FakeLiblsl.fakeChunks.slice()
|
|
51
|
+
public fakeTimestamps = FakeLiblsl.fakeTimestamps.slice()
|
|
52
|
+
|
|
53
|
+
public outletHandle: OutletHandle = {} as OutletHandle
|
|
54
|
+
public infoHandle: InfoHandle = {} as InfoHandle
|
|
55
|
+
|
|
56
|
+
public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
|
|
57
|
+
public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
|
|
58
|
+
public lastDestroyStreamInfoOptions?: DestroyStreamInfoOptions
|
|
59
|
+
public lastResolveByPropOptions?: ResolveByPropOptions
|
|
60
|
+
public lastGetChannelCountOptions?: GetChannelCountOptions
|
|
61
|
+
|
|
62
|
+
public lastCreateOutletOptions?: CreateOutletOptions
|
|
63
|
+
public lastPushSampleFloatTimestampOptions?: PushSampleFloatTimestampOptions
|
|
64
|
+
public lastPushSampleStringTimestampOptions?: PushSampleStringTimestampOptions
|
|
65
|
+
public lastDestroyOutletOptions?: DestroyOutletOptions
|
|
66
|
+
|
|
67
|
+
public lastCreateInletOptions?: CreateInletOptions
|
|
68
|
+
public lastOpenStreamOptions?: OpenStreamOptions
|
|
69
|
+
public lastCloseStreamOptions?: CloseStreamOptions
|
|
70
|
+
public lastPullSampleOptions?: PullSampleOptions
|
|
71
|
+
public lastPullChunkOptions?: PullChunkOptions
|
|
72
|
+
public lastFlushInletOptions?: FlushInletOptions
|
|
73
|
+
public lastDestroyInletOptions?: DestroyInletOptions
|
|
74
|
+
|
|
75
|
+
public createStreamInfoHitCount = 0
|
|
76
|
+
public createOutletHitCount = 0
|
|
77
|
+
public destroyOutletHitCount = 0
|
|
78
|
+
public createInletHitCount = 0
|
|
79
|
+
public flushInletHitCount = 0
|
|
80
|
+
public destroyInletHitCount = 0
|
|
81
|
+
public localClockHitCount = 0
|
|
82
|
+
public pushSampleStringTimestampHitCount = 0
|
|
83
|
+
|
|
84
|
+
public createStreamInfo(options: CreateStreamInfoOptions) {
|
|
85
|
+
this.createStreamInfoHitCount++
|
|
86
|
+
this.lastCreateStreamInfoOptions = options
|
|
87
|
+
return this.infoHandle
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public appendChannelsToStreamInfo(
|
|
91
|
+
options: AppendChannelsToStreamInfoOptions
|
|
92
|
+
) {
|
|
93
|
+
this.lastAppendChannelsToStreamInfoOptions = options
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public getChannelCount(options: GetChannelCountOptions) {
|
|
97
|
+
this.lastGetChannelCountOptions = options
|
|
98
|
+
return FakeLiblsl.fakeChannelCount
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public destroyStreamInfo(options: DestroyStreamInfoOptions) {
|
|
102
|
+
this.lastDestroyStreamInfoOptions = options
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async resolveByProp(options: ResolveByPropOptions) {
|
|
106
|
+
this.lastResolveByPropOptions = options
|
|
107
|
+
return FakeLiblsl.fakeInfoHandles
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public pushSampleFloatTimestamp(options: PushSampleFloatTimestampOptions) {
|
|
111
|
+
this.lastPushSampleFloatTimestampOptions = options
|
|
112
|
+
return FakeLiblsl.fakeErrorCode
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public pushSampleStringTimestamp(
|
|
116
|
+
options: PushSampleStringTimestampOptions
|
|
117
|
+
) {
|
|
118
|
+
this.lastPushSampleStringTimestampOptions = options
|
|
119
|
+
this.pushSampleStringTimestampHitCount++
|
|
120
|
+
return FakeLiblsl.fakeErrorCode
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public createOutlet(options: CreateOutletOptions) {
|
|
124
|
+
this.createOutletHitCount++
|
|
125
|
+
this.lastCreateOutletOptions = options
|
|
126
|
+
return this.outletHandle
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public destroyOutlet(options: DestroyOutletOptions) {
|
|
130
|
+
this.destroyOutletHitCount++
|
|
131
|
+
this.lastDestroyOutletOptions = options
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public createInlet(options: CreateInletOptions) {
|
|
135
|
+
this.createInletHitCount++
|
|
136
|
+
this.lastCreateInletOptions = options
|
|
137
|
+
return {} as InletHandle
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public async openStream(options: OpenStreamOptions) {
|
|
141
|
+
this.lastOpenStreamOptions = options
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public async closeStream(options: CloseStreamOptions) {
|
|
145
|
+
this.lastCloseStreamOptions = options
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public pullSample(options: PullSampleOptions) {
|
|
149
|
+
this.lastPullSampleOptions = options
|
|
150
|
+
|
|
151
|
+
const sample = this.fakeSamples.shift()
|
|
152
|
+
|
|
153
|
+
if (sample) {
|
|
154
|
+
return 1
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return 0
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public pullChunk(options: PullChunkOptions) {
|
|
161
|
+
this.lastPullChunkOptions = options
|
|
162
|
+
|
|
163
|
+
const chunk = this.fakeChunks.shift()
|
|
164
|
+
const timestamps = this.fakeTimestamps.shift()
|
|
165
|
+
|
|
166
|
+
if (chunk && timestamps) {
|
|
167
|
+
return 1
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public flushInlet(options: FlushInletOptions) {
|
|
174
|
+
this.flushInletHitCount++
|
|
175
|
+
this.lastFlushInletOptions = options
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public destroyInlet(options: DestroyInletOptions) {
|
|
179
|
+
this.destroyInletHitCount++
|
|
180
|
+
this.lastDestroyInletOptions = options
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public localClock() {
|
|
184
|
+
this.localClockHitCount++
|
|
185
|
+
return new Date().getTime()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public resetTestDouble() {
|
|
189
|
+
this.lastCreateStreamInfoOptions = undefined
|
|
190
|
+
this.lastAppendChannelsToStreamInfoOptions = undefined
|
|
191
|
+
this.lastCreateOutletOptions = undefined
|
|
192
|
+
this.lastPushSampleFloatTimestampOptions = undefined
|
|
193
|
+
this.lastPushSampleStringTimestampOptions = undefined
|
|
194
|
+
|
|
195
|
+
this.createStreamInfoHitCount = 0
|
|
196
|
+
this.destroyOutletHitCount = 0
|
|
197
|
+
this.localClockHitCount = 0
|
|
198
|
+
this.pushSampleStringTimestampHitCount = 0
|
|
199
|
+
|
|
200
|
+
this.fakeSamples = FakeLiblsl.fakeSamples.slice()
|
|
201
|
+
this.fakeChunks = FakeLiblsl.fakeChunks.slice()
|
|
202
|
+
this.fakeTimestamps = FakeLiblsl.fakeTimestamps.slice()
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MangledNameMap } from '@neurodevs/node-mangled-names'
|
|
2
|
+
|
|
3
|
+
import { Libxdf, XdfFile } from '../../impl/LibxdfAdapter.js'
|
|
4
|
+
|
|
5
|
+
export default class FakeLibxdf implements Libxdf {
|
|
6
|
+
public static libxdfPath?: string
|
|
7
|
+
public static loadXdfCalls: string[] = []
|
|
8
|
+
public static mangledNameMap: MangledNameMap
|
|
9
|
+
public static fakeXdfFile: XdfFile = { path: '', streams: [], events: [] }
|
|
10
|
+
|
|
11
|
+
public constructor(libxdfPath: string, mangledNameMap: MangledNameMap) {
|
|
12
|
+
FakeLibxdf.libxdfPath = libxdfPath
|
|
13
|
+
FakeLibxdf.mangledNameMap = mangledNameMap
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public loadXdf(path: string) {
|
|
17
|
+
FakeLibxdf.loadXdfCalls.push(path)
|
|
18
|
+
return this.fakeXdfFile
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public get mangledNameMap() {
|
|
22
|
+
return FakeLibxdf.mangledNameMap
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public get fakeXdfFile() {
|
|
26
|
+
return FakeLibxdf.fakeXdfFile
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public static resetTestDouble() {
|
|
30
|
+
FakeLibxdf.libxdfPath = undefined
|
|
31
|
+
FakeLibxdf.loadXdfCalls = []
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MangledNameMap } from '@neurodevs/node-mangled-names'
|
|
2
|
+
|
|
3
|
+
import LibxdfAdapter from '../../impl/LibxdfAdapter.js'
|
|
4
|
+
|
|
5
|
+
export default class SpyLibxdf extends LibxdfAdapter {
|
|
6
|
+
public constructor(libxdfPath: string, mangledNameMap: MangledNameMap) {
|
|
7
|
+
super(libxdfPath, mangledNameMap)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public getBindings() {
|
|
11
|
+
return this.bindings
|
|
12
|
+
}
|
|
13
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "nodenext",
|
|
4
|
+
"moduleResolution": "nodenext",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"baseUrl": "src",
|
|
9
|
+
"outDir": "build",
|
|
10
|
+
"sourceMap": false,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noImplicitAny": true,
|
|
13
|
+
"noImplicitReturns": true,
|
|
14
|
+
"noUnusedLocals": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"esModuleInterop": true,
|
|
19
|
+
"moduleDetection": "force",
|
|
20
|
+
"allowJs": true,
|
|
21
|
+
"resolveJsonModule": true,
|
|
22
|
+
"experimentalDecorators": true
|
|
23
|
+
},
|
|
24
|
+
"include": [
|
|
25
|
+
"src/*.ts",
|
|
26
|
+
"src/**/*.ts"
|
|
27
|
+
]
|
|
28
|
+
}
|