bids-validator-deno 2.1.0__tar.gz → 2.1.1__tar.gz
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.
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/PKG-INFO +1 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/deno.json +17 -14
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/pyproject.toml +1 -1
- bids_validator_deno-2.1.1/src/files/access.test.ts +40 -0
- bids_validator_deno-2.1.1/src/files/access.ts +35 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/browser.ts +2 -2
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/deno.ts +1 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/gzip.test.ts +3 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/gzip.ts +2 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/inheritance.test.ts +18 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/inheritance.ts +1 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/json.test.ts +13 -5
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/json.ts +6 -2
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/nifti.test.ts +4 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/nifti.ts +4 -2
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/streams.ts +15 -5
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/tiff.test.ts +3 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/tiff.ts +2 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/tsv.test.ts +32 -12
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/tsv.ts +6 -4
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/issues/list.ts +7 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/applyRules.test.ts +1 -46
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/applyRules.ts +39 -29
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/associations.ts +4 -2
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/context.ts +16 -9
- bids_validator_deno-2.1.1/src/schema/datatypes.test.ts +57 -0
- bids_validator_deno-2.1.1/src/schema/datatypes.ts +30 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/expressionLanguage.test.ts +81 -2
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/expressionLanguage.ts +53 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/tables.test.ts +67 -33
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/tables.ts +78 -99
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/walk.ts +2 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/setup/loadSchema.ts +5 -5
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/nifti_rules.test.ts +14 -14
- bids_validator_deno-2.1.1/src/tests/utils.ts +14 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/filetree.ts +1 -1
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/bids.ts +2 -0
- bids_validator_deno-2.1.1/src/validators/filenameCase.test.ts +32 -0
- bids_validator_deno-2.1.1/src/validators/filenameCase.ts +16 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/filenameIdentify.test.ts +1 -19
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/filenameIdentify.ts +0 -25
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/validateFiles.test.ts +15 -13
- bids_validator_deno-2.1.0/src/schema/modalities.ts +0 -16
- bids_validator_deno-2.1.0/src/tests/utils.ts +0 -12
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/LICENSE +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/README.md +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/pdm_build.py +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/.git-meta.json +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/bids-validator.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/browser.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/deno.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/dwi.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/dwi.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/filetree.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/filetree.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/ignore.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/ignore.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/files/streams.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/issues/datasetIssues.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/issues/datasetIssues.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/issues/list.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/main.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/context.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/entities.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/entities.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/fixtures.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/schema/walk.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/setup/loadSchema.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/setup/options.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/setup/options.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/setup/requestPermissions.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/summary/collectSubjectMetadata.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/summary/summary.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/summary/summary.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/README.md +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/bom-utf16.tsv +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/bom-utf8.json +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/generate-filenames.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/bids_examples.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/common.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/derivatives.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/empty_files.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/hed-integration.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/valid_dataset.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/valid_filenames.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/local/valid_headers.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/nullReadBytes.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/regression.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/schema-expression-language.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/tests/simple-dataset.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/check.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/columns.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/columns.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/issues.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/schema.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/types/validation-result.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/errors.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/logger.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/logger.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/memoize.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/objectPathHandler.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/utils/output.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/bids.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/citation.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/citation.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/filenameValidate.test.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/filenameValidate.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/hed.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/internal/emptyFile.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/internal/unusedFile.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/validators/json.ts +0 -0
- {bids_validator_deno-2.1.0 → bids_validator_deno-2.1.1}/src/version.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bids/validator",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": "./src/bids-validator.ts",
|
|
6
6
|
"./main": "./src/main.ts",
|
|
@@ -27,22 +27,22 @@
|
|
|
27
27
|
]
|
|
28
28
|
},
|
|
29
29
|
"imports": {
|
|
30
|
-
"@ajv": "npm:ajv
|
|
30
|
+
"@ajv": "npm:ajv@^8.17.1",
|
|
31
31
|
"@bids/schema": "jsr:@bids/schema@~1.1.0",
|
|
32
32
|
"@cliffy/command": "jsr:@effigies/cliffy-command@1.0.0-dev.8",
|
|
33
33
|
"@cliffy/table": "jsr:@effigies/cliffy-table@1.0.0-dev.5",
|
|
34
|
-
"@hed/validator": "npm:hed-validator
|
|
35
|
-
"@ignore": "npm:ignore
|
|
36
|
-
"@libs/xml": "jsr:@libs/xml
|
|
37
|
-
"@mango/nifti": "npm:@bids/nifti-reader-js
|
|
38
|
-
"@std/assert": "jsr:@std/assert
|
|
39
|
-
"@std/fmt": "jsr:@std/fmt
|
|
40
|
-
"@std/fs": "jsr:@std/fs
|
|
41
|
-
"@std/io": "jsr:@std/io
|
|
42
|
-
"@std/log": "jsr:@std/log
|
|
43
|
-
"@std/path": "jsr:@std/path
|
|
44
|
-
"@std/streams": "jsr:@std/streams
|
|
45
|
-
"@std/yaml": "jsr:@std/yaml@^1.0.
|
|
34
|
+
"@hed/validator": "npm:hed-validator@~4.0.1",
|
|
35
|
+
"@ignore": "npm:ignore@^7.0.5",
|
|
36
|
+
"@libs/xml": "jsr:@libs/xml@^6.0.8",
|
|
37
|
+
"@mango/nifti": "npm:@bids/nifti-reader-js@^0.6.9",
|
|
38
|
+
"@std/assert": "jsr:@std/assert@^1.0.14",
|
|
39
|
+
"@std/fmt": "jsr:@std/fmt@^1.0.8",
|
|
40
|
+
"@std/fs": "jsr:@std/fs@^1.0.19",
|
|
41
|
+
"@std/io": "jsr:@std/io@^0.225.2",
|
|
42
|
+
"@std/log": "jsr:@std/log@^0.224.14",
|
|
43
|
+
"@std/path": "jsr:@std/path@^1.1.2",
|
|
44
|
+
"@std/streams": "jsr:@std/streams@^1.0.12",
|
|
45
|
+
"@std/yaml": "jsr:@std/yaml@^1.0.9"
|
|
46
46
|
},
|
|
47
47
|
"tasks": {
|
|
48
48
|
"test": "deno test -A src/"
|
|
@@ -54,6 +54,9 @@
|
|
|
54
54
|
"proseWrap": "preserve",
|
|
55
55
|
"include": [
|
|
56
56
|
"src/"
|
|
57
|
+
],
|
|
58
|
+
"exclude": [
|
|
59
|
+
"src/tests/bom-utf8.json"
|
|
57
60
|
]
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { assert, assertArrayIncludes, assertObjectMatch } from '@std/assert'
|
|
2
|
+
import { basename, dirname } from '@std/path'
|
|
3
|
+
import { BIDSFileDeno } from './deno.ts'
|
|
4
|
+
|
|
5
|
+
export function testAsyncFileAccess(
|
|
6
|
+
name: string,
|
|
7
|
+
fn: (file: BIDSFileDeno, ...args: any[]) => Promise<any>,
|
|
8
|
+
...args: any[]
|
|
9
|
+
) {
|
|
10
|
+
Deno.test({
|
|
11
|
+
name,
|
|
12
|
+
ignore: Deno.build.os === 'windows',
|
|
13
|
+
async fn(t) {
|
|
14
|
+
await t.step('Dangling symlink', async () => {
|
|
15
|
+
const file = new BIDSFileDeno('tests/data', '/broken-symlink')
|
|
16
|
+
try {
|
|
17
|
+
await fn(file, ...args)
|
|
18
|
+
assert(false, 'Expected error')
|
|
19
|
+
} catch (e: any) {
|
|
20
|
+
assertObjectMatch(e, {
|
|
21
|
+
code: 'FILE_READ',
|
|
22
|
+
location: '/broken-symlink',
|
|
23
|
+
})
|
|
24
|
+
assertArrayIncludes(['NotFound', 'FilesystemLoop'], [e.subCode])
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
await t.step('Insufficient permissions', async () => {
|
|
28
|
+
const tmpfile = await Deno.makeTempFile()
|
|
29
|
+
await Deno.chmod(tmpfile, 0o000)
|
|
30
|
+
const file = new BIDSFileDeno('', tmpfile)
|
|
31
|
+
try {
|
|
32
|
+
await fn(file, ...args)
|
|
33
|
+
assert(false, 'Expected error')
|
|
34
|
+
} catch (e: any) {
|
|
35
|
+
assertObjectMatch(e, { code: 'FILE_READ', subCode: 'PermissionDenied' })
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type BIDSFile } from '../types/filetree.ts'
|
|
2
|
+
import { type Issue } from '../types/issues.ts'
|
|
3
|
+
|
|
4
|
+
function IOErrorToIssue(err: { code: string; name: string }): Issue {
|
|
5
|
+
const subcode = err.name
|
|
6
|
+
let issueMessage: string | undefined = undefined
|
|
7
|
+
if (err.code === 'ENOENT' || err.code === 'ELOOP') {
|
|
8
|
+
issueMessage = 'Possible dangling symbolic link'
|
|
9
|
+
}
|
|
10
|
+
return { code: 'FILE_READ', subCode: err.name, issueMessage }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function openStream(file: BIDSFile): ReadableStream<Uint8Array<ArrayBuffer>> {
|
|
14
|
+
try {
|
|
15
|
+
return file.stream
|
|
16
|
+
} catch (err: any) {
|
|
17
|
+
throw { location: file.path, ...IOErrorToIssue(err) }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function readBytes(
|
|
22
|
+
file: BIDSFile,
|
|
23
|
+
size: number,
|
|
24
|
+
offset = 0,
|
|
25
|
+
): Promise<Uint8Array<ArrayBuffer>> {
|
|
26
|
+
return file.readBytes(size, offset).catch((err: any) => {
|
|
27
|
+
throw { location: file.path, ...IOErrorToIssue(err) }
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function readText(file: BIDSFile): Promise<string> {
|
|
32
|
+
return file.text().catch((err: any) => {
|
|
33
|
+
throw { location: file.path, ...IOErrorToIssue(err) }
|
|
34
|
+
})
|
|
35
|
+
}
|
|
@@ -29,8 +29,8 @@ export class BIDSFileBrowser implements BIDSFile {
|
|
|
29
29
|
return this.#file.size
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
get stream(): ReadableStream<Uint8Array
|
|
33
|
-
return this.#file.stream()
|
|
32
|
+
get stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
|
|
33
|
+
return this.#file.stream() as ReadableStream<Uint8Array<ArrayBuffer>>
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
get ignored(): boolean {
|
|
@@ -45,7 +45,7 @@ export class BIDSFileDeno implements BIDSFile {
|
|
|
45
45
|
return this.#fileInfo ? this.#fileInfo.size : -1
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
get stream(): ReadableStream<Uint8Array
|
|
48
|
+
get stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
|
|
49
49
|
const handle = this.#openHandle()
|
|
50
50
|
return handle.readable
|
|
51
51
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert, assertObjectMatch } from '@std/assert'
|
|
2
2
|
import { parseGzip } from './gzip.ts'
|
|
3
3
|
import { BIDSFileDeno } from './deno.ts'
|
|
4
|
+
import { testAsyncFileAccess } from './access.test.ts'
|
|
4
5
|
|
|
5
6
|
Deno.test('parseGzip', async (t) => {
|
|
6
7
|
await t.step('parses anonymized file', async () => {
|
|
@@ -40,3 +41,5 @@ Deno.test('parseGzip', async (t) => {
|
|
|
40
41
|
assert(!gzip)
|
|
41
42
|
})
|
|
42
43
|
})
|
|
44
|
+
|
|
45
|
+
testAsyncFileAccess('Test file access errors for parseGzip', parseGzip)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Gzip } from '@bids/schema/context'
|
|
6
6
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
7
|
+
import { readBytes } from './access.ts'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Parse a gzip header from a file
|
|
@@ -19,7 +20,7 @@ export async function parseGzip(
|
|
|
19
20
|
file: BIDSFile,
|
|
20
21
|
maxBytes: number = 512,
|
|
21
22
|
): Promise<Gzip | undefined> {
|
|
22
|
-
const buf = await
|
|
23
|
+
const buf = await readBytes(file, maxBytes)
|
|
23
24
|
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
24
25
|
if (view.byteLength < 2 || view.getUint16(0, false) !== 0x1f8b) return undefined
|
|
25
26
|
|
|
@@ -72,4 +72,22 @@ Deno.test('walkback inheritance tests', async (t) => {
|
|
|
72
72
|
assertEquals(rootElectrodes.path, '/space-talairach_electrodes.tsv')
|
|
73
73
|
},
|
|
74
74
|
)
|
|
75
|
+
await t.step(
|
|
76
|
+
'The presence of target entities does not trigger exact match logic',
|
|
77
|
+
async () => {
|
|
78
|
+
const rootFileTree = pathsToTree([
|
|
79
|
+
'/sub-01/ieeg/sub-01_task-rest_ieeg.edf',
|
|
80
|
+
'/sub-01/ieeg/sub-01_task-rest_space-anat_electrodes.tsv',
|
|
81
|
+
'/sub-01/ieeg/sub-01_task-rest_space-MNI_electrodes.tsv',
|
|
82
|
+
])
|
|
83
|
+
const dataFile = rootFileTree.get('sub-01/ieeg/sub-01_task-rest_ieeg.edf') as BIDSFile
|
|
84
|
+
const electrodes = walkBack(dataFile, true, ['.tsv'], 'electrodes', ['space'])
|
|
85
|
+
const localElectrodes: BIDSFile[] = electrodes.next().value
|
|
86
|
+
assert(Array.isArray(localElectrodes))
|
|
87
|
+
assertEquals(localElectrodes.map((f) => f.path), [
|
|
88
|
+
'/sub-01/ieeg/sub-01_task-rest_space-anat_electrodes.tsv',
|
|
89
|
+
'/sub-01/ieeg/sub-01_task-rest_space-MNI_electrodes.tsv',
|
|
90
|
+
])
|
|
91
|
+
},
|
|
92
|
+
)
|
|
75
93
|
})
|
|
@@ -50,7 +50,7 @@ export function* walkBack<T extends string[]>(
|
|
|
50
50
|
if (candidates.length > 1) {
|
|
51
51
|
const exactMatch = candidates.find((file) => {
|
|
52
52
|
const { entities } = readEntities(file.name)
|
|
53
|
-
return Object.keys(sourceParts.entities).every((entity) =>
|
|
53
|
+
return [...Object.keys(sourceParts.entities), ...(targetEntities ?? [])].every((entity) =>
|
|
54
54
|
entities[entity] === sourceParts.entities[entity]
|
|
55
55
|
)
|
|
56
56
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type assert, assertObjectMatch } from '@std/assert'
|
|
2
2
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
3
3
|
import type { FileIgnoreRules } from './ignore.ts'
|
|
4
|
+
import { testAsyncFileAccess } from './access.test.ts'
|
|
4
5
|
|
|
6
|
+
import { pathsToTree } from '../files/filetree.ts'
|
|
5
7
|
import { loadJSON } from './json.ts'
|
|
6
8
|
|
|
7
9
|
function encodeUTF16(text: string) {
|
|
@@ -17,9 +19,12 @@ function encodeUTF16(text: string) {
|
|
|
17
19
|
return buffer
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
function makeFile(text: string, encoding: string): BIDSFile {
|
|
22
|
+
function makeFile(path: string, text: string, encoding: string): BIDSFile {
|
|
21
23
|
const bytes = encoding === 'utf-8' ? new TextEncoder().encode(text) : encodeUTF16(text)
|
|
24
|
+
const file = pathsToTree([path]).get(path) as BIDSFile
|
|
22
25
|
return {
|
|
26
|
+
path: file.path,
|
|
27
|
+
parent: file.parent,
|
|
23
28
|
readBytes: async (size: number) => {
|
|
24
29
|
return new Uint8Array(bytes)
|
|
25
30
|
},
|
|
@@ -29,13 +34,13 @@ function makeFile(text: string, encoding: string): BIDSFile {
|
|
|
29
34
|
|
|
30
35
|
Deno.test('Test JSON error conditions', async (t) => {
|
|
31
36
|
await t.step('Load valid JSON', async () => {
|
|
32
|
-
const JSONfile = makeFile('{"a": 1}', 'utf-8')
|
|
37
|
+
const JSONfile = makeFile('/valid-contents.json', '{"a": 1}', 'utf-8')
|
|
33
38
|
const result = await loadJSON(JSONfile)
|
|
34
39
|
assertObjectMatch(result, { a: 1 })
|
|
35
40
|
})
|
|
36
41
|
|
|
37
42
|
await t.step('Error on BOM', async () => {
|
|
38
|
-
const BOMfile = makeFile('\uFEFF{"a": 1}', 'utf-8')
|
|
43
|
+
const BOMfile = makeFile('/BOM.json', '\uFEFF{"a": 1}', 'utf-8')
|
|
39
44
|
let error: any = undefined
|
|
40
45
|
await loadJSON(BOMfile).catch((e) => {
|
|
41
46
|
error = e
|
|
@@ -44,7 +49,7 @@ Deno.test('Test JSON error conditions', async (t) => {
|
|
|
44
49
|
})
|
|
45
50
|
|
|
46
51
|
await t.step('Error on UTF-16', async () => {
|
|
47
|
-
const UTF16file = makeFile('{"a": 1}', 'utf-16')
|
|
52
|
+
const UTF16file = makeFile('/utf16.json', '{"a": 1}', 'utf-16')
|
|
48
53
|
let error: any = undefined
|
|
49
54
|
await loadJSON(UTF16file).catch((e) => {
|
|
50
55
|
error = e
|
|
@@ -53,11 +58,14 @@ Deno.test('Test JSON error conditions', async (t) => {
|
|
|
53
58
|
})
|
|
54
59
|
|
|
55
60
|
await t.step('Error on invalid JSON syntax', async () => {
|
|
56
|
-
const badJSON = makeFile('{"a": 1]', 'utf-8')
|
|
61
|
+
const badJSON = makeFile('/bad-syntax.json', '{"a": 1]', 'utf-8')
|
|
57
62
|
let error: any = undefined
|
|
58
63
|
await loadJSON(badJSON).catch((e) => {
|
|
59
64
|
error = e
|
|
60
65
|
})
|
|
61
66
|
assertObjectMatch(error, { code: 'JSON_INVALID' })
|
|
62
67
|
})
|
|
68
|
+
loadJSON.cache.clear()
|
|
63
69
|
})
|
|
70
|
+
|
|
71
|
+
testAsyncFileAccess('Test file access errors for loadJSON', loadJSON)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { filememoizeAsync } from '../utils/memoize.ts'
|
|
1
2
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
3
|
+
import { readBytes } from './access.ts'
|
|
2
4
|
|
|
3
5
|
async function readJSONText(file: BIDSFile): Promise<string> {
|
|
4
6
|
// Read JSON text from a file
|
|
@@ -6,7 +8,7 @@ async function readJSONText(file: BIDSFile): Promise<string> {
|
|
|
6
8
|
const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true })
|
|
7
9
|
// Streaming TextDecoders are buggy in Deno and Chrome, so read the
|
|
8
10
|
// entire file into memory before decoding and parsing
|
|
9
|
-
const data = await
|
|
11
|
+
const data = await readBytes(file, file.size)
|
|
10
12
|
try {
|
|
11
13
|
const text = decoder.decode(data)
|
|
12
14
|
if (text.startsWith('\uFEFF')) {
|
|
@@ -20,7 +22,7 @@ async function readJSONText(file: BIDSFile): Promise<string> {
|
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
async function _loadJSON(file: BIDSFile): Promise<Record<string, unknown>> {
|
|
24
26
|
const text = await readJSONText(file) // Raise encoding errors
|
|
25
27
|
let parsedText
|
|
26
28
|
try {
|
|
@@ -36,3 +38,5 @@ export async function loadJSON(file: BIDSFile): Promise<Record<string, unknown>>
|
|
|
36
38
|
}
|
|
37
39
|
return parsedText
|
|
38
40
|
}
|
|
41
|
+
|
|
42
|
+
export const loadJSON = filememoizeAsync(_loadJSON)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { assert, assertEquals, assertObjectMatch } from '@std/assert'
|
|
2
2
|
import { FileIgnoreRules } from './ignore.ts'
|
|
3
3
|
import { BIDSFileDeno } from './deno.ts'
|
|
4
|
+
import { testAsyncFileAccess } from './access.test.ts'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import { axisCodes, loadHeader } from './nifti.ts'
|
|
6
7
|
|
|
7
8
|
Deno.test('Test loading nifti header', async (t) => {
|
|
8
9
|
const ignore = new FileIgnoreRules([])
|
|
@@ -96,3 +97,5 @@ Deno.test('Test extracting axis codes', async (t) => {
|
|
|
96
97
|
assertEquals(axisCodes(affine), ['A', 'S', 'R'])
|
|
97
98
|
})
|
|
98
99
|
})
|
|
100
|
+
|
|
101
|
+
testAsyncFileAccess('Test file access errors for loadHeader', loadHeader)
|
|
@@ -2,6 +2,7 @@ import { isCompressed, isNIFTI1, isNIFTI2, NIFTI1, NIFTI2 } from '@mango/nifti'
|
|
|
2
2
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
3
3
|
import { logger } from '../utils/logger.ts'
|
|
4
4
|
import type { NiftiHeader } from '@bids/schema/context'
|
|
5
|
+
import { readBytes } from './access.ts'
|
|
5
6
|
|
|
6
7
|
async function extract(buffer: Uint8Array, nbytes: number): Promise<Uint8Array<ArrayBuffer>> {
|
|
7
8
|
// The fflate decompression that is used in nifti-reader does not like
|
|
@@ -32,8 +33,8 @@ async function extract(buffer: Uint8Array, nbytes: number): Promise<Uint8Array<A
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export async function loadHeader(file: BIDSFile): Promise<NiftiHeader> {
|
|
36
|
+
const buf = await readBytes(file, 1024)
|
|
35
37
|
try {
|
|
36
|
-
const buf = await file.readBytes(1024)
|
|
37
38
|
const data = isCompressed(buf.buffer) ? await extract(buf, 540) : buf.slice(0, 540)
|
|
38
39
|
let header
|
|
39
40
|
if (isNIFTI1(data.buffer)) {
|
|
@@ -138,7 +139,8 @@ export function axisCodes(affine: number[][]): string[] {
|
|
|
138
139
|
|
|
139
140
|
// Orthogonalize cosZ with respect to cosX and orthY
|
|
140
141
|
const orthZ = sub(
|
|
141
|
-
cosZ,
|
|
142
|
+
cosZ,
|
|
143
|
+
add(scale(cosX, dot(cosX, cosZ)), scale(orthY, dot(orthY, cosZ))),
|
|
142
144
|
)
|
|
143
145
|
|
|
144
146
|
const basis = [cosX, orthY, orthZ]
|
|
@@ -8,6 +8,16 @@ export class UnicodeDecodeError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const _decode = TextDecoder.prototype.decode
|
|
12
|
+
|
|
13
|
+
TextDecoder.prototype.decode = function (input, options) {
|
|
14
|
+
try {
|
|
15
|
+
return _decode.call(this, input, options)
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw { code: 'INVALID_FILE_ENCODING', message: error }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* A transformer that ensures the input stream is valid UTF-8 and throws
|
|
13
23
|
* a UnicodeDecodeError if UTF-16 BOM is detected
|
|
@@ -16,15 +26,15 @@ export class UTF8StreamTransformer implements Transformer<Uint8Array, string> {
|
|
|
16
26
|
private decoder: TextDecoder
|
|
17
27
|
private firstChunk: boolean
|
|
18
28
|
|
|
19
|
-
constructor() {
|
|
20
|
-
this.decoder = new TextDecoder('utf-8')
|
|
29
|
+
constructor(options = { fatal: false }) {
|
|
30
|
+
this.decoder = new TextDecoder('utf-8', options)
|
|
21
31
|
this.firstChunk = true
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
transform(chunk: Uint8Array, controller: TransformStreamDefaultController<string>) {
|
|
25
35
|
// Check first chunk for UTF-16 BOM
|
|
26
36
|
if (this.firstChunk) {
|
|
27
|
-
|
|
37
|
+
let decoded = this.decoder.decode(chunk, { stream: true })
|
|
28
38
|
if (decoded.startsWith('\uFFFD')) {
|
|
29
39
|
throw new UnicodeDecodeError('This file appears to be UTF-16')
|
|
30
40
|
}
|
|
@@ -46,6 +56,6 @@ export class UTF8StreamTransformer implements Transformer<Uint8Array, string> {
|
|
|
46
56
|
/**
|
|
47
57
|
* Creates a TransformStream that validates and decodes UTF-8 text
|
|
48
58
|
*/
|
|
49
|
-
export function createUTF8Stream() {
|
|
50
|
-
return new TransformStream(new UTF8StreamTransformer())
|
|
59
|
+
export function createUTF8Stream(options = { fatal: false }) {
|
|
60
|
+
return new TransformStream(new UTF8StreamTransformer(options))
|
|
51
61
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert, assertObjectMatch } from '@std/assert'
|
|
2
2
|
import { parseTIFF } from './tiff.ts'
|
|
3
3
|
import { BIDSFileDeno } from './deno.ts'
|
|
4
|
+
import { testAsyncFileAccess } from './access.test.ts'
|
|
4
5
|
|
|
5
6
|
Deno.test('parseTIFF', async (t) => {
|
|
6
7
|
await t.step('parse example file as TIFF', async () => {
|
|
@@ -53,3 +54,5 @@ Deno.test('parseTIFF', async (t) => {
|
|
|
53
54
|
})
|
|
54
55
|
})
|
|
55
56
|
})
|
|
57
|
+
|
|
58
|
+
testAsyncFileAccess('Test file access errors for parseTIFF', parseTIFF)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Ome, Tiff } from '@bids/schema/context'
|
|
6
6
|
import * as XML from '@libs/xml'
|
|
7
7
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
8
|
+
import { readBytes } from './access.ts'
|
|
8
9
|
|
|
9
10
|
function getImageDescription(
|
|
10
11
|
dataview: DataView<ArrayBuffer>,
|
|
@@ -44,7 +45,7 @@ export async function parseTIFF(
|
|
|
44
45
|
file: BIDSFile,
|
|
45
46
|
OME: boolean,
|
|
46
47
|
): Promise<{ tiff?: Tiff; ome?: Ome }> {
|
|
47
|
-
const buf = await
|
|
48
|
+
const buf = await readBytes(file, 4096)
|
|
48
49
|
const dataview = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
49
50
|
const magic = dataview.getUint16(0, true)
|
|
50
51
|
const littleEndian = magic === 0x4949
|
|
@@ -6,9 +6,17 @@ import {
|
|
|
6
6
|
assertStrictEquals,
|
|
7
7
|
} from '@std/assert'
|
|
8
8
|
import { pathToFile } from './filetree.ts'
|
|
9
|
+
import { BIDSFileDeno } from './deno.ts'
|
|
9
10
|
import { loadTSV, loadTSVGZ } from './tsv.ts'
|
|
10
11
|
import { streamFromString } from '../tests/utils.ts'
|
|
11
12
|
import { ColumnsMap } from '../types/columns.ts'
|
|
13
|
+
import { testAsyncFileAccess } from './access.test.ts'
|
|
14
|
+
|
|
15
|
+
function compressedStreamFromString(str: string): ReadableStream<Uint8Array<ArrayBuffer>> {
|
|
16
|
+
return streamFromString(str).pipeThrough(new CompressionStream('gzip')) as ReadableStream<
|
|
17
|
+
Uint8Array<ArrayBuffer>
|
|
18
|
+
>
|
|
19
|
+
}
|
|
12
20
|
|
|
13
21
|
Deno.test('TSV loading', async (t) => {
|
|
14
22
|
await t.step('Empty file produces empty map', async () => {
|
|
@@ -175,6 +183,17 @@ Deno.test('TSV loading', async (t) => {
|
|
|
175
183
|
}
|
|
176
184
|
})
|
|
177
185
|
|
|
186
|
+
await t.step('Raises issue on non utf-8', async () => {
|
|
187
|
+
const file = new BIDSFileDeno('', './tests/data/iso8859.tsv')
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await loadTSV(file)
|
|
191
|
+
assert(false, 'Expected error')
|
|
192
|
+
} catch (e: any) {
|
|
193
|
+
assertObjectMatch(e, { code: 'INVALID_FILE_ENCODING' })
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
178
197
|
// Tests will have populated the memoization cache
|
|
179
198
|
loadTSV.cache.clear()
|
|
180
199
|
})
|
|
@@ -182,7 +201,7 @@ Deno.test('TSV loading', async (t) => {
|
|
|
182
201
|
Deno.test('TSVGZ loading', async (t) => {
|
|
183
202
|
await t.step('No header and empty file produces empty map', async () => {
|
|
184
203
|
const file = pathToFile('/empty.tsv.gz')
|
|
185
|
-
file.stream =
|
|
204
|
+
file.stream = compressedStreamFromString('')
|
|
186
205
|
|
|
187
206
|
const map = await loadTSVGZ(file, [])
|
|
188
207
|
// map.size looks for a column called map, so work around it
|
|
@@ -191,7 +210,7 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
191
210
|
|
|
192
211
|
await t.step('Empty file produces header-only map', async () => {
|
|
193
212
|
const file = pathToFile('/empty.tsv.gz')
|
|
194
|
-
file.stream =
|
|
213
|
+
file.stream = compressedStreamFromString('')
|
|
195
214
|
|
|
196
215
|
const map = await loadTSVGZ(file, ['a', 'b', 'c'])
|
|
197
216
|
assertEquals(map.a, [])
|
|
@@ -201,7 +220,7 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
201
220
|
|
|
202
221
|
await t.step('Single column file produces single column maps', async () => {
|
|
203
222
|
const file = pathToFile('/single_column.tsv')
|
|
204
|
-
file.stream =
|
|
223
|
+
file.stream = compressedStreamFromString('1\n2\n3\n')
|
|
205
224
|
|
|
206
225
|
const map = await loadTSVGZ(file, ['a'])
|
|
207
226
|
assertEquals(map.a, ['1', '2', '3'])
|
|
@@ -209,7 +228,7 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
209
228
|
|
|
210
229
|
await t.step('Mismatched header length throws issue', async () => {
|
|
211
230
|
const file = pathToFile('/single_column.tsv.gz')
|
|
212
|
-
file.stream =
|
|
231
|
+
file.stream = compressedStreamFromString('1\n2\n3\n')
|
|
213
232
|
|
|
214
233
|
try {
|
|
215
234
|
await loadTSVGZ(file, ['a', 'b'])
|
|
@@ -220,7 +239,7 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
220
239
|
|
|
221
240
|
await t.step('Missing final newline is ignored', async () => {
|
|
222
241
|
const file = pathToFile('/missing_newline.tsv.gz')
|
|
223
|
-
file.stream =
|
|
242
|
+
file.stream = compressedStreamFromString('1\n2\n3')
|
|
224
243
|
|
|
225
244
|
const map = await loadTSVGZ(file, ['a'])
|
|
226
245
|
assertEquals(map.a, ['1', '2', '3'])
|
|
@@ -228,7 +247,7 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
228
247
|
|
|
229
248
|
await t.step('Empty row throws issue', async () => {
|
|
230
249
|
const file = pathToFile('/empty_row.tsv.gz')
|
|
231
|
-
file.stream =
|
|
250
|
+
file.stream = compressedStreamFromString('1\t2\t3\n\n4\t5\t6\n')
|
|
232
251
|
|
|
233
252
|
try {
|
|
234
253
|
await loadTSVGZ(file, ['a', 'b', 'c'])
|
|
@@ -253,37 +272,38 @@ Deno.test('TSVGZ loading', async (t) => {
|
|
|
253
272
|
// Use 1500 to avoid overlap with default initial capacity
|
|
254
273
|
const headers = ['a', 'b', 'c']
|
|
255
274
|
const text = '1\t2\t3\n'.repeat(1500)
|
|
256
|
-
file.stream =
|
|
275
|
+
file.stream = compressedStreamFromString(text)
|
|
257
276
|
|
|
258
277
|
let map = await loadTSVGZ(file, headers, 0)
|
|
259
278
|
assertEquals(map.a, [])
|
|
260
279
|
assertEquals(map.b, [])
|
|
261
280
|
assertEquals(map.c, [])
|
|
262
281
|
|
|
263
|
-
file.stream =
|
|
282
|
+
file.stream = compressedStreamFromString(text)
|
|
264
283
|
map = await loadTSVGZ(file, headers, 1)
|
|
265
284
|
assertEquals(map.a, ['1'])
|
|
266
285
|
assertEquals(map.b, ['2'])
|
|
267
286
|
assertEquals(map.c, ['3'])
|
|
268
287
|
|
|
269
|
-
file.stream =
|
|
288
|
+
file.stream = compressedStreamFromString(text)
|
|
270
289
|
map = await loadTSVGZ(file, headers, 2)
|
|
271
290
|
assertEquals(map.a, ['1', '1'])
|
|
272
291
|
assertEquals(map.b, ['2', '2'])
|
|
273
292
|
assertEquals(map.c, ['3', '3'])
|
|
274
293
|
|
|
275
|
-
file.stream =
|
|
294
|
+
file.stream = compressedStreamFromString(text)
|
|
276
295
|
map = await loadTSVGZ(file, headers, -1)
|
|
277
296
|
assertEquals(map.a, Array(1500).fill('1'))
|
|
278
297
|
assertEquals(map.b, Array(1500).fill('2'))
|
|
279
298
|
assertEquals(map.c, Array(1500).fill('3'))
|
|
280
299
|
|
|
281
300
|
// Check that maxRows does not truncate shorter files
|
|
282
|
-
file.stream =
|
|
301
|
+
file.stream = compressedStreamFromString('1\t2\t3\n4\t5\t6\n7\t8\t9\n')
|
|
283
302
|
map = await loadTSVGZ(file, headers, 4)
|
|
284
303
|
assertEquals(map.a, ['1', '4', '7'])
|
|
285
304
|
assertEquals(map.b, ['2', '5', '8'])
|
|
286
305
|
assertEquals(map.c, ['3', '6', '9'])
|
|
287
306
|
})
|
|
288
|
-
|
|
289
307
|
})
|
|
308
|
+
|
|
309
|
+
testAsyncFileAccess('Test file access errors for loadTSV', loadTSV)
|
|
@@ -7,6 +7,8 @@ import { ColumnsMap } from '../types/columns.ts'
|
|
|
7
7
|
import type { BIDSFile } from '../types/filetree.ts'
|
|
8
8
|
import { filememoizeAsync } from '../utils/memoize.ts'
|
|
9
9
|
import { createUTF8Stream } from './streams.ts'
|
|
10
|
+
import { openStream } from './access.ts'
|
|
11
|
+
import { BIDSFileDeno } from './deno.ts'
|
|
10
12
|
|
|
11
13
|
async function loadColumns(
|
|
12
14
|
reader: ReadableStreamDefaultReader<string>,
|
|
@@ -55,9 +57,9 @@ export async function loadTSVGZ(
|
|
|
55
57
|
headers: string[],
|
|
56
58
|
maxRows: number = -1,
|
|
57
59
|
): Promise<ColumnsMap> {
|
|
58
|
-
const reader = file
|
|
60
|
+
const reader = openStream(file)
|
|
59
61
|
.pipeThrough(new DecompressionStream('gzip'))
|
|
60
|
-
.pipeThrough(createUTF8Stream())
|
|
62
|
+
.pipeThrough(createUTF8Stream({ fatal: true }))
|
|
61
63
|
.pipeThrough(new TextLineStream())
|
|
62
64
|
.getReader()
|
|
63
65
|
|
|
@@ -75,8 +77,8 @@ export async function loadTSVGZ(
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
async function _loadTSV(file: BIDSFile, maxRows: number = -1): Promise<ColumnsMap> {
|
|
78
|
-
const reader = file
|
|
79
|
-
.pipeThrough(createUTF8Stream())
|
|
80
|
+
const reader = openStream(file)
|
|
81
|
+
.pipeThrough(createUTF8Stream({ fatal: true }))
|
|
80
82
|
.pipeThrough(new TextLineStream())
|
|
81
83
|
.getReader()
|
|
82
84
|
|
|
@@ -130,7 +130,8 @@ export const bidsIssues: IssueDefinitionRecord = {
|
|
|
130
130
|
},
|
|
131
131
|
TSV_PSEUDO_AGE_DEPRECATED: {
|
|
132
132
|
severity: 'warning',
|
|
133
|
-
reason:
|
|
133
|
+
reason:
|
|
134
|
+
'Use of the value "89+" in column "age" is deprecated. Use 89 for all ages 89 and over.',
|
|
134
135
|
},
|
|
135
136
|
INVALID_GZIP: {
|
|
136
137
|
severity: 'error',
|
|
@@ -198,6 +199,11 @@ export const bidsIssues: IssueDefinitionRecord = {
|
|
|
198
199
|
severity: 'warning',
|
|
199
200
|
reason: 'The validation on this HED string returned a warning.',
|
|
200
201
|
},
|
|
202
|
+
CASE_COLLISION: {
|
|
203
|
+
severity: 'error',
|
|
204
|
+
reason: 'Files with the same name but different casing have been found.',
|
|
205
|
+
},
|
|
206
|
+
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
export const nonSchemaIssues = { ...bidsIssues }
|