@kaisers-io/refs 0.13.0 → 0.14.0

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 (3) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/dist/refs.mjs +43 -43
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,140 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.14.0] - 2026-09-13
9
+
10
+ ### Upgrading
11
+
12
+ **A repository declaring a workspace pattern like `packages/**` now reports every member behind
13
+ it.** Those patterns were refused outright, which also stood the unregistered-package pass down for
14
+ the whole ref — so a ref that reported nothing for months may suddenly name hundreds of packages.
15
+ They are not new; they were invisible. `refs doctor` prints the first ten and a count of the rest,
16
+ `refs doctor --json` carries all of them, and
17
+ `refs edit --package=<name> --decline --path=<path> <ref>` records the ones you do not want so they
18
+ stop coming back.
19
+
20
+ ### Added
21
+
22
+ - **A workspace pattern that matches at more than one depth is expanded.** `packages/**/*` — the
23
+ most common pnpm spelling — was reported as `unsupported_pattern`, which also made the whole scan
24
+ unreliable and stood the unregistered-package pass down for that ref. A repository declaring it
25
+ got no answer at all, permanently.
26
+
27
+ Such a pattern is now walked from the deepest directory it names outright. Pruning is the
28
+ MATCHER's decision: minimatch answers "could anything below this directory still match?", so a
29
+ subtree no pattern can reach is never entered, a hidden directory is skipped where a wildcard
30
+ would not select it and walked where a pattern names it, and a package boundary does not stop
31
+ the walk — nested packages are ordinary, and both resolvers glob manifest paths rather than
32
+ stopping at one. Selection matches the manifest path for the same reason: `packages/core/**`
33
+ selects `packages/core` itself, because `**` matches zero segments before the manifest.
34
+ `node_modules` and `.git` are never walked under any pattern — a recursive pattern matches an
35
+ installed dependency's path as a string, and reporting those would be reporting a repository's
36
+ dependencies as its own packages.
37
+
38
+ The whole scan runs under one budget shared by every pattern — 32 levels deep, 20 000
39
+ directories, 200 000 entries — because patterns overlap, and a per-pattern budget would charge
40
+ the same tree once per declaration. Reaching a limit is not itself a failure; having to leave a
41
+ subtree that could still match unwalked is, and that is the only condition `scan_budget_exhausted`
42
+ is reported under. A directory the pattern names but the repository does not have stays silent,
43
+ which is ordinary: astro declares `smoke/**/*` and a fresh clone has no `smoke/`.
44
+
45
+ Measured against the resolvers themselves, on fresh clones: `withastro/astro` now detects 554
46
+ packages where pnpm's own workspace listing reports 554 and refs previously reported 37;
47
+ `vercel/next.js` detects 38 where pnpm reports 38; `payloadcms/payload` 53 where pnpm reports 53.
48
+ Pattern semantics were checked against `@npmcli/map-workspaces` over six pattern shapes.
49
+
50
+ - **A ref with hundreds of findings no longer prints all of them.** The same repositories that
51
+ motivated the expansion can legitimately have hundreds of workspace members, so a ref tracking
52
+ three of them would print 551 repair commands into one `doctor` line. Ten are printed, followed
53
+ by a count of what was held back — act on those and run the check again for the next batch.
54
+ `refs doctor --json` carries every finding on the check itself, uncapped: a finding no command
55
+ can repair is never cleared by acting on the ones printed before it, so a capped list would put
56
+ it permanently out of reach.
57
+
58
+ - **A drift finding you have decided against can be recorded, so it stops coming back.**
59
+ `refs doctor` reports every workspace member a checkout declares that the configuration does not
60
+ have. There was no way to say "I looked, and no": the finding returned on every run forever, so
61
+ `config-drift` sat on `warn` permanently and the next real finding arrived in a line already
62
+ being ignored. On a home tracking four monorepos, six of seven findings were decisions that had
63
+ already been made — a repository root, and four packages published nowhere.
64
+
65
+ `refs edit --package=<name> --decline --path=<path> <ref>` records the decision on the ref, as
66
+ the name AND the path. Neither identifies it alone: a name-only record would also silence a
67
+ different package that later takes the name, and a path-only one would silence whatever moves in.
68
+ The `unregistered` finding now prints this command beside the registration command, so both
69
+ answers are available where the question is asked.
70
+
71
+ What a decline does not do: it never touches a finding about a configured entry (`missing`,
72
+ `relocated`, `ambiguous`, `unverifiable`), never suppresses `discovery_incomplete`, and never
73
+ hides the other claimants of an ambiguous name. The package is reported again if it moves.
74
+ `--undecline` withdraws the decision; registering the package clears it in the same write.
75
+ `refs doctor` says how many decisions a run left unreported, so a quiet check is not a silent
76
+ one.
77
+
78
+ ## [0.13.1] - 2026-09-12
79
+
80
+ ### Upgrading
81
+
82
+ **A repository declaring a workspace pattern like `crates/*/js` will suddenly report packages refs
83
+ could not see before.** The pattern was classified as unsupported, which also made the whole scan
84
+ unreliable and stood the unregistered-package pass down for that ref. `refs doctor` may therefore
85
+ name members on a ref that reported nothing for months. They are not new; they were invisible.
86
+ Register the ones worth routing to, and leave the rest.
87
+
88
+ ### Added
89
+
90
+ - **Every drift finding that has a repair now prints it as a command.** `unregistered` has printed
91
+ a runnable `refs edit --create` since the probe shipped; `missing` and `relocated` described the
92
+ repair in prose and left the reader to construct it. The asymmetry was not deliberate — one of
93
+ the two repairs did not exist as a command at all. `refs doctor` now ends those findings with
94
+ `refs edit --package='<name>' --remove '<ref>'` and
95
+ `refs edit --package='<name>' '<ref>' 'path' '<new>'`, quoted for a shell like every other
96
+ command refs prints. A new path the configuration could not hold is still reported, without a command
97
+ that would only fail validation.
98
+
99
+ Every printed `refs edit` now attaches its option values with `=` and, where a positional would
100
+ otherwise begin with `-`, ends the options with `--`. Quoting gets a value past the shell; refs'
101
+ own parser reads it next, and `refs edit '<ref>' path '-new/pkg'` — a path `zPackagePath` accepts
102
+ — arrived correctly quoted and exited `2` with `unknown option '-new/pkg'`. The terminator is
103
+ printed only where it is needed, so appending `--json` to an ordinary printed line still works.
104
+
105
+ - **`refs edit <ref> --package <name> --remove` unregisters a package.** The counterpart to
106
+ `--create`, and the repair the `missing` finding needed: a package that left a repository's
107
+ workspaces leaves an entry behind that `refs resolve` keeps answering with, pointing at a path no
108
+ checkout has. The only instruction refs could give was to hand-edit `config.toml`. Removal
109
+ touches configuration alone and never consults a checkout — it has to work while the checkout is
110
+ stale, absent, or still carrying the directory. Removing the last entry drops the `packages`
111
+ table rather than leaving an empty one, which `add` and the drift probe read differently.
112
+
113
+ ### Fixed
114
+
115
+ - **A workspace pattern whose wildcard is not in its last segment is expanded.** `crates/*/js` —
116
+ the spelling a Rust/JS monorepo uses — was reported as `unsupported_pattern` and every package
117
+ beneath it went undetected. That pattern costs one `readdir` and a literal probe per child, the
118
+ same shape `packages/*` has, so the budget that refused it was measuring the wrong thing. Two
119
+ wildcards still mean two levels and `packages/*/nested/*` is still refused. Measured on a
120
+ repository declaring `crates/*/js` and `turbopack/crates/*/js`: 41 packages detected instead of
121
+ 37, with no diagnostics; three other monorepos detect exactly what they did before.
122
+
123
+ - **`tag_format` is detected from every tag, not from the top twenty of a refname sort.** Detection
124
+ counted formats among the first 20 tags of `git tag --sort=-version:refname`. That sort is
125
+ version-aware over the whole refname, so in a monorepo that tags per package it groups by prefix:
126
+ those twenty are one package's tags, not a sample of the repository's. Measured against two real
127
+ repositories, `refs add` proposed `create-astro@{version}` for a repository with 839
128
+ `astro@{version}` tags, and `keystatic@{version}` — a package whose last tag is `0.0.36` and whose
129
+ directory no longer exists — for one where `@keystatic/core@{version}` is the live package with 183 of
130
+ them. Both now resolve correctly, and the two repositories whose detection was already right are
131
+ unchanged.
132
+
133
+ A tag list that could not be read whole now yields no candidate at all rather than one derived
134
+ from whatever survived. `git tag` output is capped at the subprocess stream limit, and the note
135
+ saying so was written to stderr and never read — which did not matter while only twenty lines
136
+ were needed, and does once the count is a claim about all of them.
137
+
138
+ What this establishes is historical plurality, not which package is primary or still alive: a
139
+ retired package with a thousand tags still outvotes its replacement's hundred. The two-phase flow
140
+ shows the candidate for exactly that reason.
141
+
8
142
  ## [0.13.0] - 2026-09-12
9
143
 
10
144
  ### Upgrading