Skip to content

Commit dfffe5d

Browse files
committed
Release version 0.11.0: Introduced a comprehensive addon/plugin system with lifecycle hooks, enhanced support for package.yaml, and new CLI flags for global package scanning and cache management. Fixed transport fidelity for .npmrc and improved JSON output for machine readability. Updated documentation to reflect new features and ensured all changes are covered by tests, increasing test coverage to 598 passing tests. Updated CHANGELOG.md with detailed release notes.
1 parent ea487dd commit dfffe5d

89 files changed

Lines changed: 3641 additions & 169 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Dependency Freshness -- because "we'll update later" is not a strategy.
2+
name: Dependency Freshness
3+
4+
on:
5+
pull_request:
6+
branches: [main]
7+
schedule:
8+
- cron: '0 6 * * 1'
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: depfresh-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
depfresh:
20+
name: Run depfresh
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- uses: pnpm/action-setup@v4
26+
27+
- uses: actions/setup-node@v6
28+
with:
29+
node-version: 24.x
30+
cache: pnpm
31+
32+
- name: Install dependencies
33+
run: pnpm install --frozen-lockfile
34+
35+
- name: Generate depfresh JSON report
36+
run: npx --yes depfresh --output json --fail-on-outdated > depfresh-report.json
37+
continue-on-error: true
38+
39+
- name: Upload depfresh report
40+
if: always()
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: depfresh-report
44+
path: depfresh-report.json

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Semver because I'm not a psychopath.
44

5+
## [0.11.0] - 2026-02-23
6+
7+
The "close every gap or shut up" release. Ran a full codebase audit against taze, found 5 gaps, closed all 5 in one pass. Verified with real code inspection, runtime test runs, and CLI smoke checks on actual repos. Not vibes. Not a roadmap. Shipped code with 598 passing tests.
8+
9+
### Added
10+
11+
- **Addon/plugin system** -- first-class `addons` with deterministic lifecycle ordering, async hooks, and per-package write veto. Failures surface as `AddonError` (`ERR_ADDON`) with addon name + hook metadata. Debugging no longer requires telepathy.
12+
- **`package.yaml` support** -- full pipeline: discovery, resolve, write. Both `package.json` and `package.yaml` load, with deterministic same-directory precedence (YAML wins). Overrides, `pnpm.overrides`, `packageManager`, protocol-preserving rewrites, CRLF/trailing-newline preservation -- all work. Workspace-boundary parity included.
13+
- **`--global-all`** -- scans npm + pnpm + bun global packages in one run. Dedupes by package name, maps write targets back to every matching manager. `--global` still works for single-manager mode.
14+
- **`--ignore-paths`** -- exclude directories from package discovery. The flag taze had that we didn't. Now we do.
15+
- **`--refresh-cache` / `--no-cache`** -- explicit cache bypass without overloading `--force` semantics. Fresh registry metadata, no guessing.
16+
17+
### Fixed
18+
19+
- **`.npmrc` transport fidelity** -- registry requests now actually use `proxy`, `https-proxy`, `strict-ssl`, and `cafile` via `undici` transport. Previously parsed, politely ignored. HTTPS prefers `https-proxy`, HTTP prefers `proxy`, CA bundles loaded once and reused.
20+
- **Non-transient transport failures fail fast** -- broken `cafile` paths no longer waste retry attempts. `ResolveError` immediately. You're welcome.
21+
- **JSON output cleaned** -- `--output json` forces silent logging. No ANSI cursor restore leaking into stdout in non-TTY. Machine-parseable means machine-parseable.
22+
23+
### Changed
24+
25+
- **Docs parity** -- all new flags documented in README, CLI reference, and configuration docs. No "added the flag, forgot the docs" energy.
26+
27+
### Stats
28+
29+
- 60 new tests (538 -> 598). 77 test files. Build, typecheck, lint clean. Verified against taze v19.9.2 (55 tests, 13 test files). The numbers speak.
30+
531
## [0.10.1] - 2026-02-23
632

733
The "your age column was a decoration" patch. Turns out npm's abbreviated metadata endpoint doesn't return the `time` field, so the age column was rendering headers with no data like a restaurant menu with no prices. Switched to full metadata. The cache means you pay the bandwidth once per TTL, cry about it never.
@@ -296,6 +322,7 @@ First release. Wrote it from scratch because waiting for PRs to get merged in ta
296322
- TTY detection. No spinners in your CI logs. `NO_COLOR` respected.
297323
- 54 tests. More than some production apps I've seen.
298324

325+
[0.11.0]: https://github.com/vcode-sh/depfresh/releases/tag/v0.11.0
299326
[0.10.1]: https://github.com/vcode-sh/depfresh/releases/tag/v0.10.1
300327
[0.10.0]: https://github.com/vcode-sh/depfresh/releases/tag/v0.10.0
301328
[0.9.2]: https://github.com/vcode-sh/depfresh/releases/tag/v0.9.2

README.md

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Spiritual successor to [taze](https://github.com/antfu/taze) by Anthony Fu -- a
1818
- **Per-package modes** -- `packageMode` lets you set exact, glob, or regex patterns per dependency.
1919
- **Write safely** -- `--write` updates files. `--verify-command` tests each dep individually and reverts failures.
2020
- **Post-write hooks** -- `--execute`, `--install`, `--update`. Chain commands after writing.
21-
- **Global packages** -- `--global` checks npm, pnpm, and bun globals.
21+
- **Global packages** -- `--global` checks one detected manager, `--global-all` scans npm + pnpm + bun with deduped package names.
2222
- **Private registries** -- full `.npmrc` support. Scoped registries, auth tokens, env vars. Fixed from day one.
2323
- **JSON output** -- structured envelope for scripts and AI agents. No ANSI noise.
2424
- **CI mode** -- `--fail-on-outdated` exits with code 1. Plug it into your pipeline.
@@ -29,13 +29,15 @@ Spiritual successor to [taze](https://github.com/antfu/taze) by Anthony Fu -- a
2929
- **Sorting** -- 6 strategies: by diff severity, publish time, or name.
3030
- **CRLF preservation** -- Windows line endings survive the write. You're welcome.
3131
- **Nested workspace detection** -- auto-skips monorepos inside monorepos.
32-
- **Programmatic API** -- 9 exported functions, 7 lifecycle callbacks, full type safety.
32+
- **Programmatic API** -- lifecycle callbacks + addon system for custom workflows.
3333

3434
## Why
3535

3636
Because `npm outdated` gives you a table and then abandons you. Because Renovate requires a PhD in YAML. Because your AI coding assistant should be able to update your deps without you holding its hand.
3737

38-
depfresh checks every `package.json` in your project, tells you what's outdated, and optionally writes the updates. Monorepos, workspace catalogs, private registries - it handles all of it without a config file.
38+
depfresh checks every package manifest (`package.json`, `package.yaml`) in your project, tells you what's outdated, and optionally writes the updates. Monorepos, workspace catalogs, private registries - it handles all of it without a config file.
39+
40+
If both `package.yaml` and `package.json` exist in the same directory, depfresh uses `package.yaml` and skips the sibling `package.json` to avoid duplicate package entries.
3941

4042
## Install
4143

@@ -85,6 +87,16 @@ depfresh -w --verify-command "pnpm test"
8587

8688
# CI: fail if anything is outdated
8789
depfresh --fail-on-outdated
90+
91+
# Skip specific directories from recursive scan
92+
depfresh --ignore-paths "apps/legacy/**,examples/**"
93+
94+
# Bypass cache for one run (same behavior)
95+
depfresh --refresh-cache
96+
depfresh --no-cache
97+
98+
# Check globals across npm + pnpm + bun (deduped names)
99+
depfresh --global-all
88100
```
89101

90102
## CLI Flags
@@ -93,14 +105,18 @@ The top flags to get you started. Full reference with all 27+ flags: **[docs/cli
93105

94106
| Flag | Alias | Default | Description |
95107
|------|-------|---------|-------------|
96-
| `--recursive` | `-r` | `true` | Recursively search for package.json files |
108+
| `--recursive` | `-r` | `true` | Recursively search for package manifests (`package.json`, `package.yaml`) |
97109
| `--write` | `-w` | `false` | Write updated versions to package files |
98110
| `--interactive` | `-I` | `false` | Select which deps to update |
99111
| `--mode` | `-m` | `default` | Range mode: `default` `major` `minor` `patch` `latest` `newest` `next` |
100112
| `--include` | `-n` | -- | Only include packages matching regex (comma-separated) |
101113
| `--exclude` | `-x` | -- | Exclude packages matching regex (comma-separated) |
102-
| `--force` | `-f` | `false` | Force update even if version is satisfied |
103-
| `--global` | `-g` | `false` | Check global packages |
114+
| `--ignore-paths` | -- | -- | Extra ignore globs (comma-separated), merged with default ignored paths |
115+
| `--force` | `-f` | `false` | Force update even if version is satisfied (does not bypass cache) |
116+
| `--refresh-cache` | -- | `false` | Bypass cache reads and fetch fresh metadata for this run |
117+
| `--no-cache` | -- | `false` | Alias for `--refresh-cache` |
118+
| `--global` | `-g` | `false` | Check global packages for one detected package manager |
119+
| `--global-all` | -- | `false` | Check global packages across npm, pnpm, and bun with deduped package names |
104120
| `--output` | `-o` | `table` | Output format: `table` `json` |
105121
| `--execute` | `-e` | -- | Run command after writing (e.g. `"pnpm test"`) |
106122
| `--verify-command` | `-V` | -- | Run command per dep, revert on failure |
@@ -126,6 +142,22 @@ export default defineConfig({
126142
})
127143
```
128144

145+
Addon example (programmatic API):
146+
147+
```typescript
148+
import { check, resolveConfig, type depfreshAddon } from 'depfresh'
149+
150+
const addon: depfreshAddon = {
151+
name: 'audit-log',
152+
afterPackageWrite(_ctx, pkg, changes) {
153+
console.log(`updated ${pkg.name}: ${changes.length} changes`)
154+
},
155+
}
156+
157+
const options = await resolveConfig({ write: true, addons: [addon] })
158+
await check(options)
159+
```
160+
129161
Full options reference: **[docs/configuration/](docs/configuration/)**
130162

131163
## JSON Output
@@ -244,7 +276,7 @@ const options = await resolveConfig({
244276
const exitCode = await check(options)
245277
```
246278

247-
9 exported functions, 7 lifecycle callbacks, 16+ types. Full API reference: **[docs/api/](docs/api/)**
279+
Programmatic API with lifecycle callbacks, addon plugins, and full typed exports. Full reference: **[docs/api/](docs/api/)**
248280

249281
## Monorepo Support
250282

@@ -253,11 +285,11 @@ depfresh auto-detects workspace structures. No config needed.
253285
| Package Manager | Workspaces | Catalogs |
254286
|----------------|------------|----------|
255287
| pnpm | `pnpm-workspace.yaml` | `catalog:` protocol |
256-
| Bun | `workspaces` in `package.json` | `workspaces.catalog` |
257-
| Yarn | `workspaces` in `package.json` | `.yarnrc.yml` catalogs |
258-
| npm | `workspaces` in `package.json` | -- |
288+
| Bun | `workspaces` in `package.json` or `package.yaml` | `workspaces.catalog` |
289+
| Yarn | `workspaces` in `package.json` or `package.yaml` | `.yarnrc.yml` catalogs |
290+
| npm | `workspaces` in `package.json` or `package.yaml` | -- |
259291

260-
Workspace catalogs are resolved and updated in-place. Your `pnpm-workspace.yaml` catalog entries get depfreshaded alongside your `package.json` deps. No manual sync needed.
292+
Workspace catalogs are resolved and updated in-place. Your `pnpm-workspace.yaml` catalog entries get depfreshaded alongside your manifest deps (`package.json` / `package.yaml`). No manual sync needed.
261293

262294
## Private Registries
263295

@@ -271,30 +303,67 @@ depfresh reads `.npmrc` from your project and home directory. Scoped registries,
271303

272304
This was broken in taze for 4+ years. I fixed it on day one. You're welcome.
273305

274-
## What I Fixed from taze
275-
276-
Not to throw shade at taze -- it served the community well for years. But some things needed fixing, and "PR welcome" only goes so far when the PRs sit open for months.
277-
278-
| Problem | taze | depfresh |
279-
|---------|------|------|
280-
| `.npmrc` / private registries | Ignored | Full support |
281-
| Network retry | None | Exponential backoff |
282-
| Write clobber (bun catalogs) | Data loss | Single-writer architecture |
283-
| Version resolution ordering | Assumed sorted arrays | Explicit semver comparison |
284-
| Interactive mode | Flickery | @clack/prompts |
285-
| JSON output | None | Structured envelope |
286-
| Dep type filtering | None | `--deps-only` / `--dev-only` |
287-
| Config merging | deepmerge (CJS) | defu (ESM) |
288-
| npm config loading | @npmcli/config (heavy, hacky) | Direct ini parsing |
289-
| Cache | JSON file (race conditions) | SQLite with WAL mode |
306+
## depfresh vs taze
307+
308+
Verified against taze v19.9.2 (commit `31c6fe8`, 2026-01-20). Not marketing. Real code inspection, runtime test runs, CLI smoke checks on actual repos.
309+
310+
### Feature parity (both have it)
311+
312+
| Feature | taze | depfresh | Notes |
313+
|---------|------|----------|-------|
314+
| 7 range modes | yes | yes | |
315+
| Include/exclude filters | yes | yes | depfresh adds glob patterns alongside regex |
316+
| Interactive TUI | yes | yes | Both have vim keys + per-version selection |
317+
| `--cwd` | yes | yes | |
318+
| `--fail-on-outdated` | yes | yes | |
319+
| `package.yaml` support | yes | yes | |
320+
| Addon/plugin API | yes | yes | |
321+
| pnpm catalogs | yes | yes | |
322+
| Yarn catalogs | yes | yes | |
323+
| CRLF preservation | yes | yes | |
324+
| CJK width handling | yes | yes | |
325+
326+
### Where depfresh is ahead
327+
328+
| Feature | taze | depfresh |
329+
|---------|------|----------|
330+
| JSON output envelope | no ([#201](https://github.com/antfu-collective/taze/issues/201) open) | Structured envelope with schema version |
331+
| Machine-readable CLI contract | no | `--help-json` with workflows, flag relationships, schema |
332+
| `--deps-only` / `--dev-only` | no ([#101](https://github.com/antfu-collective/taze/issues/101) open) | yes |
333+
| `packageMode` precedence | buggy ([#91](https://github.com/antfu-collective/taze/issues/91) open) | Deterministic |
334+
| Global package breadth | npm + pnpm | npm + pnpm + bun (`--global-all`) |
335+
| Bun catalog writes | Bug history, data loss risk | Single-writer architecture, tested |
336+
| `.npmrc` / private registries | Ignored for years | Full support from day one |
337+
| `.npmrc` transport (proxy/TLS/CA) | Parsed, not applied | Applied via `undici` transport adapter |
338+
| Network retry | None | Exponential backoff, non-transient errors fail fast |
339+
| Cache | JSON file (race conditions) | SQLite WAL mode, memory fallback |
340+
| Verify + rollback | no | `--verify-command` tests each dep, reverts failures |
341+
| Typed error hierarchy | Limited | Structured subclasses with `.code` and `.cause` |
342+
| Structured JSON errors | no | JSON error envelope with `error.code`, `error.retryable` |
343+
| Explicit cache bypass | no | `--refresh-cache` / `--no-cache` |
344+
345+
### Where taze is ahead
346+
347+
| Area | Why |
348+
|------|-----|
349+
| Ecosystem adoption | 4,061 stars, years of trust, larger user base |
350+
| npm config edge cases | `@npmcli/config` may cover obscure auth patterns we haven't hit yet |
351+
352+
### Numbers
353+
354+
| Metric | taze v19.9.2 | depfresh v0.11.0 |
355+
|--------|-------------:|------------------:|
356+
| Test files | 13 | 77 |
357+
| Passing tests | 55 | 598 |
358+
| CLI flags | 24 | 36 |
290359

291360
## Documentation
292361

293362
The full docs, for people who read manuals before assembling furniture.
294363

295364
- **[CLI Reference](docs/cli/)** -- all 27+ flags, modes, sorting, filtering, hooks, interactive, CI, workspaces
296365
- **[Configuration](docs/configuration/)** -- config files, every option, packageMode, depFields, private registries, cache
297-
- **[Programmatic API](docs/api/)** -- exported functions, lifecycle callbacks, types, workflow examples
366+
- **[Programmatic API](docs/api/)** -- exported functions, lifecycle callbacks, addon plugins, types, workflow examples
298367
- **[Output Formats](docs/output-formats/)** -- table, JSON, exit codes, AI agent integration
299368
- **[Agent Workflows](docs/agents/README.md)** -- copy-paste quickstarts for Codex, Claude Code, and Gemini CLI
300369
- **[Integrations](docs/integrations/README.md)** -- GitHub Actions and thin MCP wrapper guidance

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Everything I know about depfresh is in here, split into files so you can pretend
1616
- [Options Reference](./configuration/options.md) -- the exhaustive list
1717
- [Workspaces](./configuration/workspaces.md) -- monorepos, catalogs, scanning
1818

19-
- **[Programmatic API](./api/)** -- Exported functions, lifecycle callbacks, types, and workflow examples. For when you want to wrap depfresh in your own tooling and take credit for it. I respect the hustle.
19+
- **[Programmatic API](./api/)** -- Exported functions, lifecycle callbacks, addon plugins, types, and workflow examples. For when you want to wrap depfresh in your own tooling and take credit for it. I respect the hustle.
2020
- [Overview](./api/overview.md) -- quick start, defaults, examples
2121
- [Functions](./api/functions.md) -- every exported function
2222
- [Types](./api/types.md) -- the type catalogue

docs/api/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ I exposed the internals. On purpose. You can `import` from `depfresh` and do wha
66

77
- **[Overview](./overview.md)** -- Quick start, defaults, workflow examples. The "just show me some code" page.
88

9-
- **[Functions](./functions.md)** -- Every exported function: `check`, `resolveConfig`, `loadPackages`, `resolvePackage`, `writePackage`, and the rest. Plus lifecycle callbacks for the control freaks.
9+
- **[Functions](./functions.md)** -- Every exported function: `check`, `resolveConfig`, `loadPackages`, `resolvePackage`, `writePackage`, and the rest. Includes lifecycle callbacks and addon plugin hooks.
1010

11-
- **[Types](./types.md)** -- The full type catalogue. `depfreshOptions`, `PackageMeta`, `ResolvedDepChange`, and 15 other things your editor wants to autocomplete.
11+
- **[Types](./types.md)** -- The full type catalogue. `depfreshOptions`, `depfreshAddon`, `PackageMeta`, `ResolvedDepChange`, and everything your editor wants to autocomplete.
1212

13-
- **[Errors](./errors.md)** -- Structured error classes: `RegistryError`, `CacheError`, `ConfigError`, `WriteError`, `ResolveError`. All extend `depfreshError`, all have stable codes.
13+
- **[Errors](./errors.md)** -- Structured error classes: `RegistryError`, `CacheError`, `ConfigError`, `WriteError`, `ResolveError`, `AddonError`. All extend `depfreshError`, all have stable codes.

docs/api/errors.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All errors thrown by depfresh extend `depfreshError`, which gives you a stable `
55
```ts
66
import {
77
depfreshError,
8+
AddonError,
89
RegistryError,
910
CacheError,
1011
ConfigError,
@@ -20,6 +21,7 @@ import {
2021
| `depfreshError` | (base) | Abstract base. Use `instanceof depfreshError` to catch everything depfresh throws. |
2122
| `RegistryError` | `ERR_REGISTRY` | HTTP errors from the npm/JSR registry. Has `.status` (number) and `.url` (string). 4xx errors don't retry. 5xx errors do. |
2223
| `CacheError` | `ERR_CACHE` | SQLite failures, corrupt entries, connection issues. depfresh logs and falls back to memory cache -- you'll only see this if you're using the cache API directly. |
24+
| `AddonError` | `ERR_ADDON` | Addon hook failures. Includes `.addon` and `.hook` to identify the failing plugin and lifecycle stage. |
2325
| `ConfigError` | `ERR_CONFIG` | Invalid config file, malformed regex patterns in `include`/`exclude`, bad `packageMode` entries. Thrown during `resolveConfig()` or `parseDependencies()`. |
2426
| `WriteError` | `ERR_WRITE` | File system failures during package writes. Permission denied, disk full, the usual suspects. |
2527
| `ResolveError` | `ERR_RESOLVE` | Network timeouts, DNS failures, fetch errors that aren't HTTP status codes. The "something went wrong between you and the registry" bucket. |

0 commit comments

Comments
 (0)