@vltpkg/query 1.0.0-rc.32 → 1.0.0-rc.34

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 (215) hide show
  1. package/dist/index.js +10 -6
  2. package/dist/parser.js +2 -1
  3. package/dist/pseudo/hostname.js +3 -1
  4. package/dist/pseudo/malware.d.ts +3 -16
  5. package/dist/pseudo/malware.js +11 -171
  6. package/dist/pseudo/scripts.js +1 -1
  7. package/dist/pseudo/vuln.d.ts +22 -0
  8. package/dist/pseudo/vuln.js +220 -0
  9. package/dist/pseudo.js +3 -3
  10. package/dist/types.d.ts +3 -2
  11. package/package.json +19 -10
  12. package/skills/dss-query/REFERENCE.md +123 -0
  13. package/skills/dss-query/SKILL.md +190 -0
  14. package/skills/dss-query/evals/README.md +79 -0
  15. package/skills/dss-query/evals/evals.json +89 -0
  16. package/skills/dss-query/evals/grade.mjs +480 -0
  17. package/src/attribute.ts +186 -0
  18. package/src/combinator.ts +132 -0
  19. package/src/id.ts +46 -0
  20. package/src/index.ts +620 -0
  21. package/src/parser.ts +122 -0
  22. package/src/pseudo/abandoned.ts +9 -0
  23. package/src/pseudo/attr.ts +92 -0
  24. package/src/pseudo/built.ts +19 -0
  25. package/src/pseudo/confused.ts +29 -0
  26. package/src/pseudo/cve.ts +93 -0
  27. package/src/pseudo/cwe.ts +97 -0
  28. package/src/pseudo/debug.ts +9 -0
  29. package/src/pseudo/deprecated.ts +9 -0
  30. package/src/pseudo/dev.ts +18 -0
  31. package/src/pseudo/diff.ts +90 -0
  32. package/src/pseudo/dist.ts +144 -0
  33. package/src/pseudo/dynamic.ts +9 -0
  34. package/src/pseudo/empty.ts +15 -0
  35. package/src/pseudo/entropic.ts +9 -0
  36. package/src/pseudo/env.ts +6 -0
  37. package/src/pseudo/eval.ts +9 -0
  38. package/src/pseudo/fs.ts +9 -0
  39. package/src/pseudo/helpers.ts +106 -0
  40. package/src/pseudo/host.ts +111 -0
  41. package/src/pseudo/hostname.ts +156 -0
  42. package/src/pseudo/license.ts +134 -0
  43. package/src/pseudo/link.ts +30 -0
  44. package/src/pseudo/malware.ts +41 -0
  45. package/src/pseudo/minified.ts +9 -0
  46. package/src/pseudo/missing.ts +16 -0
  47. package/src/pseudo/native.ts +9 -0
  48. package/src/pseudo/network.ts +9 -0
  49. package/src/pseudo/obfuscated.ts +9 -0
  50. package/src/pseudo/optional.ts +18 -0
  51. package/src/pseudo/outdated.ts +305 -0
  52. package/src/pseudo/overridden.ts +20 -0
  53. package/src/pseudo/path.ts +146 -0
  54. package/src/pseudo/peer.ts +18 -0
  55. package/src/pseudo/prerelease.ts +47 -0
  56. package/src/pseudo/private.ts +19 -0
  57. package/src/pseudo/prod.ts +18 -0
  58. package/src/pseudo/published.ts +248 -0
  59. package/src/pseudo/registry.ts +29 -0
  60. package/src/pseudo/root.ts +19 -0
  61. package/src/pseudo/scanned.ts +20 -0
  62. package/src/pseudo/score.ts +186 -0
  63. package/src/pseudo/scripts.ts +54 -0
  64. package/src/pseudo/semver.ts +320 -0
  65. package/src/pseudo/severity.ts +231 -0
  66. package/src/pseudo/shell.ts +9 -0
  67. package/src/pseudo/shrinkwrap.ts +9 -0
  68. package/src/pseudo/spec.ts +131 -0
  69. package/src/pseudo/squat.ts +225 -0
  70. package/src/pseudo/suspicious.ts +9 -0
  71. package/src/pseudo/tracker.ts +9 -0
  72. package/src/pseudo/trivial.ts +9 -0
  73. package/src/pseudo/type.ts +26 -0
  74. package/src/pseudo/undesirable.ts +9 -0
  75. package/src/pseudo/unknown.ts +9 -0
  76. package/src/pseudo/unmaintained.ts +9 -0
  77. package/src/pseudo/unpopular.ts +9 -0
  78. package/src/pseudo/unstable.ts +9 -0
  79. package/src/pseudo/vuln.ts +273 -0
  80. package/src/pseudo/workspace.ts +22 -0
  81. package/src/pseudo.ts +413 -0
  82. package/src/types.ts +165 -0
  83. package/tap-snapshots/test/attribute.ts.test.cjs +304 -0
  84. package/tap-snapshots/test/combinator.ts.test.cjs +333 -0
  85. package/tap-snapshots/test/id.ts.test.cjs +118 -0
  86. package/tap-snapshots/test/parser.ts.test.cjs +344 -0
  87. package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +18 -0
  88. package/tap-snapshots/test/pseudo/attr.ts.test.cjs +161 -0
  89. package/tap-snapshots/test/pseudo/built.ts.test.cjs +70 -0
  90. package/tap-snapshots/test/pseudo/confused.ts.test.cjs +32 -0
  91. package/tap-snapshots/test/pseudo/cve.ts.test.cjs +49 -0
  92. package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +49 -0
  93. package/tap-snapshots/test/pseudo/debug.ts.test.cjs +18 -0
  94. package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +18 -0
  95. package/tap-snapshots/test/pseudo/dist.ts.test.cjs +30 -0
  96. package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +18 -0
  97. package/tap-snapshots/test/pseudo/empty.ts.test.cjs +50 -0
  98. package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +18 -0
  99. package/tap-snapshots/test/pseudo/env.ts.test.cjs +18 -0
  100. package/tap-snapshots/test/pseudo/eval.ts.test.cjs +18 -0
  101. package/tap-snapshots/test/pseudo/fs.ts.test.cjs +18 -0
  102. package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +18 -0
  103. package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +148 -0
  104. package/tap-snapshots/test/pseudo/license.ts.test.cjs +41 -0
  105. package/tap-snapshots/test/pseudo/link.ts.test.cjs +36 -0
  106. package/tap-snapshots/test/pseudo/malware.ts.test.cjs +22 -0
  107. package/tap-snapshots/test/pseudo/minified.ts.test.cjs +18 -0
  108. package/tap-snapshots/test/pseudo/missing.ts.test.cjs +30 -0
  109. package/tap-snapshots/test/pseudo/native.ts.test.cjs +18 -0
  110. package/tap-snapshots/test/pseudo/network.ts.test.cjs +18 -0
  111. package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +18 -0
  112. package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +130 -0
  113. package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +75 -0
  114. package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +101 -0
  115. package/tap-snapshots/test/pseudo/private.ts.test.cjs +38 -0
  116. package/tap-snapshots/test/pseudo/published.ts.test.cjs +171 -0
  117. package/tap-snapshots/test/pseudo/registry.ts.test.cjs +68 -0
  118. package/tap-snapshots/test/pseudo/root.ts.test.cjs +24 -0
  119. package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +18 -0
  120. package/tap-snapshots/test/pseudo/score.ts.test.cjs +239 -0
  121. package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +61 -0
  122. package/tap-snapshots/test/pseudo/semver.ts.test.cjs +575 -0
  123. package/tap-snapshots/test/pseudo/severity.ts.test.cjs +95 -0
  124. package/tap-snapshots/test/pseudo/shell.ts.test.cjs +18 -0
  125. package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +18 -0
  126. package/tap-snapshots/test/pseudo/spec.ts.test.cjs +148 -0
  127. package/tap-snapshots/test/pseudo/squat.ts.test.cjs +129 -0
  128. package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +18 -0
  129. package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +18 -0
  130. package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +18 -0
  131. package/tap-snapshots/test/pseudo/type.ts.test.cjs +51 -0
  132. package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +18 -0
  133. package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +18 -0
  134. package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +18 -0
  135. package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +18 -0
  136. package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +18 -0
  137. package/tap-snapshots/test/pseudo/vuln.ts.test.cjs +208 -0
  138. package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +20 -0
  139. package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +19 -0
  140. package/tap-snapshots/test/pseudo.ts.test.cjs +1247 -0
  141. package/test/attribute.ts +266 -0
  142. package/test/combinator.ts +168 -0
  143. package/test/fixtures/graph.ts +999 -0
  144. package/test/fixtures/selector.ts +103 -0
  145. package/test/fixtures/types.ts +7 -0
  146. package/test/id.ts +102 -0
  147. package/test/index.ts +1000 -0
  148. package/test/parser.ts +117 -0
  149. package/test/pseudo/abandoned.ts +98 -0
  150. package/test/pseudo/attr.ts +298 -0
  151. package/test/pseudo/built.ts +214 -0
  152. package/test/pseudo/confused.ts +160 -0
  153. package/test/pseudo/cve.ts +249 -0
  154. package/test/pseudo/cwe.ts +255 -0
  155. package/test/pseudo/debug.ts +98 -0
  156. package/test/pseudo/deprecated.ts +98 -0
  157. package/test/pseudo/dev.ts +61 -0
  158. package/test/pseudo/diff.ts +417 -0
  159. package/test/pseudo/dist.ts +215 -0
  160. package/test/pseudo/dynamic.ts +98 -0
  161. package/test/pseudo/empty.ts +108 -0
  162. package/test/pseudo/entropic.ts +101 -0
  163. package/test/pseudo/env.ts +98 -0
  164. package/test/pseudo/eval.ts +98 -0
  165. package/test/pseudo/fs.ts +98 -0
  166. package/test/pseudo/helpers.ts +370 -0
  167. package/test/pseudo/host-context.ts +276 -0
  168. package/test/pseudo/hostname.ts +314 -0
  169. package/test/pseudo/license.ts +265 -0
  170. package/test/pseudo/link.ts +94 -0
  171. package/test/pseudo/malware.ts +184 -0
  172. package/test/pseudo/minified.ts +98 -0
  173. package/test/pseudo/missing.ts +111 -0
  174. package/test/pseudo/native.ts +98 -0
  175. package/test/pseudo/network.ts +98 -0
  176. package/test/pseudo/obfuscated.ts +98 -0
  177. package/test/pseudo/optional.ts +73 -0
  178. package/test/pseudo/outdated.ts +331 -0
  179. package/test/pseudo/overridden.ts +317 -0
  180. package/test/pseudo/path.ts +680 -0
  181. package/test/pseudo/peer.ts +101 -0
  182. package/test/pseudo/prerelease.ts +279 -0
  183. package/test/pseudo/private.ts +108 -0
  184. package/test/pseudo/prod.ts +61 -0
  185. package/test/pseudo/published.ts +557 -0
  186. package/test/pseudo/registry.ts +122 -0
  187. package/test/pseudo/root.ts +66 -0
  188. package/test/pseudo/scanned.ts +78 -0
  189. package/test/pseudo/score.ts +591 -0
  190. package/test/pseudo/scripts.ts +294 -0
  191. package/test/pseudo/semver.ts +822 -0
  192. package/test/pseudo/severity.ts +310 -0
  193. package/test/pseudo/shell.ts +98 -0
  194. package/test/pseudo/shrinkwrap.ts +98 -0
  195. package/test/pseudo/spec.ts +525 -0
  196. package/test/pseudo/squat.ts +565 -0
  197. package/test/pseudo/suspicious.ts +98 -0
  198. package/test/pseudo/tracker.ts +98 -0
  199. package/test/pseudo/trivial.ts +98 -0
  200. package/test/pseudo/type.ts +105 -0
  201. package/test/pseudo/undesirable.ts +98 -0
  202. package/test/pseudo/unknown.ts +98 -0
  203. package/test/pseudo/unmaintained.ts +98 -0
  204. package/test/pseudo/unpopular.ts +98 -0
  205. package/test/pseudo/unstable.ts +98 -0
  206. package/test/pseudo/vuln.ts +625 -0
  207. package/test/pseudo/vulnerable.ts +137 -0
  208. package/test/pseudo/workspace.ts +111 -0
  209. package/test/pseudo.ts +558 -0
  210. package/tsconfig.json +3 -0
  211. package/tsconfig.publish.json +19 -0
  212. package/tsconfig.publish.tsbuildinfo +1 -0
  213. package/typedoc.mjs +2 -0
  214. package/dist/pseudo/vulnerable.d.ts +0 -8
  215. package/dist/pseudo/vulnerable.js +0 -17
@@ -0,0 +1,266 @@
1
+ import { joinDepIDTuple } from '@vltpkg/dep-id'
2
+ import type { EdgeLike, NodeLike } from '@vltpkg/types'
3
+ import type { Attribute } from 'postcss-selector-parser'
4
+ import t from 'tap'
5
+ import {
6
+ attribute,
7
+ filterAttributes,
8
+ getManifestPropertyValues,
9
+ } from '../src/attribute.ts'
10
+ import {
11
+ getMissingNodeGraph,
12
+ getSimpleGraph,
13
+ } from './fixtures/graph.ts'
14
+ import {
15
+ copyGraphSelectionState,
16
+ getGraphSelectionState,
17
+ selectorFixture,
18
+ } from './fixtures/selector.ts'
19
+ import type { TestCase } from './fixtures/types.ts'
20
+ import type {
21
+ GraphSelectionState,
22
+ ParserState,
23
+ } from '../src/types.ts'
24
+
25
+ type ManifestLike = Record<string, any>
26
+
27
+ const testAttr = selectorFixture(attribute)
28
+
29
+ t.test('attribute', async t => {
30
+ const simpleGraph = getSimpleGraph()
31
+ const all: GraphSelectionState = {
32
+ edges: new Set<EdgeLike>(simpleGraph.edges),
33
+ nodes: new Set<NodeLike>(simpleGraph.nodes.values()),
34
+ }
35
+ const b = getGraphSelectionState(simpleGraph, 'b')
36
+ const empty: GraphSelectionState = {
37
+ edges: new Set(),
38
+ nodes: new Set(),
39
+ }
40
+
41
+ const queryToExpected = new Set<TestCase>([
42
+ [
43
+ '[version="1.0.0"]',
44
+ all,
45
+ ['my-project', 'a', 'c', 'd', 'e', 'f', '@x/y'],
46
+ ], // attribute matches all
47
+ [
48
+ '[name]',
49
+ all,
50
+ ['my-project', 'a', 'b', 'c', 'd', 'e', 'f', '@x/y'],
51
+ ], // attribute has any content
52
+ ['[nonexistingattribute]', all, []], // non existing attribute
53
+ ['[name="@x/y"]', all, ['@x/y']], // attribute matches scoped quoted
54
+ ['[name^=m]', all, ['my-project']], // attribute starts with value
55
+ ['[name^="m"]', all, ['my-project']], // attribute starts with value
56
+ ['[name^=@x]', all, ['@x/y']], // attribute starts with scope
57
+ ['[name^="@x"]', all, ['@x/y']], // attribute starts with scope
58
+ ['[name$=project]', all, ['my-project']], // attribute ends with value
59
+ ['[name$="project"]', all, ['my-project']], // attribute ends with value
60
+ ['[name~=project]', all, ['my-project']], // attribute contains word
61
+ ['[name~="project"]', all, ['my-project']], // attribute contains word
62
+ ['[name~=notfound]', all, []], // attribute contains not found
63
+ ['[name~="notfound"]', all, []], // attribute contains not found
64
+ ['[name*=pro]', all, ['my-project']], // attribute contains string
65
+ ['[name*="pro"]', all, ['my-project']], // attribute contains string
66
+ ['[name|=my]', all, ['my-project']], // attribute starts with hyphened string
67
+ ['[name|="my"]', all, ['my-project']], // attribute starts with hyphened string
68
+ ['[name|="a"]', all, ['a']], // also matches on exact match
69
+ ['[name="A"]', all, []], // case sensitive by default
70
+ ['[name="A" i]', all, ['a']], // case insensitive flag
71
+ ['[name="A" s]', all, []], // case insensitive flag
72
+ ['[name="a"]', empty, []], // no matches if starting from empty partial
73
+ ['[name="a"]', b, []], // no matches if node can't be find in partial
74
+ ['[name="b"]', b, ['b']], // matches single node found
75
+ ['[keywords=something]', all, ['c']], // matches item in array prop
76
+ ['[keywords=missing]', all, []], // can not match missing item in array
77
+ ])
78
+ const initial = {
79
+ edges: new Set(all.edges),
80
+ nodes: new Set(all.nodes),
81
+ }
82
+ for (const [query, partial, expected] of queryToExpected) {
83
+ const res = await testAttr(
84
+ query,
85
+ initial,
86
+ copyGraphSelectionState(partial),
87
+ )
88
+ t.strictSame(
89
+ res.nodes.map(i => i.name),
90
+ expected,
91
+ `query > "${query}"`,
92
+ )
93
+ t.matchSnapshot(
94
+ {
95
+ edges: res.edges.map(i => i.name).sort(),
96
+ nodes: res.nodes.map(i => i.name).sort(),
97
+ },
98
+ `query > "${query}"`,
99
+ )
100
+ }
101
+
102
+ await t.test('missing node', async t => {
103
+ const missingNodeGraph = getMissingNodeGraph()
104
+ const all: GraphSelectionState = {
105
+ edges: new Set<EdgeLike>(missingNodeGraph.edges),
106
+ nodes: new Set<NodeLike>(missingNodeGraph.nodes.values()),
107
+ }
108
+ const queryToExpected = new Set<TestCase>([
109
+ ['[name]', all, ['node-missing-project']], // root node has a name
110
+ ['[name=a]', all, []], // can't match missing node
111
+ ])
112
+ const initial = copyGraphSelectionState(all)
113
+ for (const [query, partial, expected] of queryToExpected) {
114
+ const res = await testAttr(
115
+ query,
116
+ initial,
117
+ copyGraphSelectionState(partial),
118
+ )
119
+ t.strictSame(
120
+ res.nodes.map(i => i.name),
121
+ expected,
122
+ `query > "${query}"`,
123
+ )
124
+ t.matchSnapshot(
125
+ {
126
+ edges: res.edges.map(i => i.name).sort(),
127
+ nodes: res.nodes.map(i => i.name).sort(),
128
+ },
129
+ `query > "${query}"`,
130
+ )
131
+ }
132
+ })
133
+ })
134
+
135
+ t.test('bad selector type', async t => {
136
+ await t.rejects(
137
+ testAttr(':dev'),
138
+ /Mismatching query node/,
139
+ 'should throw an error',
140
+ )
141
+ })
142
+
143
+ t.test('bad operator', async t => {
144
+ await t.rejects(
145
+ testAttr('[name==foo]'),
146
+ /Unsupported attribute operator: +=/,
147
+ 'should throw an error',
148
+ )
149
+ })
150
+
151
+ t.test('bad operator [loose mode]', async t => {
152
+ await t.resolves(
153
+ testAttr('[name==foo]', undefined, undefined, true),
154
+ 'should resolve with no error',
155
+ )
156
+ })
157
+
158
+ t.test(
159
+ 'unquotted scoped names are not supported by the parser',
160
+ async t => {
161
+ await t.rejects(
162
+ testAttr('[name=@x/y]'),
163
+ /Unexpected "\/" found./,
164
+ 'should throw an error',
165
+ )
166
+ },
167
+ )
168
+
169
+ t.test('getManifestPropertyValues', async t => {
170
+ const simpleGraph = getSimpleGraph()
171
+ const b = simpleGraph.nodes.get(
172
+ joinDepIDTuple(['registry', '', 'b@1.0.0']),
173
+ )!
174
+
175
+ t.strictSame(
176
+ getManifestPropertyValues(
177
+ b,
178
+ ['scripts', 'postinstall'],
179
+ 'postinstall',
180
+ ),
181
+ ['postinstall'],
182
+ 'should return the postinstall script value',
183
+ )
184
+
185
+ t.strictSame(
186
+ getManifestPropertyValues(b, ['contributors'], 'contributors'),
187
+ ['[object Object]'],
188
+ 'should return a stringified object value',
189
+ )
190
+
191
+ t.strictSame(
192
+ getManifestPropertyValues(b, ['contributors', 'name'], 'name'),
193
+ ['Ruy Adorno'],
194
+ 'should return the value from a array-nested object',
195
+ )
196
+
197
+ b.manifest = { foo: 1 } as ManifestLike
198
+ t.strictSame(
199
+ getManifestPropertyValues(b, ['foo'], 'foo'),
200
+ ['1'],
201
+ 'should return values as strings',
202
+ )
203
+
204
+ b.manifest = { keywords: [undefined, 'foo'] } as ManifestLike
205
+ t.strictSame(
206
+ getManifestPropertyValues(b, ['keywords'], 'keywords'),
207
+ ['', 'foo'],
208
+ 'should turn falsy values into empty strings',
209
+ )
210
+
211
+ b.manifest = 'bad manifest' as unknown as ManifestLike
212
+ t.strictSame(
213
+ getManifestPropertyValues(b, ['name'], 'name'),
214
+ undefined,
215
+ 'should not return results from a bad manifest',
216
+ )
217
+
218
+ delete b.manifest
219
+ t.strictSame(
220
+ getManifestPropertyValues(b, ['name'], 'name'),
221
+ undefined,
222
+ 'should not return results from a missing manifest',
223
+ )
224
+ })
225
+
226
+ t.test('filterAttributes', async t => {
227
+ const simpleGraph = getSimpleGraph()
228
+ const all: GraphSelectionState = {
229
+ edges: new Set<EdgeLike>(simpleGraph.edges),
230
+ nodes: new Set<NodeLike>(simpleGraph.nodes.values()),
231
+ }
232
+ const state = {
233
+ cancellable: async () => {},
234
+ collect: {
235
+ edges: new Set<EdgeLike>(),
236
+ nodes: new Set<NodeLike>(),
237
+ },
238
+ comment: '',
239
+ current: { type: 'attribute', value: 'postinstall' } as Attribute,
240
+ initial: copyGraphSelectionState(all),
241
+ partial: all,
242
+ loose: false,
243
+ importers: simpleGraph.importers,
244
+ hostContexts: new Map(),
245
+ walk: async (state: ParserState) => state,
246
+ retries: 0,
247
+ securityArchive: undefined,
248
+ signal: new AbortController().signal,
249
+ specificity: { idCounter: 0, commonCounter: 0 },
250
+ }
251
+ filterAttributes(
252
+ state,
253
+ (attr: string) => !!attr,
254
+ 'postinstall',
255
+ 'scripts',
256
+ false,
257
+ ['scripts', 'postinstall'],
258
+ )
259
+ t.matchSnapshot(
260
+ {
261
+ edges: [...state.partial.edges].map(i => i.name).sort(),
262
+ nodes: [...state.partial.nodes].map(i => i.name).sort(),
263
+ },
264
+ 'should have filtered out nodes with postinstall script only',
265
+ )
266
+ })
@@ -0,0 +1,168 @@
1
+ import t from 'tap'
2
+ import { combinator } from '../src/combinator.ts'
3
+ import { walk } from '../src/index.ts'
4
+ import { getCycleGraph, getSimpleGraph } from './fixtures/graph.ts'
5
+ import {
6
+ copyGraphSelectionState,
7
+ getGraphSelectionState,
8
+ selectorFixture,
9
+ } from './fixtures/selector.ts'
10
+ import type { TestCase } from './fixtures/types.ts'
11
+ import type { EdgeLike, NodeLike } from '@vltpkg/types'
12
+ import type { GraphSelectionState } from '../src/types.ts'
13
+
14
+ const testCombinator = selectorFixture(combinator)
15
+
16
+ t.test('combinator', async t => {
17
+ const simpleGraph = getSimpleGraph()
18
+ const all: GraphSelectionState = {
19
+ edges: new Set<EdgeLike>(simpleGraph.edges),
20
+ nodes: new Set<NodeLike>(simpleGraph.nodes.values()),
21
+ }
22
+ const root = {
23
+ edges: new Set<EdgeLike>(),
24
+ nodes: new Set<NodeLike>([simpleGraph.mainImporter]),
25
+ }
26
+ const b = getGraphSelectionState(simpleGraph, 'b')
27
+ const d = getGraphSelectionState(simpleGraph, 'd')
28
+ const bd: GraphSelectionState = {
29
+ edges: new Set<EdgeLike>([...b.edges, ...d.edges]),
30
+ nodes: new Set<NodeLike>([...b.nodes, ...d.nodes]),
31
+ }
32
+ const e = getGraphSelectionState(simpleGraph, 'e')
33
+ const f = getGraphSelectionState(simpleGraph, 'f')
34
+ const empty: GraphSelectionState = {
35
+ edges: new Set(),
36
+ nodes: new Set(),
37
+ }
38
+ const queryToExpected = new Set<TestCase>([
39
+ // child combinator
40
+ ['>', all, ['a', 'b', 'e', '@x/y', 'c', 'd', 'f']], // direct children of all nodes
41
+ ['>', root, ['a', 'b', 'e', '@x/y']], // from root
42
+ ['>', d, ['e', 'f']], // from transitive dep
43
+ ['>', bd, ['c', 'd', 'e', 'f']], // from mutiple deps
44
+ ['>', e, []], // from node with no deps
45
+ ['>', empty, []], // from empty nodes
46
+ // subsequent sibling combinator
47
+ ['~', all, ['b', 'e', '@x/y', 'a', 'd', 'c', 'f']], // siblings of all nodes
48
+ ['~', f, ['e']], // leaf node with a sibling
49
+ ['~', d, ['c']], // transitive node
50
+ ['~', e, ['a', 'b', '@x/y', 'f']], // node with siblings at multiple locations
51
+ ['~', root, []], // root has no siblings
52
+ ['~', empty, []], // sibling of empty nodes
53
+ // descendent combinator
54
+ [' ', root, ['a', 'b', 'e', '@x/y', 'c', 'd', 'f']], // from root
55
+ [' ', f, []], // leaf node
56
+ [' ', empty, []], // empty list of nodes
57
+ [' ', d, ['e', 'f']], // transitive node
58
+ [' ', b, ['c', 'd', 'e', 'f']], // direct dep
59
+ ])
60
+ const initial = copyGraphSelectionState(all)
61
+ for (const [query, partial, expected] of queryToExpected) {
62
+ const res = await testCombinator(
63
+ { value: query, type: 'combinator' },
64
+ initial,
65
+ copyGraphSelectionState(partial),
66
+ )
67
+ t.strictSame(
68
+ res.nodes.map(i => i.name),
69
+ expected,
70
+ `query > "${query}"`,
71
+ )
72
+ t.matchSnapshot(
73
+ {
74
+ edges: res.edges.map(i => i.name).sort(),
75
+ nodes: res.nodes.map(i => i.name).sort(),
76
+ },
77
+ `query > "${query}"`,
78
+ )
79
+ }
80
+
81
+ await t.test('cycle', async t => {
82
+ const cycleGraph = getCycleGraph()
83
+ const all: GraphSelectionState = {
84
+ edges: new Set<EdgeLike>(cycleGraph.edges),
85
+ nodes: new Set<NodeLike>(cycleGraph.nodes.values()),
86
+ }
87
+ const root = {
88
+ edges: new Set<EdgeLike>(),
89
+ nodes: new Set<NodeLike>([cycleGraph.mainImporter]),
90
+ }
91
+ const a = getGraphSelectionState(cycleGraph, 'a')
92
+ const b = getGraphSelectionState(cycleGraph, 'b')
93
+ const queryToExpected = new Set<TestCase>([
94
+ ['>', all, ['a', 'b']], // direct children of all nodes
95
+ ['>', a, ['b']], // direct children of a
96
+ ['>', b, ['a']], // direct children of b
97
+ [' ', root, ['a', 'b']], // descendents of :root
98
+ [' ', a, ['b', 'a']], // descendents of a
99
+ [' ', b, ['a', 'b']], // descendents of b
100
+ ['~', root, []], // siblings of :root
101
+ ['~', a, []], // siblings of a
102
+ ['~', b, []], // siblings of b
103
+ ])
104
+ const initial = copyGraphSelectionState(all)
105
+ for (const [query, partial, expected] of queryToExpected) {
106
+ const result = await testCombinator(
107
+ { value: query, type: 'combinator' },
108
+ initial,
109
+ copyGraphSelectionState(partial),
110
+ )
111
+ t.strictSame(
112
+ result.nodes.map(i => i.name),
113
+ expected,
114
+ `query > "${query}"`,
115
+ )
116
+ t.matchSnapshot(
117
+ {
118
+ edges: result.edges.map(i => i.name).sort(),
119
+ nodes: result.nodes.map(i => i.name).sort(),
120
+ },
121
+ `query > "${query}"`,
122
+ )
123
+ }
124
+ })
125
+
126
+ // space between tags are not descendent combinators
127
+ await t.test('ignore spaces between tags', async t => {
128
+ const test = selectorFixture(walk)
129
+ const result = await test(
130
+ '* { * }',
131
+ all,
132
+ copyGraphSelectionState(all),
133
+ )
134
+ t.strictSame(
135
+ result.nodes.map(i => i.name),
136
+ [...all.nodes].map(i => i.name),
137
+ 'should not treat spaces between tags as descendent combinators',
138
+ )
139
+ })
140
+ })
141
+
142
+ t.test('bad selector type', async t => {
143
+ await t.rejects(
144
+ testCombinator(':dev'),
145
+ /Mismatching query node/,
146
+ 'should throw an error',
147
+ )
148
+ })
149
+
150
+ t.test('usnupported combinator', async t => {
151
+ await t.rejects(
152
+ testCombinator({ value: '+', type: 'combinator' }),
153
+ /Unsupported combinator: \+/,
154
+ 'should throw an error',
155
+ )
156
+ })
157
+
158
+ t.test('usnupported combinator [loose mode]', async t => {
159
+ await t.resolves(
160
+ testCombinator(
161
+ { value: '+', type: 'combinator' },
162
+ undefined,
163
+ undefined,
164
+ true,
165
+ ),
166
+ 'should resolve with no error',
167
+ )
168
+ })