Skip to content

Bump svelte to 5.55+ and add detached-resource regression harness#2323

Merged
myieye merged 9 commits into
developfrom
chore/svelte-5.55-detached-resource
Jun 3, 2026
Merged

Bump svelte to 5.55+ and add detached-resource regression harness#2323
myieye merged 9 commits into
developfrom
chore/svelte-5.55-detached-resource

Conversation

@myieye

@myieye myieye commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Split out of #2315 (the dependency/CI security PR) so that PR stays trivially mergeable.

This carries the one change with runtime impact — the svelte catalog bump 5.53→5.55.7 (resolves 5.56.0) — together with the detached-resource work it was entangled with:

  • project-context fix: own the getOrAdd factories in $effect.root so a service created by a consumer that unmounts before its resource resolves isn't torn down/frozen under a later consumer (cached-service freeze). initProjectContext now registers the matching teardown on its caller's onDestroy.
  • browser-test harness (WrappedResource{Consumer,Harness}, wrapper-service) covering the three distinct failure modes: happy-path render, creator-unmounts-before-resolve (canonical regression), api-swap-after-creator-unmount (real-world trigger).

This completes a third layer of test coverage — warranted for reactivity this tricky: detached-resource.test.ts covers the DetachedResource class alone, detached-api-resource.browser.test.ts covers ProjectContext.apiResource through components asserting the resource directly (both pre-existing), and the new wrapped-resource.browser.test.ts covers a cached service's $derived over the resource as rendered by a later consumer — the layer where the live bug (stale PartOfSpeechService) actually surfaced, and the only one that fails if the $effect.root fix is reverted.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e983443e-de2d-4a89-84e6-d2d3546a54d5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR upgrades Svelte to 5.55.7 and implements a wrapper service pattern with ProjectContext lifecycle ownership. ProjectContext now owns cached service roots via $effect.root scopes to prevent derived staleness across component lifecycles. A new WrapperService wraps detached resources with derived transformations, consumed by WrappedResourceConsumer components that share service instances within the same project context. A test harness and browser test suite validate the pattern's reactivity and lifecycle correctness.

Changes

Wrapper service pattern and ProjectContext lifecycle

Layer / File(s) Summary
Svelte upgrade and ignores
frontend/pnpm-workspace.yaml, frontend/viewer/.gitignore
Svelte workspace catalog updated from ^5.45.2 to ^5.55.7 to support lifecycle-scoped derived behavior. Ignores added for __screenshots__/ directory and eslint_report.json.
ProjectContext lifecycle ownership and destroy
frontend/viewer/src/project/project-context.svelte.ts, frontend/viewer/src/DotnetProjectView.svelte
ProjectContext imports untrack and adds #rootCleanups to own $effect.root scopes for cached services. Internal #ownAndCache helper runs factories inside $effect.root via untrack to prevent derived staleness, and a public destroy() method tears down all cached roots. DotnetProjectView calls destroy() on unmount to clean up service lifecycles when switching projects.
WrapperService resource transformation class
frontend/viewer/src/project/detached-resource/wrapper-service.svelte.ts
New WrapperService class wraps a DetachedResource and exposes a transformed $derived.by field that maps resource current values into {value, upper} objects with uppercase transformation.
WrappedResourceConsumer component
frontend/viewer/src/project/detached-resource/WrappedResourceConsumer.svelte
Component exports WRAPPER_SYMBOL as a shared cache key, accepts fetchData and lookupKey props, obtains or creates a WrapperService via projectContext.getOrAdd, derives a found entry from wrapper.transformed, and renders the found item's uppercase value and total count.
Test harness, helpers, and browser test suite
frontend/viewer/src/project/detached-resource/detached-api-resource-test-types.ts, frontend/viewer/src/project/detached-resource/WrappedResourceHarness.svelte, frontend/viewer/src/project/detached-resource/wrapped-resource.browser.test.ts
New WrappedHarnessControls interface. WrappedResourceHarness initializes ProjectContext with test metadata, manages two consumer visibility states, and exposes controls for showing/destroying consumers and swapping APIs. Test helpers (deferred, mountHarness, consumerText) and Vitest suite validate wrapper reactivity after resource resolution, across consumer destruction, and across API swaps after creator unmount.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • sillsdev/languageforge-lexbox#1632: Both PRs bump the Svelte workspace version; main PR upgrades to ^5.55.7 to leverage lifecycle-scoped derived behavior required for the wrapper service pattern.

Suggested labels

dependencies

Poem

🐰 A wrapper service hops into view,
Caching roots in effect scopes so true,
Svelte 5.55 brings the lifecycle care,
No leaks when switching projects with flair!
Tests hop along to prove it's all right. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main objectives: Svelte version bump and adding a detached-resource regression test harness.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly explains the Svelte bump to 5.55.7, the project-context fix for cached services, and the new test harness for detached resources, all directly related to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/svelte-5.55-detached-resource

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Jun 1, 2026
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

UI unit Tests

  1 files   61 suites   29s ⏱️
180 tests 180 ✅ 0 💤 0 ❌
252 runs  252 ✅ 0 💤 0 ❌

Results for commit aaeb921.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

C# Unit Tests

165 tests  ±0   165 ✅ ±0   20s ⏱️ -1s
 23 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit aaeb921. ± Comparison against base commit ab401bc.

♻️ This comment has been updated with latest results.

@argos-ci

argos-ci Bot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Jun 3, 2026, 10:59 AM

myieye added a commit that referenced this pull request Jun 1, 2026
Frontend (pnpm) — direct: @sveltejs/kit 2.49→2.60.1, @opentelemetry/sdk-node
0.208→0.217, @opentelemetry/auto-instrumentations-node 0.67→0.75, js-cookie
3.0.5→3.0.7. Catalog: vite→7.3.2, postcss→8.5.10. In viewer: lint-staged 13→15
(clears micromatch + yaml advisories naturally). Minimal pnpm.overrides for the
three transitives that don't have a clean parent-bump path: cookie (SvelteKit
still pins ^0.6), minimatch@9 (mjml→js-beautify chain), immutable@3
(graphql-codegen→relay-compiler@12 chain).

GitHub Actions — bump actions/{checkout,setup-node,setup-dotnet,upload-artifact,
download-artifact} v4→v5, pnpm/action-setup and arduino/setup-task SHA pins
→ v5/v2 tags, actions/labeler v5→v6, SHA-pin remaining actions, all for Node-24
compatibility ahead of the June 2026 deprecation. Drop the unused Claude Code
workflow.

FwLiteMaui — bump android SupportedOSPlatformVersion 23→24 to clear CA1416
warnings on BlazorWebView APIs (Android 6.0 Marshmallow is rounding-error
share in 2026; the WebView is the app, so the platform attribute was
inaccurate, not actually enabling API 23 support).

FwLiteWeb — drop the Assembly.Location dance, use AppContext.BaseDirectory
directly (IL3000).

The svelte 5.53→5.55 bump + detached-resource test harness moved to #2323; the
Platform.Bible extension (npm) bumps moved to their own PR. This PR is the
pnpm/CI/.NET dependency bumps only.

Not fixed (need follow-up issues): mjml ≤4.18.0 (no patch in v4 line — needs
v5 migration; html-minifier ReDoS is transitive via it).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
myieye added a commit that referenced this pull request Jun 1, 2026
Frontend (pnpm) — direct: @sveltejs/kit 2.49→2.60.1, @opentelemetry/sdk-node
0.208→0.217, @opentelemetry/auto-instrumentations-node 0.67→0.75, js-cookie
3.0.5→3.0.7. Catalog: vite→7.3.2, postcss→8.5.10. In viewer: lint-staged 13→15
(clears micromatch + yaml advisories naturally). Minimal pnpm.overrides for the
three transitives that don't have a clean parent-bump path: cookie (SvelteKit
still pins ^0.6), minimatch@9 (mjml→js-beautify chain), immutable@3
(graphql-codegen→relay-compiler@12 chain).

GitHub Actions — bump actions/{checkout,setup-node,setup-dotnet,upload-artifact,
download-artifact} v4→v5, pnpm/action-setup and arduino/setup-task SHA pins
→ v5/v2 tags, actions/labeler v5→v6, SHA-pin remaining actions, all for Node-24
compatibility ahead of the June 2026 deprecation. Drop the unused Claude Code
workflow.

FwLiteMaui — bump android SupportedOSPlatformVersion 23→24 to clear CA1416
warnings on BlazorWebView APIs (Android 6.0 Marshmallow is rounding-error
share in 2026; the WebView is the app, so the platform attribute was
inaccurate, not actually enabling API 23 support).

FwLiteWeb — drop the Assembly.Location dance, use AppContext.BaseDirectory
directly (IL3000).

The svelte 5.53→5.55 bump + detached-resource test harness moved to #2323; the
Platform.Bible extension (npm) bumps moved to their own PR. This PR is the
pnpm/CI/.NET dependency bumps only.

Not fixed (need follow-up issues): mjml ≤4.18.0 (no patch in v4 line — needs
v5 migration; html-minifier ReDoS is transitive via it).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/viewer/src/project/detached-resource/WrappedResourceHarness.svelte`:
- Around line 3-45: The harness never tears down the ProjectContext causing
leaked $effect.root instances; update WrappedResourceHarness.svelte to call
projectContext.destroy() when the component unmounts by registering a teardown
in the Svelte destroy lifecycle (e.g., import and use onDestroy or the
framework-equivalent) so that projectContext.destroy() runs on unmount; keep
current initProjectContext, controls, and swapApi logic unchanged and only add
the lifecycle hook that invokes projectContext.destroy().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6d08409e-9d0e-48dd-935f-491ee11b2395

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7c6e4 and 7172be2.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • frontend/pnpm-workspace.yaml
  • frontend/viewer/.gitignore
  • frontend/viewer/src/DotnetProjectView.svelte
  • frontend/viewer/src/project/detached-resource/WrappedResourceConsumer.svelte
  • frontend/viewer/src/project/detached-resource/WrappedResourceHarness.svelte
  • frontend/viewer/src/project/detached-resource/detached-api-resource-test-types.ts
  • frontend/viewer/src/project/detached-resource/wrapped-resource.browser.test.ts
  • frontend/viewer/src/project/detached-resource/wrapper-service.svelte.ts
  • frontend/viewer/src/project/project-context.svelte.ts

myieye and others added 4 commits June 2, 2026 12:04
Bump the svelte catalog entry 5.53→5.55.7 (resolves 5.56.0). This is split out
of the dependency/CI security PR because it is the one bump that changes runtime
behaviour rather than just clearing an advisory.

Fix a cached-service freeze in project-context: own the getOrAdd factories in
$effect.root so a service created by a consumer that unmounts before the
resource resolves is no longer torn down (or frozen) underneath a later
consumer. Add a focused browser-test harness (WrappedResource{Consumer,Harness},
wrapper-service) covering the three distinct failure modes: happy-path render,
creator-unmounts-before-resolve (the canonical regression), and
api-swap-after-creator-unmount (the real-world trigger).

Gitignore eslint_report.json and __screenshots__/ test artifacts.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The $effect.root instances that #ownAndCache creates to own cached-service
deriveds aren't tied to the component tree, so they (and their $derived
subscriptions) survived past the view that created the context. destroy()
existed to tear them down but nothing called it; wire it into onDestroy so
roots don't accumulate across project switches.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PartOfSpeechService.current was a `$derived.by`. A cached service's derived can be read back stale once its observers unmount (e.g. the browse list/filter during in-app navigation), so PoS labels disappeared from the dictionary preview even though the parts-of-speech fetch had resolved. Expose it as a plain getter over the resource `$state` (matching WritingSystemService), which can't go stale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@myieye myieye force-pushed the chore/svelte-5.55-detached-resource branch from 7172be2 to eb7c9e8 Compare June 2, 2026 10:54
myieye and others added 5 commits June 2, 2026 13:09
MorphTypesService, PublicationService and CustomViewService exposed resource-backed data via `$derived.by` exactly like PartOfSpeechService did, so they share the same staleness: a cached service's derived can read back stale once its observers unmount. Convert their `current` (and MorphTypes' `prefixes`/`suffixes`, read via decorate() in transient list rows) to plain getters over the resource `$state`, matching WritingSystemService.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The missing parts of speech in the dictionary preview did not reproduce in a clean environment with `$derived.by` — it appears to have been a dev-server stale-state artifact, not the reactive form. The getter conversions were therefore unnecessary and lost memoization, so revert PartOfSpeechService, MorphTypesService, PublicationService and CustomViewService back to `$derived.by`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both harnesses call initProjectContext but never tore down the context-owned $effect.root instances; call projectContext.destroy() on unmount, mirroring DotnetProjectView.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Transform by appending '-derived' instead of upper-casing
- Move getOrAdd caching into useWrapperService (mirrors usePartsOfSpeech)
- Assert the pre-swap consumer renders NONE for the post-swap key
- Auto-destroy ProjectContext via onDestroy in initProjectContext and
  drop the manual destroy calls

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@myieye myieye merged commit e2f5aa8 into develop Jun 3, 2026
27 checks passed
@myieye myieye deleted the chore/svelte-5.55-detached-resource branch June 3, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant