Skip to content

Commit f876446

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/content-translator-incremental-richtext
# Conflicts: # content-translator/CHANGELOG.md # content-translator/dev/src/seed.ts # content-translator/package.json # content-translator/pnpm-lock.yaml # content-translator/src/translate/traverseFields.ts # content-translator/src/translate/traverseRichText.ts
2 parents 7dc3544 + d0a50cc commit f876446

87 files changed

Lines changed: 11498 additions & 7281 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.

.claude/skills/bump-dependencies/SKILL.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: bump-dependencies
3-
description: Run the bundled bump-dependencies.sh to bump plugin dependencies, then walk every non-patch version change to surface breaking changes and apply any plugin-side updates needed. Use when the user says "bump dependencies", "upgrade dependencies", "update dependencies", or wants to refresh a single plugin's dependencies.
3+
description: Run the bundled bump-dependencies.sh to bump plugin dependencies, then walk every non-patch version change to surface breaking changes and apply any plugin-side updates needed. Also audits every workspace for security advisories (audit-dependencies.sh) and fixes them via override floors or direct-dependency pins. Use when the user says "bump dependencies", "upgrade dependencies", "update dependencies", "audit dependencies", "check for vulnerabilities", or wants to refresh a single plugin's dependencies.
44
---
55

66
# Bump plugin dependencies and review breaking changes
@@ -69,6 +69,52 @@ Goal: bump the dependencies via the bundled script, then for every non-patch jum
6969

7070
Don't commit. The user runs `git commit` themselves so they can review the diff.
7171

72+
## Reconcile `pnpm-workspace.yaml` overrides
73+
74+
The script bumps `package.json` and `pnpm-lock.yaml` but never touches the `overrides:` block in each plugin's `pnpm-workspace.yaml`. Overrides win during resolution, so any dependency pinned there is silently capped — `package.json` shows the new version while the lockfile keeps the old one. After the bump, audit the overrides:
75+
76+
1. **Find the mismatch.** For every dependency that appears in both the bump diff and an `overrides:` block, compare declared vs resolved:
77+
78+
```sh
79+
grep -E "^ <dep>:" <plugin>/pnpm-workspace.yaml # the pin
80+
pnpm -C <plugin> list <dep> # what actually resolved
81+
```
82+
83+
An exact pin (`next: '16.2.6'`) caps the dep; a `^`-floor (`mongoose: '^8.22.1'`) only sets a minimum and won't block an upgrade.
84+
85+
2. **Bump exact-pin dedup overrides; never remove them.** `next` is pinned to one exact version on purpose: it forces a single `next` (and therefore one React) across the plugin root, the `dev*/` apps, and the `@payloadcms/*` peers. Remove the pin and the `@payloadcms/*` multi-window peer range (`>=16.2.6 <17`) lets a transitive dep pull a second `next`, so the tree resolves two versions at once — the duplicate-deps Payload admin crash (`Cannot destructure property 'config' of useConfig()`; see the `fix-payload-duplicate-deps` skill). Bump the pin to match the new `package.json`, reinstall, and confirm one version remains:
86+
87+
```sh
88+
pnpm -C <plugin> list next | grep -oE 'next@[0-9.]+' | sort -u # must be a single line
89+
```
90+
91+
3. **Audit `^` security floors for redundancy.** A floor exists because some transitive dep once pulled a vulnerable version. Once that dep updates its own range, the floor is dead weight. Test it empirically: strip the floor lines (keep the dedup pins), reinstall every plugin, and compare each package's lowest resolved version to its floor.
92+
- natural resolution **≥ floor** → redundant, drop the override.
93+
- natural resolution **< floor** → still load-bearing, keep it (removing it puts a below-floor version back in the lockfile).
94+
95+
A downward drop after stripping is proof the floor does real work: the pre-strip lockfile held the forced safe version, so re-resolving lower means the natural range sits below the floor. A bump as large as a Payload minor rarely retires a floor — at the 3.84→3.85 bump, even `mongoose` (whose `@payloadcms/db-mongodb` consumer now requires `8.22.1` directly) still resolved `8.15.1` from another consumer, so every floor stayed. Restore the tree with `git stash` (not `git checkout -- .`, which is denied) once measured.
96+
97+
## Audit for vulnerabilities and fix them
98+
99+
A version bump is the natural moment to clear advisories. Each plugin is its own workspace, so audit per lockfile and consolidate.
100+
101+
1. **Run the consolidated audit.** The helper next to this skill runs `pnpm audit` in every folder with its own `pnpm-lock.yaml` and prints one deduplicated table (one row per advisory, with the count of affected plugins) — what a per-folder loop can't show:
102+
103+
```sh
104+
./.claude/skills/bump-dependencies/audit-dependencies.sh # any advisory → exit 1
105+
./.claude/skills/bump-dependencies/audit-dependencies.sh high # CI gate: only high+ fails
106+
```
107+
108+
The same advisory usually spans many plugins (shared Payload/tooling deps), so fix it once per package, not once per plugin.
109+
110+
2. **Prefer the root-cause fix — update the owning dependency before reaching for an override.** Run `pnpm -C <plugin> why <pkg>` to find which direct/parent dependency pulls the vulnerable version. If a newer release of that owner requires the patched range, bumping the owner clears the advisory _and_ leaves no override behind — the cleanest fix, and it tracks upstream instead of pinning against it. The bump step (`bump-dependencies.sh``pnpm up --latest`) already does this for **direct** deps, so within the full flow the survivors are usually transitive-only with an already-current owner — those genuinely need step 3/4. Reach for an override only after confirming the owner can't move (already latest, or its range still spans the vulnerable version). An override is a forceful pin: it masks the real graph and accumulates, so it's the fallback, not the default.
111+
112+
3. **Otherwise, fix with an override floor — the same mechanism as the security floors above.** For each advisory add (or raise) `pkg: '^<patched>'` in every plugin's `overrides:` block. Watch for a floor that _caps below_ the patched version: `esbuild: '^0.27.0'` excludes the `0.28.1` fix, so it has to move to `^0.28.1`, not just gain a sibling.
113+
114+
4. **When the override won't move a transitive, pin it as a direct devDependency.** pnpm applies overrides during _fresh_ resolution but will not re-resolve an already-locked transitive to satisfy a changed override — `install`, `install --force`, `dedupe`, and `update <pkg>` all leave it in place (the lockfile even records the new override next to the old resolved version). The reliable escape hatch is to add the package as a direct `devDependency` at the patched range in the plugin that pulls it; a direct dep always resolves to its declared range, and the transitive consumer dedupes onto it. Example: `vite` (pulled by `vitest`, peer `^6 || ^7 || ^8`) stayed on the vulnerable line under a `vite: '^8.0.16'` override, so `"vite": "^8.0.16"` went into chat-agent and vercel-deployments `devDependencies` next to `vitest`.
115+
116+
5. **Reinstall, re-audit to zero, then verify the toolchain.** Forcing a build-tool version is exactly where things break — `esbuild` minors (`0.x` = breaking by convention) and a `vite` major shift the transform/bundle path. After the fix, re-run the audit _and_ `typecheck` + `test` for every plugin, plus `build` for any plugin that uses the tool directly (e.g. astro's `esbuild` + `vite-plugin-dts`). Don't claim the advisories are cleared until the audit is empty and the suites are green.
117+
72118
## Heuristics for what to read carefully vs skim
73119

74120
- **Payload core (`payload`, `@payloadcms/*`)** — read every minor's release notes; this repo's plugins live or die by Payload API stability. Pay attention to field-config, plugin API, and admin component prop changes.
@@ -83,3 +129,4 @@ Goal: bump the dependencies via the bundled script, then for every non-patch jum
83129
- Script returns `FAILED` for any plugin → report and stop, don't try to fix it inline.
84130
- A breaking change requires an architectural decision (e.g. Payload removes a hook the plugin depends on) → write up the options and ask before picking one.
85131
- Peer-dependency range needs to widen or narrow to track upstream → confirm with the user; this changes the plugin's public install matrix.
132+
- An exact-pin override (e.g. `next`) needs to cross a major, or a security floor's removal can't be proven redundant by the strip-and-reinstall test → confirm with the user before changing the `overrides:` block.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/bin/bash
2+
# audit-dependencies.sh — consolidated `pnpm audit` across every plugin workspace.
3+
#
4+
# Runs `pnpm audit` in each folder that has its own pnpm-lock.yaml and prints a
5+
# single deduplicated table (one row per advisory, with the count of affected
6+
# plugins) — the Dependabot-style overview that a per-folder loop can't give.
7+
#
8+
# Usage (from the repo root):
9+
# ./.claude/skills/bump-dependencies/audit-dependencies.sh [severity]
10+
#
11+
# severity (optional): low | moderate | high | critical
12+
# Exit non-zero only when an advisory at or above this level remains.
13+
# Default: any advisory (low) fails. Use `high` for a CI gate that
14+
# tolerates dev-only moderate/low noise.
15+
16+
set -uo pipefail
17+
18+
THRESHOLD="${1:-low}"
19+
# macOS ships bash 3.2 (no associative arrays); use a case lookup.
20+
rank_of() {
21+
case "$1" in
22+
low) echo 1 ;; moderate) echo 2 ;; high) echo 3 ;; critical) echo 4 ;;
23+
*) echo 1 ;;
24+
esac
25+
}
26+
MIN_RANK="$(rank_of "$THRESHOLD")"
27+
28+
# Discover workspaces: every depth-1 dir with its own lockfile.
29+
PLUGINS=()
30+
for lock in */pnpm-lock.yaml; do
31+
[ -f "$lock" ] && PLUGINS+=("$(dirname "$lock")")
32+
done
33+
if [ ${#PLUGINS[@]} -eq 0 ]; then
34+
echo "No */pnpm-lock.yaml found. Run this from the repo root." >&2
35+
exit 2
36+
fi
37+
38+
TMPDIR_AUDIT="$(mktemp -d)"
39+
trap 'rm -f "$TMPDIR_AUDIT"/*.json 2>/dev/null; rmdir "$TMPDIR_AUDIT" 2>/dev/null' EXIT
40+
41+
for p in "${PLUGINS[@]}"; do
42+
pnpm -C "$p" audit --json > "$TMPDIR_AUDIT/$p.json" 2>/dev/null || true
43+
done
44+
45+
python3 - "$TMPDIR_AUDIT" "$MIN_RANK" "${PLUGINS[@]}" <<'PY'
46+
import sys, os, json
47+
from collections import defaultdict
48+
49+
tmp = sys.argv[1]
50+
min_rank = int(sys.argv[2])
51+
plugins = sys.argv[3:]
52+
RANK = {"low": 1, "moderate": 2, "high": 3, "critical": 4, "info": 0}
53+
54+
adv = {} # id -> advisory dict
55+
affected = defaultdict(set) # id -> {plugin}
56+
for p in plugins:
57+
path = os.path.join(tmp, f"{p}.json")
58+
try:
59+
data = json.load(open(path))
60+
except Exception:
61+
continue
62+
for aid, a in (data.get("advisories") or {}).items():
63+
adv[aid] = a
64+
affected[aid].add(p)
65+
66+
if not adv:
67+
print("\n✓ No known vulnerabilities in any workspace.")
68+
sys.exit(0)
69+
70+
rows = []
71+
for aid, a in adv.items():
72+
sev = a.get("severity", "info")
73+
patched = a.get("patched_versions", "") or ""
74+
fixable = "yes" if patched and patched not in ("<0.0.0", "") else "NO"
75+
rows.append((RANK.get(sev, 0), sev, a.get("module_name", "?"),
76+
(a.get("vulnerable_versions") or "")[:15],
77+
(patched or "")[:13], fixable, len(affected[aid]),
78+
(a.get("title") or "").strip()[:46], a.get("url", "")))
79+
rows.sort(key=lambda r: (-r[0], r[2]))
80+
81+
print(f"\n{'sev':<9}{'package':<22}{'vulnerable':<16}{'patched':<14}{'fix':<5}{'#plug':<6}title")
82+
print("-" * 100)
83+
worst = 0
84+
for rank, sev, mod, vuln, patched, fixable, n, title, url in rows:
85+
worst = max(worst, rank)
86+
print(f"{sev:<9}{mod:<22}{vuln:<16}{patched:<14}{fixable:<5}{n:<6}{title}")
87+
print(f"{'':<9}{url}")
88+
89+
tot = defaultdict(int)
90+
for r in rows:
91+
tot[r[1]] += 1
92+
print(f"\n{len(rows)} unique advisories: " + ", ".join(f"{k} {v}" for k, v in
93+
sorted(tot.items(), key=lambda kv: -RANK.get(kv[0], 0))))
94+
95+
# Exit non-zero when anything at/above the threshold remains.
96+
sys.exit(1 if worst >= min_rank else 0)
97+
PY

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ jobs:
136136
- name: Run tests (astro-payload-richtext-lexical)
137137
run: cd astro-payload-richtext-lexical && pnpm test
138138

139+
- name: Run tests (cloudinary)
140+
run: cd cloudinary && pnpm test
141+
139142
- name: Run tests (content-translator)
140143
run: cd content-translator && pnpm test
141144

admin-search/dev/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@
3838
"engines": {
3939
"node": ">=22.12.0"
4040
},
41-
"packageManager": "pnpm@11.1.1"
41+
"packageManager": "pnpm@11.8.0"
4242
}

admin-search/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,24 @@
2929
"typecheck": "tsc --noEmit"
3030
},
3131
"dependencies": {
32-
"@payloadcms/translations": "^3.84.1"
32+
"@payloadcms/translations": "^3.85.1"
3333
},
3434
"peerDependencies": {
35-
"@payloadcms/ui": "^3.84.1",
35+
"@payloadcms/ui": "^3.85.1",
3636
"next": "^15.0.0 || ^16.0.0",
37-
"payload": "^3.84.1",
38-
"react": "19.2.6",
39-
"react-dom": "19.2.6"
37+
"payload": "^3.85.1",
38+
"react": "19.2.7",
39+
"react-dom": "19.2.7"
4040
},
4141
"devDependencies": {
4242
"@payloadcms/eslint-config": "^3.28.0",
4343
"@swc/cli": "^0.8.1",
44-
"@swc/core": "^1.15.33",
45-
"@types/react": "^19.2.14",
44+
"@swc/core": "^1.15.41",
45+
"@types/react": "^19.2.17",
4646
"@types/react-dom": "^19.2.3",
4747
"copyfiles": "^2.4.1",
4848
"eslint": "^9.39.4",
49-
"prettier": "^3.8.3",
49+
"prettier": "^3.8.4",
5050
"rimraf": "^6.1.3",
5151
"typescript": "^6.0.3"
5252
},
@@ -84,5 +84,5 @@
8484
"engines": {
8585
"node": ">=22.12.0"
8686
},
87-
"packageManager": "pnpm@11.1.1"
87+
"packageManager": "pnpm@11.8.0"
8888
}

0 commit comments

Comments
 (0)