Skip to content

build(deps-dev): upgrade every dependency to its latest safe version - #219

Merged
chhoumann merged 31 commits into
masterfrom
chore/deps-upgrade-2026-10
Oct 24, 2026
Merged

chhoumann merged 31 commits into
masterfrom
chore/deps-upgrade-2026-10

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Security sweep of every dependency in the repo: npm packages, the lockfiles, GitHub Actions, the shared release workflows, the docs site and the mobile harness. It moves each to its latest safe release and removes what we barely used. Nothing in the shipped plugin changes behavior: main.js differs from master only by Svelte's 5.57.1 to 5.57.2 runtime patch and one refactored helper.

Supersedes #215 (Dependabot actions group). That PR is left open for you to close.

What changes for you

  • Fewer dependencies. The plugin's lockfile drops from 543 to 245 packages. Direct devDependencies drop from 19 to 12.
  • Clean audit. pnpm audit is clean in both workspaces. It reported 7 advisories (1 high) at the root and 12 (8 high, including a critical Astro RCE) in the docs site.
  • Policy as config. "Latest safe" is enforced by pnpm, not by habit. Both pnpm-workspace.yaml files refuse releases younger than 7 days, and refuse a release from the last year whose provenance is weaker than earlier versions'. Dependabot gets the same 7-day cooldown.
  • Code scanning. Every action and reusable workflow is pinned to a full commit SHA, closing the 3 actions/unpinned-tag alerts.

Dependencies

"Safe" here means a stable release, no known advisory (pnpm audit, GitHub advisory DB), at least 7 days old, and no new install script. The newest release was skipped wherever it was younger than that. The table lists those cases.

Plugin (package.json, pnpm-lock.yaml)

Dependency Old New Action Notes
pnpm (packageManager) 10.32.1 12.14.0 migrated 10.32.1 is affected by high pnpm advisories (fixed in 10.34.5). Settings moved from package.json#pnpm and .npmrc to pnpm-workspace.yaml, and onlyBuiltDependencies became allowBuilds. 12.15.0 is 3 days old.
Node (engines) >=20.18.1 ^22.13.0 || ^24.0.0 || >=26 bumped The lines pnpm 12, ESLint 10 and Vitest 5 all support.
eslint ^9.39.4 ^10.13.0 migrated Config moves from the deprecated tseslint.config() to defineConfig from eslint/config.
typescript-eslint ^8.70.1 ^8.71.2 bumped 8.72.0 is 5 days old.
eslint-plugin-svelte ^3.23.0 ^3.23.1 bumped Uses configs.recommended (flat).
globals ^17.12.0 ^17.13.0 bumped
eslint-plugin-obsidianmd ^0.4.2 removed See Removals.
svelte-eslint-parser ^1.8.1 removed Already provided and wired by eslint-plugin-svelte.
vitest ^4.1.11 ^5.0.5 migrated Clears GHSA-qxg6-89mg-rqv8 and GHSA-xp26-3454-mjgp (@vitest/mocker). The suite needed no changes.
@vitest/coverage-v8 4.1.11 removed Only backed a local test:coverage script that nothing consumed. The script went too.
vite ^8.3.3 ^8.3.4 bumped Vitest peer. 8.3.5 is 3 days old.
obsidian-e2e ^0.10.0 ^0.14.0 bumped Drops the vulnerable vite-plus peer (GHSA-587h-7wmj-h5pm high, plus 4 more). It is our own package, published with provenance, and exempt from the age gate.
obsidian (types) 1.13.1 1.14.4 bumped Typecheck clean. See Risks for display().
svelte ^5.57.1 ^5.57.2 bumped
svelte-check ^4.7.6 ^4.7.7 bumped
@types/node 26.6.2 26.6.5 bumped
typescript ^6.0.3 ^6.0.3 kept 7.0.2 is out, but typescript-eslint, svelte-check and svelte-preprocess cap their peer range below 7.
esbuild 0.28.2 0.28.2 kept Already the latest. Its install script is now denied (the binary arrives as an optional dependency).
esbuild-svelte 0.9.5 vendored About 30 lines in esbuild.config.mjs. See Removals.
svelte-preprocess ^6.0.5 removed Svelte 5 strips TypeScript in <script lang="ts"> itself. main.js is byte-identical without it.
tslib ^2.2.0 removed Nothing imported it.
12 pnpm.overrides various removed Every targeted package is now gone or resolves at or above its patched floor.

Docs site (website/)

Dependency Old (locked) New Action Notes
pnpm (packageManager) unpinned 12.14.0 migrated Its own pnpm-workspace.yaml with the same policy. Pinning matters because Workers Builds otherwise installs with pnpm 10.11.1.
astro ^7.0.2 (7.0.3) ^7.4.0 migrated 7.0.3 had a critical RCE through AVIF optimization (GHSA-26w7-cxv4-gfx2) and 2 more advisories.
@astrojs/starlight 0.41.1 0.42.6 migrated Its breaking changes touch nothing the site uses.
@fontsource/ibm-plex-sans, -mono ^5.2.8, ^5.2.7 ^5.3.0 bumped Font files are byte-identical.
sharp ^0.34.5 ^0.35.5 bumped Three high libvips/libheif/librsvg advisories. Kept for scripts/generate-og-image.mjs. It now shares Astro's copy.
starlight-links-validator ^0.25.2 ^0.27.0 bumped Kept, since it fails the build on a broken link.
wrangler ^4.103.0 (4.105.0) ^4.151.1 bumped Kept so deploys use the locked version instead of npx latest.
overrides undici, postcss-selector-parser added Two transitive packages pinned below their fix by their parents.

GitHub Actions and reusable workflows

Action Old New
chhoumann/obsidian-plugin-workflows (3 reusable workflows) @v4 tag ebcf84b # v4 (the current head of v4)
step-security/harden-runner v2.21.1 v2.22.1 (v2.22.2 is 5 days old)
actions/setup-node v7.0.0 v7.1.0
github/codeql-action/init, /analyze 2892aa5 # v4 v4.39.0 (v4.39.1 is 3 days old)
actions/checkout, pnpm/action-setup, actions/dependency-review-action, amannn/action-semantic-pull-request unchanged, already latest and SHA-pinned

Dependabot now covers website/. Its bumps are titled build(website): so they can't trigger a plugin release. Toolkit bumps get their own group, so the three release stubs move together in one PR instead of riding the actions group.

Other pinned tooling

Tool Old New
pymobiledevice3 (iOS harness) unpinned uv run --with 11.33.1 via PEP 723 metadata, exclude-newer 2026-10-17
websockets (Android harness) unpinned uv run --with 17.2, same mechanism

Removals and vendoring

  • eslint-plugin-obsidianmd. It was 185 of 526 locked packages, including its own copies of TypeScript 5.4 and Obsidian 1.12. It also brought four ESLint plugins that cap ESLint at 9. We enabled 7 of its rules, and each is a global-reference or AST-shape check. They are now no-restricted-globals / no-restricted-syntax entries whose messages name the upstream rule. A probe file showed every rule fires on its positive cases and stays quiet on the upstream negatives. One deliberate difference: the core rules also flag typeof document guards, which the plugin source has none of.
  • esbuild-svelte. We used it to call compile() with css: "injected". Its caching, preprocess, external-CSS and rune-module paths were dead for us. The inline plugin keeps esbuild-formatted warnings and errors and watch rebuilds. main.js is byte-identical. Rune modules (.svelte.ts) now fail the build loudly instead of being silently mis-bundled.
  • svelte-preprocess, svelte-eslint-parser, tslib, @vitest/coverage-v8. Dead weight, as listed above.

Also in this PR

  • src/svelteMount.ts drops the Svelte 4 ComponentType branch, which Svelte 5 deprecates. All six components use runes. svelte-check still rejects unknown or mistyped props at each mount call, verified by a probe.
  • Flaky E2E fix. The native widget roundtrip test failed 2 of 9 full-suite runs because a transient Obsidian element from an earlier test disappeared mid-measurement. It now counts leaked elements instead of comparing body size.
  • Docs site layout. Astro 7.4 fixes the CSS of <CardGrid stagger>, which was broken on the live site, and that shifted the landing-page grid. stagger is removed so the grid matches the live site.
  • AGENTS.md now says esbuild, not Rollup.

Risks

  • The release pipeline now runs under our pnpm policy, untested until the next release. The shared workflows check the toolkit out into .release-toolkit inside this repo. Its pnpm install now finds our pnpm-workspace.yaml, switches to pnpm 12.14.0, and applies our age and trust policy to the toolkit's lockfile. It passes today, as a reviewer reproduced. A toolkit lockfile carrying a package younger than 7 days would fail the release job. The robust fix is --ignore-workspace in the toolkit's install step, in obsidian-plugin-workflows. Until then, a workflow_dispatch of Prepare release with dry-run: true after merging is cheap insurance.
  • PluginSettingTab.display() is deprecated since Obsidian 1.13 in favor of getSettingDefinitions(). Migrating would raise minAppVersion past 1.12.7 and drop users on 1.12, so it is not done here.
  • Test files fail tsc -p tsconfig.json with 71 errors. That count is identical on master. No script or CI job typechecks tests, so it is left for its own change.
  • The docs site's search can ship broken. Pagefind 1.5.2 can write truncated search files under heavy machine load (Service mode can acknowledge WriteFiles before the bytes reach disk; closing then ships truncated files Pagefind/pagefind#1271, open upstream). This predates the PR, and no fix is released yet.
  • Wrangler pins a beta miniflare (5.20261012.1-beta). It is used only by wrangler dev.

Verification

  • pnpm run lint, pnpm run build (includes tsc and svelte-check) and pnpm run test all pass: 19 files, 359 tests.
  • Docs site. pnpm install --frozen-lockfile && pnpm run build produces 35 pages and passes the links validator. Pages were checked in a browser against a build of master: fonts, logo, dark mode, mobile menu and search.
  • Real Obsidian. Obsidian 1.13.7 on Linux, isolated obsidian-e2e vault.
    • The plugin loads with no runtime errors.
    • api.update("status", "closed") and api.createYamlProperty("reviewed", "yes") on a note produced ---\nstatus: closed\ntags:\n - a\nreviewed: yes\n---.
    • getPropertyValue read closed back.
  • Full live E2E suite. After the flaky-test fix, 3 consecutive full runs each gave 109 passed and 1 skipped (the deliberate skip from feat(editor): edit properties with Obsidian's native widgets; raise minAppVersion to 1.12.7 #168).
  • Two independent reviews ran (claude-fable-5-1 and gpt-6-astra) across three rounds. Every finding was addressed except the toolkit risk above.

Note

Medium Risk
Large tooling and lockfile changes affect CI, releases, and installs (pnpm age/trust policy may interact with the release toolkit), though shipped plugin behavior is intended to stay the same aside from a minor Svelte patch and build-path refactors.

Overview
This PR is a repo-wide dependency and supply-chain refresh: npm/pnpm lockfiles, GitHub Actions, the shared release workflow stubs, the website/ docs workspace, and mobile Python harness pins.

Tooling and policy. The plugin moves to pnpm 12.14.0 with pnpm-workspace.yaml rules (7-day minimumReleaseAge, trustPolicy, allowBuilds for esbuild). Node engines narrow to supported LTS ranges. Dependabot adds 7-day cooldowns, a separate website/ npm ecosystem with build(website): commits, and an isolated release-toolkit group for obsidian-plugin-workflows. The docs site gets its own website/pnpm-workspace.yaml and upgraded Astro/Starlight stack; the landing page drops broken <CardGrid stagger>.

Build and lint. esbuild.config.mjs drops esbuild-svelte / svelte-preprocess in favor of a custom compileSvelte plugin (Svelte compile + injected CSS) and explicit rejection of .svelte.ts rune modules. ESLint 10 drops eslint-plugin-obsidianmd and re-expresses the same Obsidian guideline checks via no-restricted-globals / no-restricted-syntax. test:coverage and @vitest/coverage-v8 are removed; Vitest 5 and related dev deps are bumped.

CI and release. Workflow actions are re-pinned (e.g. harden-runner, setup-node, CodeQL); reusable release workflows move from @v4 tags to a full commit SHA.

Small code/test tweaks. src/svelteMount.ts is Svelte 5–only (no legacy ComponentType path). E2E cleanup measurement counts leakedBodyChildren by identity instead of body child count delta. Mobile scripts declare PEP 723 deps for uv run without --with.

Reviewed by Cursor Bugbot for commit 97bde8d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Upgrade all dev dependencies and replace esbuild-svelte with direct svelte/compiler integration

  • Moves dev dependencies to their latest versions, dropping esbuild-svelte, svelte-preprocess, eslint-plugin-obsidianmd, and coverage tooling; the removed Obsidian lint rules are reimplemented as core no-restricted-globals and no-restricted-syntax selectors in eslint.config.mjs
  • Adds a custom compileSvelte esbuild plugin that compiles Svelte files directly with svelte/compiler, plus a plugin that rejects .svelte.js and .svelte.ts rune modules
  • Switches package manager to pnpm 12.14.0 and declares Node 22, 24, or 26+ support; adds release-age and trust policies in pnpm-workspace.yaml and website/pnpm-workspace.yaml
  • Makes the mobile harness scripts self-contained via uv inline metadata, and updates GitHub Actions and Dependabot configs to pinned revisions, cooldowns, and grouped updates
  • Risk: mountSvelteComponent in src/svelteMount.ts no longer accepts older Svelte component forms, and builds now fail on Svelte rune modules; test:coverage script and coverage ignore entries are removed

Macroscope summarized 97bde8d.

Pins the three obsidian-plugin-workflows reusable workflows to the v4
commit (ebcf84b) instead of the moving tag, which closes the
actions/unpinned-tag code-scanning alerts. Folds in the Dependabot
actions group (harden-runner v2.22.1, setup-node v7.1.0) and moves
codeql-action to v4.39.0 with an exact version comment.
pnpm 10.32.1 is affected by several high-severity pnpm advisories
(fixed in 10.34.5). pnpm 11+ only reads settings from
pnpm-workspace.yaml, so the build allowlist (now allowBuilds) and the
overrides move there and .npmrc goes away (autoInstallPeers is the
default).

The workspace now refuses releases younger than 7 days and any recent
release whose provenance is weaker than earlier versions. The lockfile
is re-resolved under that policy. Node >=22.13 matches the floor of
pnpm 12, ESLint 10 and Vitest 5.
… TypeScript

Svelte 5 strips type annotations in <script lang="ts"> itself. Our
components only use type-level TypeScript and plain <style>, so the
preprocessor (and its postinstall script) did nothing: main.js is
byte-identical without it.
We only used esbuild-svelte to call svelte/compiler's compile() with
css: "injected". Its caching, preprocess, external-CSS and
.svelte.ts paths were dead code for us. The inline plugin keeps
esbuild-formatted warnings and errors and watch-mode rebuilds, and
main.js is byte-identical.
eslint-plugin-svelte's flat config already brings and wires
svelte-eslint-parser, and nothing imports tslib (esbuild inlines its
own helpers).
… core ESLint

ESLint 10.13, typescript-eslint 8.71, eslint-plugin-svelte 3.23,
globals 17.13. The config moves from the deprecated tseslint.config()
helper to defineConfig() from eslint/config.

eslint-plugin-obsidianmd was 185 of our 526 locked packages (including
its own TypeScript 5.4 and Obsidian 1.12 copies, plus four ESLint
plugins that cap ESLint at 9) for the seven rules we enable. Each of
those rules is a global-reference or AST-shape check, so they are now
no-restricted-globals / no-restricted-syntax entries that name the
upstream rule. A probe file confirmed every rule fires on its
positive case and stays quiet on the upstream negatives (window
timers, local 'document', --css-variables).
Vitest 5.0.5 with Vite 8.3.4. The suite needed no changes: no test
relies on mocks surviving between tests (now cleared by default) or
on un-awaited async assertions (now failures).

@vitest/coverage-v8 only backed a local test:coverage script that no
CI job, check or doc workflow consumed, so it goes along with the
script and its ignore entries. This also clears GHSA-qxg6-89mg-rqv8
and GHSA-xp26-3454-mjgp in @vitest/mocker 4.1.11.
0.14 drops the vite-plus peer, which removes the last pnpm audit
findings (GHSA-587h-7wmj-h5pm high and four lower vite-plus
advisories), and types its matchers against Vitest 5. No harness API
we use changed. The live suite runs 104 passed / 1 skipped against
Obsidian 1.13.7; the 3 multi-value.test.ts failures reproduce with
master's main.js and are tracked separately.
obsidian 1.13.1 -> 1.14.4, svelte 5.57.1 -> 5.57.2, svelte-check
4.7.6 -> 4.7.7, @types/node 26.6.2 -> 26.6.5. Typecheck is clean.
PluginSettingTab.display() is deprecated since Obsidian 1.13 in favor
of getSettingDefinitions(), but moving to it would raise minAppVersion
past 1.12.7, so that stays a separate product decision.
Every component is a runes component, so the Svelte 4 ComponentType
branch (deprecated in Svelte 5) is dead. Props are now taken from the
component with NoInfer, so svelte-check still rejects unknown or
mistyped props at each mount call.
All twelve overrides were floors for advisories in transitive
packages. After the upgrades and removals, the packages they targeted
are either gone (@babel/core, fast-uri, handlebars, js-yaml, tinypool)
or already resolve at or above the patched version (brace-expansion
5.0.12, moment 2.31.0, nanoid 3.3.20, postcss 8.5.30,
postcss-selector-parser 7.1.6, source-map-js 1.2.2). pnpm audit is
clean without them.
The iOS and Android harnesses ran with an unpinned
'uv run --with pymobiledevice3/websockets', so every run resolved the
newest release of the whole dependency tree. They now carry PEP 723
script metadata pinning pymobiledevice3 11.33.1 and websockets 17.2
with exclude-newer 2026-10-17 for transitive packages, and the
documented commands become 'uv run scripts/mobile/<script>.py'.
pnpm 12 ignores the "pnpm" field in package.json, so the docs site's build
allowlist was dead and every install failed with ERR_PNPM_IGNORED_BUILDS.
website/ now has its own pnpm-workspace.yaml with the plugin's release-age
and trust policy. The file also makes website/ its own workspace root.

None of the three install scripts is needed. esbuild, workerd and sharp
all ship prebuilt binaries as optional dependencies, so allowBuilds denies
them explicitly instead of allowing them.

packageManager pins pnpm 12.14.0. Cloudflare Workers Builds otherwise
installs with its default pnpm 10.11.1, which predates these settings.
Astro 7.0.3 is affected by GHSA-26w7-cxv4-gfx2 (critical, RCE through
AVIF optimization, fixed in 7.2.8), GHSA-98hj-9722-w3j8 (XSS, fixed in
7.3.2) and GHSA-mpr7-6m5w-76rw (fixed in 7.3.3). 7.4.0 is the newest
release past the 7-day age gate. It also moves Vite to 8.3.4, past the
Vite dev-server advisories fixed in 8.3.3, and Sharp to 0.35.5.

Starlight 0.42 requires Astro 7.2.10 and starlight-links-validator 0.26+
requires Starlight 0.42, so the three move together. Starlight 0.42's
breaking changes are the mobile menu markup and the removed "tagline"
config option. The site styles neither, and the hero "tagline" in
index.mdx is unrelated frontmatter.
Sharp 0.34.5 is affected by GHSA-f88m-g3jw-g9cj, GHSA-rgj7-g3m4-5g8c and
GHSA-wq5f-xc86-pv6w (libvips, libheif and librsvg), fixed in 0.35.5.

Astro's image service does not need this dependency. Astro declares
Sharp as its own optional dependency and resolves that copy, so images
optimized with 0.35.5 even while the direct dependency was 0.34.5.
scripts/generate-og-image.mjs imports Sharp directly, and pnpm's strict
layout hides Astro's copy from it, so the direct dependency stays. At
^0.35.5 it dedupes with Astro's copy. The script still renders og.png
under 0.35, whose breaking changes do not touch sharp(svg).png().
… 5.3.0

The font files and CSS are byte-identical to 5.2.x. 5.3.0 only adds a
CSS module type declaration and package metadata.
4.151.1 is the newest release past the 7-day age gate. It brings Sharp
0.35.5 through Miniflare, so Sharp 0.34.5 and its install script leave
the tree and allowBuilds no longer lists Sharp.

Wrangler stays a dev dependency. Workers Builds runs "npx wrangler
deploy" with the Wrangler version from package.json, so dropping it
would deploy with whatever npm serves at build time, outside the lockfile
and the release-age policy. wrangler.jsonc needs no changes, and
"wrangler dev" still serves dist/ with the 404 page.
…e-age policy

A fresh resolution moves every transitive dependency to its newest
release that is at least 7 days old. That clears the stale duplicates
pnpm audit flagged: postcss 8.5.16, js-yaml 4.3.0, nanoid 3.3.15,
source-map-js 1.2.1 and zod 4.4.3 now resolve to 8.5.30, 4.3.2, 3.3.20,
1.2.2 and 4.6.7. All 424 locked packages were published at least 7 days
ago.
…dencies

After the re-resolution pnpm audit still reports two packages that their
parents pin below the fix:

- undici 7.29.1, pinned exactly by Miniflare: GHSA-xjj6-f33q-929c,
  GHSA-4qv8-2p9j-2685 and GHSA-f72v-6w45-q472, fixed in 7.30.1.
- postcss-selector-parser 6.1.4, via postcss-nested 6 in Expressive
  Code: GHSA-rj75-hqrm-r3gf, fixed in 7.1.6. The plugin's workspace
  already carries the same override.

The built site is byte-identical with and without the overrides, and
"wrangler dev" still serves it. pnpm audit is now clean.
website/ has its own lockfile, which the root npm entry never looked at.
The new entry mirrors the root's schedule and 7-day cooldown with two
differences. A single group holds every minor and patch update, because
Astro, Starlight and starlight-links-validator require each other's new
versions. The commit prefix is build(website) instead of build with
include: scope, because include: scope would title production bumps
build(deps), and the release policy releases the plugin for those.
The CardGrid "stagger" prop never took effect on the live site. Astro
7.0 scoped the stagger selector to the card children, which do not carry
the grid's scope class. Astro 7.4 fixes the scoping, so the right column
dropped 5rem, and the custom "lift on hover" transform replaced the
stagger transform, making those cards jump 82px up on hover. Dropping the
prop keeps the aligned grid the site has always shown and the hover lift
works on every card.
The native widget roundtrip test compared document.body's child count
before and after, so a transient Obsidian element from an earlier test
(a fading notice, a closing modal) disappearing mid-test read as -1
and failed it. It failed 2 of 9 full-suite runs and 0 of 3 runs of
its own file. The check now counts elements added during the
measurement that are still attached, which is what a leak is. A probe
confirmed it reports 1 for a leaked element and 0 when only a
pre-existing element goes away.
…ll script

esbuild-svelte compiled .svelte.js/.svelte.ts rune modules. The inline
plugin doesn't, and without a guard such a file would bundle as plain
TypeScript and throw at runtime, so it is now a build error.
esbuild's binary arrives as an optional dependency, so its install
script is denied, as in the docs site.
…rules

Also documents that the core-rule replacement flags typeof guards,
which the obsidianmd rules skipped.
obsidian-plugin-workflows bumps change release behavior, so they leave
the actions group. engines.node excludes Node 23, which ESLint 10 and
Vitest 5 do not support.
In production the DEVCMD stripper loaded first and returned source for
a .svelte.ts file with dev-command markers, so the rune-module guard
never ran. The guard is now its own plugin registered first. It
rejects rune modules with and without markers, in dev and production.
Dependabot names each reusable-workflow path as its own dependency, so
excluding the toolkit from the actions group alone would split one
toolkit bump into three PRs that can land out of sync.
…ugin name

Vitest 5 rejects Node 25 as it does Node 23. The manifest name is read
once next to the imports and escaped before it goes into the selector.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
metaedit-docs 27654b1 Oct 24 2026, 08:47 AM

@chhoumann
chhoumann merged commit 7c692c6 into master Oct 24, 2026
12 checks passed
@chhoumann
chhoumann deleted the chore/deps-upgrade-2026-10 branch October 24, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant