@vzn/vx 0.0.539 → 0.0.541

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.539",
3
+ "version": "0.0.541",
4
4
  "description": "The fastest task runner and build cache for JavaScript monorepos.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,10 +21,10 @@
21
21
  "node": ">=18"
22
22
  },
23
23
  "optionalDependencies": {
24
- "@vzn/vx-linux-x64": "0.0.539",
25
- "@vzn/vx-linux-arm64": "0.0.539",
26
- "@vzn/vx-darwin-x64": "0.0.539",
27
- "@vzn/vx-darwin-arm64": "0.0.539"
24
+ "@vzn/vx-linux-x64": "0.0.541",
25
+ "@vzn/vx-linux-arm64": "0.0.541",
26
+ "@vzn/vx-darwin-x64": "0.0.541",
27
+ "@vzn/vx-darwin-arm64": "0.0.541"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/select.ts CHANGED
@@ -356,6 +356,14 @@ export async function resolveFilters(
356
356
  if (unmatched.length === 0 && emptyWalks.length > 0) {
357
357
  return { error: `no projects selected: ${emptyWalks.join('; ')}` }
358
358
  }
359
+ // Every pattern matched and an exclusion took them all back: "no
360
+ // projects matched filter(s): one, !one" read as a typo of `one`.
361
+ const negations = parsed.filter((f) => f.negate).map((f) => f.raw)
362
+ if (unmatched.length === 0 && negations.length > 0) {
363
+ return {
364
+ error: `no projects selected: ${negations.join(', ')} excluded every project the other filters matched`,
365
+ }
366
+ }
359
367
  // One line, not a warning per pattern and then an error saying the same:
360
368
  // the patterns are in the error, and the nearest project name is the
361
369
  // hint a typo needs.
@@ -183,6 +183,7 @@ export async function applyGraphHooks(
183
183
  for (const plugin of plugins) {
184
184
  if (plugin.graph === undefined) continue
185
185
  await safe(plugin, 'graph', () => plugin.graph!(nodes, ctx))
186
+ await safe(plugin, 'graph', () => checkNodeShapes(nodes))
186
187
  afterEach?.(plugin)
187
188
  last = plugin
188
189
  }
@@ -190,6 +191,24 @@ export async function applyGraphHooks(
190
191
  await safe(last, 'graph', () => checkGraph(nodes, ctx.workspaceRoot))
191
192
  }
192
193
 
194
+ /**
195
+ * A hook writes the graph as plain JS: `deps = null` surfaced as "null is
196
+ * not an object", naming no task (X-15). Checked per plugin, before its
197
+ * edits are read, so the refusal names the plugin that wrote them.
198
+ */
199
+ function checkNodeShapes(nodes: Map<string, TaskNode>): void {
200
+ const what = (v: unknown): string => (v === null ? 'null' : typeof v)
201
+ for (const [key, node] of nodes as Map<string, unknown>) {
202
+ if (typeof node !== 'object' || node === null) {
203
+ throw new Error(`'${key}' holds ${what(node)}, not a task`)
204
+ }
205
+ const deps = (node as { deps?: unknown }).deps
206
+ if (!Array.isArray(deps)) {
207
+ throw new Error(`${key}'s deps is ${what(deps)}, not an array of task ids`)
208
+ }
209
+ }
210
+ }
211
+
193
212
  /**
194
213
  * `key` stage: every plugin may add `{ name: value }` material to every
195
214
  * task. Stored on the node as sorted `[plugin/name, value]` pairs so the