bids-validator-deno 2.2.1__tar.gz → 2.2.3__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.
Files changed (115) hide show
  1. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/PKG-INFO +1 -1
  2. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/deno.json +2 -2
  3. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/pyproject.toml +1 -1
  4. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/deno.ts +6 -5
  5. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/nifti.test.ts +4 -0
  6. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/nifti.ts +29 -23
  7. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/openers.ts +15 -0
  8. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/repo.ts +17 -15
  9. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/issues/list.ts +4 -0
  10. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/expressionLanguage.ts +16 -4
  11. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/walk.ts +2 -16
  12. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/LICENSE +0 -0
  13. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/README.md +0 -0
  14. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/pdm_build.py +0 -0
  15. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/.git-meta.json +0 -0
  16. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/bids-validator.ts +0 -0
  17. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/access.test.ts +0 -0
  18. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/access.ts +0 -0
  19. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/browser.test.ts +0 -0
  20. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/browser.ts +0 -0
  21. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/deno.test.ts +0 -0
  22. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/dwi.test.ts +0 -0
  23. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/dwi.ts +0 -0
  24. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/filetree.test.ts +0 -0
  25. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/filetree.ts +0 -0
  26. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/gzip.test.ts +0 -0
  27. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/gzip.ts +0 -0
  28. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/ignore.test.ts +0 -0
  29. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/ignore.ts +0 -0
  30. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/inheritance.test.ts +0 -0
  31. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/inheritance.ts +0 -0
  32. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/json.test.ts +0 -0
  33. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/json.ts +0 -0
  34. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/openers.test.ts +0 -0
  35. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/streams.test.ts +0 -0
  36. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/streams.ts +0 -0
  37. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/tiff.test.ts +0 -0
  38. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/tiff.ts +0 -0
  39. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/tsv.test.ts +0 -0
  40. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/files/tsv.ts +0 -0
  41. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/issues/datasetIssues.test.ts +0 -0
  42. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/issues/datasetIssues.ts +0 -0
  43. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/issues/list.test.ts +0 -0
  44. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/main.ts +0 -0
  45. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/applyRules.test.ts +0 -0
  46. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/applyRules.ts +0 -0
  47. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/associations.test.ts +0 -0
  48. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/associations.ts +0 -0
  49. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/context.test.ts +0 -0
  50. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/context.ts +0 -0
  51. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/datatypes.test.ts +0 -0
  52. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/datatypes.ts +0 -0
  53. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/entities.test.ts +0 -0
  54. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/entities.ts +0 -0
  55. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/expressionLanguage.test.ts +0 -0
  56. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/fixtures.test.ts +0 -0
  57. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/tables.test.ts +0 -0
  58. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/tables.ts +0 -0
  59. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/schema/walk.test.ts +0 -0
  60. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/setup/loadSchema.test.ts +0 -0
  61. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/setup/loadSchema.ts +0 -0
  62. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/setup/options.test.ts +0 -0
  63. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/setup/options.ts +0 -0
  64. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/setup/requestPermissions.ts +0 -0
  65. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/summary/collectSubjectMetadata.ts +0 -0
  66. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/summary/summary.test.ts +0 -0
  67. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/summary/summary.ts +0 -0
  68. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/README.md +0 -0
  69. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/bom-utf16.tsv +0 -0
  70. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/bom-utf8.json +0 -0
  71. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/generate-filenames.ts +0 -0
  72. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/bids_examples.test.ts +0 -0
  73. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/common.ts +0 -0
  74. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/derivatives.test.ts +0 -0
  75. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/empty_files.test.ts +0 -0
  76. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/hed-integration.test.ts +0 -0
  77. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/nifti_rules.test.ts +0 -0
  78. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/valid_dataset.test.ts +0 -0
  79. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/valid_filenames.test.ts +0 -0
  80. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/local/valid_headers.test.ts +0 -0
  81. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/nullReadBytes.ts +0 -0
  82. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/regression.test.ts +0 -0
  83. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/schema-expression-language.test.ts +0 -0
  84. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/simple-dataset.ts +0 -0
  85. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/tests/utils.ts +0 -0
  86. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/check.ts +0 -0
  87. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/columns.test.ts +0 -0
  88. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/columns.ts +0 -0
  89. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/filetree.ts +0 -0
  90. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/issues.ts +0 -0
  91. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/schema.ts +0 -0
  92. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/types/validation-result.ts +0 -0
  93. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/errors.ts +0 -0
  94. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/logger.test.ts +0 -0
  95. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/logger.ts +0 -0
  96. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/memoize.ts +0 -0
  97. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/objectPathHandler.ts +0 -0
  98. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/output.ts +0 -0
  99. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/utils/queue.ts +0 -0
  100. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/bids.test.ts +0 -0
  101. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/bids.ts +0 -0
  102. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/citation.test.ts +0 -0
  103. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/citation.ts +0 -0
  104. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameCase.test.ts +0 -0
  105. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameCase.ts +0 -0
  106. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameIdentify.test.ts +0 -0
  107. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameIdentify.ts +0 -0
  108. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameValidate.test.ts +0 -0
  109. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/filenameValidate.ts +0 -0
  110. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/hed.ts +0 -0
  111. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/internal/emptyFile.ts +0 -0
  112. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/internal/unusedFile.ts +0 -0
  113. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/json.ts +0 -0
  114. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/validators/validateFiles.test.ts +0 -0
  115. {bids_validator_deno-2.2.1 → bids_validator_deno-2.2.3}/src/version.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bids-validator-deno
3
- Version: 2.2.1
3
+ Version: 2.2.3
4
4
  Summary: Typescript implementation of the BIDS validator
5
5
  Keywords: BIDS,BIDS validator
6
6
  Author: bids-standard developers
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bids/validator",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "exports": {
5
5
  ".": "./src/bids-validator.ts",
6
6
  "./main": "./src/main.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "imports": {
30
30
  "@ajv": "npm:ajv@^8.17.1",
31
- "@bids/schema": "jsr:@bids/schema@~1.1.0",
31
+ "@bids/schema": "jsr:@bids/schema@~1.1.1",
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
34
  "@hed/validator": "npm:hed-validator@~4.1.4",
@@ -18,7 +18,7 @@ keywords = [
18
18
  "BIDS validator",
19
19
  ]
20
20
  dynamic = []
21
- version = "2.2.1"
21
+ version = "2.2.3"
22
22
 
23
23
  [project.license]
24
24
  text = "MIT"
@@ -6,8 +6,8 @@ import * as posix from '@std/path/posix'
6
6
  import { BIDSFile, type FileOpener, FileTree } from '../types/filetree.ts'
7
7
  import { requestReadPermission } from '../setup/requestPermissions.ts'
8
8
  import { FileIgnoreRules, readBidsIgnore } from './ignore.ts'
9
- import { FsFileOpener, HTTPOpener } from './openers.ts'
10
- import { resolveAnnexedFile } from './repo.ts'
9
+ import { FsFileOpener, HTTPOpener, NullFileOpener } from './openers.ts'
10
+ import { parseAnnexedFile, resolveAnnexedFile } from './repo.ts'
11
11
  import fs from 'node:fs'
12
12
 
13
13
  export class BIDSFileDeno extends BIDSFile {
@@ -51,12 +51,13 @@ async function _readFileTree({
51
51
  try {
52
52
  const fileInfo = await Deno.stat(fullPath)
53
53
  opener = new FsFileOpener(rootPath, thisPath, fileInfo)
54
- } catch (error) {
54
+ } catch (_) {
55
+ const { key, size, gitdir } = await parseAnnexedFile(fullPath)
55
56
  try {
56
- const { url, size } = await resolveAnnexedFile(fullPath, preferredRemote, { cache, fs })
57
+ const { url } = await resolveAnnexedFile(key, preferredRemote, { cache, fs, gitdir })
57
58
  opener = new HTTPOpener(url, size)
58
59
  } catch (_) {
59
- throw error
60
+ opener = new NullFileOpener(size)
60
61
  }
61
62
  }
62
63
  tree.files.push(new BIDSFile(thisPath, opener, ignore, tree))
@@ -96,6 +96,10 @@ Deno.test('Test extracting axis codes', async (t) => {
96
96
  const affine = [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]
97
97
  assertEquals(axisCodes(affine), ['A', 'S', 'R'])
98
98
  })
99
+ await t.step('Fail gracefully on NaNs', async () => {
100
+ const affine = [[Number.NaN, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]
101
+ assertEquals(axisCodes(affine), null)
102
+ })
99
103
  })
100
104
 
101
105
  testAsyncFileAccess('Test file access errors for loadHeader', loadHeader)
@@ -34,9 +34,9 @@ async function extract(buffer: Uint8Array, nbytes: number): Promise<Uint8Array<A
34
34
 
35
35
  export async function loadHeader(file: BIDSFile): Promise<NiftiHeader> {
36
36
  const buf = await readBytes(file, 1024)
37
+ let header
37
38
  try {
38
39
  const data = isCompressed(buf.buffer) ? await extract(buf, 540) : buf.slice(0, 540)
39
- let header
40
40
  if (isNIFTI1(data.buffer)) {
41
41
  header = new NIFTI1()
42
42
  // Truncate to 348 bytes to avoid attempting to parse extensions
@@ -48,29 +48,30 @@ export async function loadHeader(file: BIDSFile): Promise<NiftiHeader> {
48
48
  if (!header) {
49
49
  throw { code: 'NIFTI_HEADER_UNREADABLE' }
50
50
  }
51
- const ndim = header.dims[0]
52
- return {
53
- dim: header.dims,
54
- // Hack: round pixdim to 3 decimal places; schema should add rounding function
55
- pixdim: header.pixDims.map((pixdim) => Math.round(pixdim * 1000) / 1000),
56
- shape: header.dims.slice(1, ndim + 1),
57
- voxel_sizes: header.pixDims.slice(1, ndim + 1),
58
- dim_info: {
59
- freq: header.dim_info & 0x03,
60
- phase: (header.dim_info >> 2) & 0x03,
61
- slice: (header.dim_info >> 4) & 0x03,
62
- },
63
- xyzt_units: {
64
- xyz: ['unknown', 'meter', 'mm', 'um'][header.xyzt_units & 0x03],
65
- t: ['unknown', 'sec', 'msec', 'usec'][(header.xyzt_units >> 3) & 0x03],
66
- },
67
- qform_code: header.qform_code,
68
- sform_code: header.sform_code,
69
- axis_codes: axisCodes(header.affine),
70
- } as NiftiHeader
71
51
  } catch (err) {
72
52
  throw { code: 'NIFTI_HEADER_UNREADABLE' }
73
53
  }
54
+
55
+ const ndim = header.dims[0]
56
+ return {
57
+ dim: header.dims,
58
+ // Hack: round pixdim to 3 decimal places; schema should add rounding function
59
+ pixdim: header.pixDims.map((pixdim) => Math.round(pixdim * 1000) / 1000),
60
+ shape: header.dims.slice(1, ndim + 1),
61
+ voxel_sizes: header.pixDims.slice(1, ndim + 1),
62
+ dim_info: {
63
+ freq: header.dim_info & 0x03,
64
+ phase: (header.dim_info >> 2) & 0x03,
65
+ slice: (header.dim_info >> 4) & 0x03,
66
+ },
67
+ xyzt_units: {
68
+ xyz: ['unknown', 'meter', 'mm', 'um'][header.xyzt_units & 0x03],
69
+ t: ['unknown', 'sec', 'msec', 'usec'][(header.xyzt_units >> 3) & 0x03],
70
+ },
71
+ qform_code: header.qform_code,
72
+ sform_code: header.sform_code,
73
+ axis_codes: axisCodes(header.affine),
74
+ } as NiftiHeader
74
75
  }
75
76
 
76
77
  /** Vector addition */
@@ -124,13 +125,18 @@ function argMax(arr: number[]): number {
124
125
  *
125
126
  * @returns character codes describing the orientation of an image affine.
126
127
  */
127
- export function axisCodes(affine: number[][]): string[] {
128
+ export function axisCodes(affine: number[][]): string[] | null {
128
129
  // This function is an extract of the Python function transforms3d.affines.decompose44
129
130
  // (https://github.com/matthew-brett/transforms3d/blob/6a43a98/transforms3d/affines.py#L10-L153)
130
131
  //
131
132
  // As an optimization, this only orthogonalizes the basis,
132
133
  // and does not normalize to unit vectors.
133
134
 
135
+ // Bad qforms result in NaNs in the rotation matrix
136
+ if (affine.some((row) => row.some((val) => !Number.isFinite(val)))) {
137
+ return null
138
+ }
139
+
134
140
  // Operate on columns, which are the cosines that project input coordinates onto output axes
135
141
  const [cosX, cosY, cosZ] = [0, 1, 2].map((j) => [0, 1, 2].map((i) => affine[i][j]))
136
142
 
@@ -148,7 +154,7 @@ export function axisCodes(affine: number[][]): string[] {
148
154
 
149
155
  // Check that indices are 0, 1 and 2 in some order
150
156
  if (maxIndices.toSorted().some((idx, i) => idx !== i)) {
151
- throw { key: 'AMBIGUOUS_AFFINE' }
157
+ throw { code: 'AMBIGUOUS_AFFINE' }
152
158
  }
153
159
 
154
160
  // Positive/negative codes for each world axis
@@ -126,3 +126,18 @@ export class HTTPOpener implements FileOpener {
126
126
  return new Uint8Array(await response.arrayBuffer())
127
127
  }
128
128
  }
129
+
130
+ export class NullFileOpener implements FileOpener {
131
+ size: number
132
+ constructor(size = 0) {
133
+ this.size = size
134
+ }
135
+ stream = async () =>
136
+ new ReadableStream({
137
+ start(controller) {
138
+ controller.close()
139
+ },
140
+ })
141
+ text = async () => ''
142
+ readBytes = async (size: number, offset?: number) => new Uint8Array()
143
+ }
@@ -84,27 +84,29 @@ export async function readRemotes(options: any): Promise<Record<string, Record<s
84
84
  return byUUID
85
85
  }
86
86
 
87
- /**
88
- * Resolve an annexed file location to an HTTP URL, if a public S3 remote is available
89
- */
90
- export async function resolveAnnexedFile(
87
+ export async function parseAnnexedFile(
91
88
  path: string,
92
- remote?: string,
93
- options?: any,
94
- ): Promise<{ url: string; size: number }> {
95
- // path is known to be a symlink
89
+ ): Promise<{ key: string; size: number; gitdir: string }> {
96
90
  const target = await Deno.readLink(path)
97
91
  const { dir, base } = parse(target)
98
92
 
99
- if (!options?.gitdir) {
100
- const dirs = dir.split(SEPARATOR_PATTERN)
101
- const gitdir = join(dirname(path), ...dirs.slice(0, dirs.indexOf('.git') + 1))
102
- options = { ...options, gitdir }
103
- }
93
+ const dirs = dir.split(SEPARATOR_PATTERN)
94
+ const gitdir = join(dirname(path), ...dirs.slice(0, dirs.indexOf('.git') + 1))
104
95
 
105
96
  const size = +base.match(annexKeyRegex)?.groups?.size!
106
97
 
107
- const rmet = await readRmet(base, options)
98
+ return { key: base, size, gitdir }
99
+ }
100
+
101
+ /**
102
+ * Resolve an annexed file location to an HTTP URL, if a public S3 remote is available
103
+ */
104
+ export async function resolveAnnexedFile(
105
+ key: string,
106
+ remote?: string,
107
+ options?: any,
108
+ ): Promise<{ url: string }> {
109
+ const rmet = await readRmet(key, options)
108
110
  const remotes = await readRemotes(options)
109
111
  let uuid: string
110
112
  if (remote) {
@@ -137,5 +139,5 @@ export async function resolveAnnexedFile(
137
139
  const metadata = rmet[uuid]
138
140
  const url = `${publicurl}/${metadata.path}?versionId=${metadata.version}`
139
141
 
140
- return { url, size }
142
+ return { url }
141
143
  }
@@ -203,6 +203,10 @@ export const bidsIssues: IssueDefinitionRecord = {
203
203
  severity: 'error',
204
204
  reason: 'Files with the same name but different casing have been found.',
205
205
  },
206
+ INVALID_FILE_ENCODING: {
207
+ severity: 'error',
208
+ reason: 'File encoding is not valid UTF-8.',
209
+ },
206
210
  }
207
211
 
208
212
  export const nonSchemaIssues = { ...bidsIssues }
@@ -79,11 +79,23 @@ export const expressionFunctions = {
79
79
  }
80
80
  return typeof operand
81
81
  },
82
- min: (list: number[]): number | null => {
83
- return list != null ? Math.min(...list.map(Number).filter((x) => !isNaN(x))) : null
82
+ min: (list: number | number[]): number | null => {
83
+ if (list == null) {
84
+ return null
85
+ }
86
+ if (!Array.isArray(list)) {
87
+ list = [list]
88
+ }
89
+ return Math.min(...list.map(Number).filter((x) => !isNaN(x)))
84
90
  },
85
- max: (list: number[]): number | null => {
86
- return list != null ? Math.max(...list.map(Number).filter((x) => !isNaN(x))) : null
91
+ max: (list: number | number[]): number | null => {
92
+ if (list == null) {
93
+ return null
94
+ }
95
+ if (!Array.isArray(list)) {
96
+ list = [list]
97
+ }
98
+ return Math.max(...list.map(Number).filter((x) => !isNaN(x)))
87
99
  },
88
100
  length: <T>(list: T[]): number | null => {
89
101
  if (Array.isArray(list) || typeof list == 'string') {
@@ -1,6 +1,7 @@
1
1
  import { BIDSContext, type BIDSContextDataset } from './context.ts'
2
- import { BIDSFile, FileOpener, FileTree } from '../types/filetree.ts'
2
+ import { BIDSFile, type FileTree } from '../types/filetree.ts'
3
3
  import type { DatasetIssues } from '../issues/datasetIssues.ts'
4
+ import { NullFileOpener } from '../files/openers.ts'
4
5
  import { loadTSV } from '../files/tsv.ts'
5
6
  import { loadJSON } from '../files/json.ts'
6
7
  import { queuedAsyncIterator } from '../utils/queue.ts'
@@ -14,21 +15,6 @@ function* quickWalk(dir: FileTree): Generator<BIDSFile> {
14
15
  }
15
16
  }
16
17
 
17
- class NullFileOpener implements FileOpener {
18
- size: number
19
- constructor(size = 0) {
20
- this.size = size
21
- }
22
- stream = async () =>
23
- new ReadableStream({
24
- start(controller) {
25
- controller.close()
26
- },
27
- })
28
- text = async () => ''
29
- readBytes = async (size: number, offset?: number) => new Uint8Array()
30
- }
31
-
32
18
  function pseudoFile(dir: FileTree, opaque: boolean): BIDSFile {
33
19
  return new BIDSFile(
34
20
  // Use a trailing slash to indicate directory