Skip to content

Apply Bswup improvements (#12408)#12418

Open
msynk wants to merge 11 commits into
bitfoundation:developfrom
msynk:12408-bswup-improvements
Open

Apply Bswup improvements (#12408)#12418
msynk wants to merge 11 commits into
bitfoundation:developfrom
msynk:12408-bswup-improvements

Conversation

@msynk
Copy link
Copy Markdown
Member

@msynk msynk commented May 31, 2026

closes #12408

Summary by CodeRabbit

  • New Features

    • Visible install-error panel with details and conditional Retry; progress UI shows/hides appropriately.
    • Optional update polling (interval + visibility) and richer update/check/error events.
    • Better single-start startup and more robust Blazor entry-script detection.
  • Bug Fixes

    • Hardened service-worker startup: manifest validation, background cache writes, per-asset retries, and safer cross-tab reload sequencing.
    • Demo now supports lax demo mode to exercise progress/error UI.
  • Style

    • Added styling for the error panel and details.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented.

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: f112e74d-de9c-4799-b0fe-524f21ac216c

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an install-failure UI and retry flow, hardens service-worker manifest loading and caching (validation, normalization, retries/backoff, strict/lax install modes), updates fetch/passive caching and prohibited-URL behavior, adds opt-in update polling and coordinated multi-tab reloads, expands runtime logging/options, and refreshes docs.

Changes

Bit.Bswup Service Worker Error Handling & Update Polling

Layer / File(s) Summary
Error UI & Demo wiring
src/Bswup/Bit.Bswup/BswupProgress.razor, src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css, src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts, src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js, src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
Adds hidden install error panel markup (title, message, details, retry), styles, caches DOM refs in start(), handles BswupMessage.error to show error UI and wire retry (calls provided reload or window.reload), replaces img/onerror bootstrapping with inline script, and documents demo’s intentional missing asset with errorTolerance: 'lax' and published config changes.
Service Worker Manifest Validation & Config
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Fail-soft importScripts of assets, validate/normalize self.assetsManifest, add sendError and manifest helpers, add cacheVersion support, validate/default self.errorTolerance, and derive VERSION/CACHE_VERSION/CACHE_NAME with cacheVersion preference.
Service Worker Install & Asset Integrity
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
createAssetsCache reworked: case-insensitive urlEndsWith, old-cache sync hardening, per-asset retry/backoff with Promise.allSettled, integrity tracking, structured error reporting, and strict-mode delete+throw on failures; install awaited in strict mode, fire-and-forget in lax mode.
Service Worker Fetch & Multi-Tab Lifecycle
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Prohibited URLs return 403 plain-text; passive caching returns network response immediately while cache.put runs asynchronously with response.clone() and errors logged; SKIP_WAITING/CLAIM_CLIENTS sequencing updated to delete old caches after claim.
Runtime Registration & Reload Coordination
src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Global idempotency guard, capture resolved registration and first-install discrimination, reloadOnce() to ensure a single page reload across signals, new explicit error message emission with reload callback, and adjusted first-install detection.
Update Polling, Options & Public API
src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Adds opt-in polling (updateInterval, updateOnVisibility), registration-aware checkForUpdate() that emits updateNotFound/updateCheckFailed, startBlazor() accepts blazorScript string
Docs & Examples
src/Bswup/README.md
Updates examples and docs for handler attributes, polling, error reporting, multi-tab behavior, adds self.cacheVersion sample, expands errorTolerance and cacheVersion semantics, and documents JS API (checkForUpdate, skipWaiting, forceRefresh) and polling guidance.
SW cleanup & published demo tweaks
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts, src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
Adjusts version assignment to use a TypeScript-compatible (self as any) cast; updates published demo worker asset selection flags and case-insensitive URL sample.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐰 I tug a thread of worker bits,
Where retries dance and manifests fit,
An error panel whispers "Try again",
Polling hums and tabs agree to reign,
Logs and docs nibble at the fixes.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Apply Bswup improvements (#12408)' is vague and uses generic phrasing that doesn't convey the specific nature of the changes; it refers to improvements but doesn't highlight key implementations. Consider a more specific title like 'Add structured error handling and update polling to Bswup' to better communicate the primary changes.
Linked Issues check ❓ Inconclusive The PR implements most critical objectives from issue #12408 including structured error reporting, retry logic, update polling, cacheVersion, multi-tab coordination, URL robustness, and atomicity improvements; however, some issues remain unresolved or incomplete. Address critical issues: prevent SW crash on manifest load failure by rejecting install early; defer updatefound checks to avoid spurious updateNotFound; sanitize Handler parameter injection in BswupProgress.razor; hide retry button for non-retriable errors.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Out of Scope Changes check ✅ Passed All changes directly address objectives from issue #12408; no out-of-scope modifications were detected in the service worker logic, progress UI, configuration, or documentation updates.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 47-57: The manifest validation detects errors (MANIFEST_ERRORS
from validateAssetsManifest) but the code continues to run and later
dereferences self.assetsManifest.assets, causing a crash; after calling
sendError you should stop further startup: return/throw to prevent asset
enumeration, and guard any access to self.assetsManifest.assets (e.g., use
Array.isArray(self.assetsManifest.assets) before iterating) and ensure VERSION
fallback is computed safely (use (self.assetsManifest && typeof
self.assetsManifest.version === 'string') ? self.assetsManifest.version :
'0.0.0-invalid-manifest') so the service worker does not proceed when the
manifest is invalid; update the block around MANIFEST_ERRORS, sendError,
VERSION, and any asset-enumeration code that uses self.assetsManifest.assets to
enforce this early exit and safe checks.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Around line 321-324: The catch block in checkForUpdate currently calls
handle(BswupMessage.error, ...) which routes transient registration.update()
failures into the blocking install-error path; change this to emit a
non-blocking message instead (e.g., add a new enum/value like
BswupMessage.updateCheckFailed or use a non-fatal level such as
BswupMessage.warn/info) and call handle(...) with that new message name,
preserving the payload ({ reason: 'update', message: String((err && (err as
any).message) || err), reload }) so callers can still react but the default
progress handler will not hide the app or show the install-failed UI. Ensure to
update any switch/handler that consumes BswupMessage.* to treat the new
updateCheckFailed value as non-blocking.
🪄 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: ae99fd6f-8550-4920-b5b5-7982f9b3572d

📥 Commits

Reviewing files that changed from the base of the PR and between 046d116 and 825d8af.

📒 Files selected for processing (7)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts Outdated
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts Outdated
@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 1, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== bit-bswup.sw.ts ==="
fd "bit-bswup.sw.ts" --exec cat {}

echo ""
echo "=== bit-bswup.ts ==="
fd "bit-bswup.ts" -E "*.js" --exec cat {}

echo ""
echo "=== bit-bswup.progress.ts ==="
fd "bit-bswup.progress.ts" --exec cat {}

echo ""
echo "=== BswupProgress.razor ==="
fd "BswupProgress.razor" --exec cat {}

echo ""
echo "=== bit-bswup.progress.css ==="
fd "bit-bswup.progress.css" --exec cat {}

echo ""
echo "=== service-worker.js (demo) ==="
fd "service-worker.js" --search-path "Bit.Bswup.Demo" --exec cat {}

echo ""
echo "=== README.md (Bswup) ==="
cat src/Bswup/README.md

Length of output: 87174



@msynk: Reviewed the PR thoroughly. The improvements are substantial — the structured error reporting, errorTolerance: 'strict' default, retry logic with exponential backoff, update polling, cacheVersion, and multi-tab lifecycle fixes are all well-designed. A few issues worth addressing:


🔴 Critical — Crash when assetsManifest fails to load

In bit-bswup.sw.ts, validateAssetsManifest correctly catches a missing/null manifest and pushes an error — but execution continues and the module-level ALL_ASSETS initialisation immediately dereferences .assets on the potentially-null manifest:

const MANIFEST_ERRORS = validateAssetsManifest(self.assetsManifest);
if (MANIFEST_ERRORS.length) {
    sendError({ reason: 'manifest', message: '...', url: ASSETS_URL });
    // ↑ execution falls through!
}
// ...
const ALL_ASSETS = self.assetsManifest.assets   // TypeError: Cannot read properties of null
    .filter(...);

If the importScripts(ASSETS_URL) call fails (404, network error, syntax error in the generated assets file), self.assetsManifest is undefined and the SW crashes with an unhandled TypeError before any error event can reach the page. Suggest guarding:

if (MANIFEST_ERRORS.length) {
    sendError({ ... });
    // Prevent further initialisation from crashing on a null manifest.
    self.addEventListener('install', (e: any) => e.waitUntil(Promise.reject(new Error('Invalid assetsManifest'))));
    throw new Error('assetsManifest validation failed: ' + MANIFEST_ERRORS.join('; '));
}

🟡 Moderate — checkForUpdate may emit spurious updateNotFound

In bit-bswup.ts, after await registration.update(), the code reads registration.installing immediately:

await registration.update();
if (!registration.installing && !registration.waiting) {
    handle(BswupMessage.updateNotFound);   // ← may fire even when install is in-flight
}

reg.update() resolves when the server responds (the SW byte-comparison is done), but the browser may not have set reg.installing synchronously by the time the next microtask runs. This can produce a false UPDATE_NOT_FOUND that races with the updatefound event. A small setTimeout(0) / requestAnimationFrame or relying solely on the absence of an updatefound event would be more reliable.


🟡 Moderate — Retry button shown for non-retriable errors

In bit-bswup.progress.ts, errorRetryButton is revealed for every BswupMessage.error including manifest validation failures (reason: 'manifest') and SRI integrity failures (reason: 'integrity'). For those cases, reloading won't help without a redeployment. Consider hiding the retry button (or changing its label to "Reload") when data.reason is one of those permanent failure types.


🟡 Moderate — Handler parameter injected unsanitised into JS string

In BswupProgress.razor:

BitBswupProgress.start(..., '@(Handler)');

If Handler contains a single quote or semicolon (e.g. '; alert(1)//), it breaks out of the string literal. Handler is a developer-supplied [Parameter] so the practical risk is low, but HttpUtility.JavaScriptStringEncode or @Json.Serialize(Handler) would make it safe and consistent.


🟢 Minor — verbose() is defined but never called

In bit-bswup.ts, verbose() is defined alongside error/warn/info/debug but is never invoked in the script. If it's intended to be part of the public log-level hierarchy it should be used, otherwise it can be removed to keep the codebase clean.


🟢 Minor — Error panel shown over partial progress bar

When BswupMessage.error fires mid-download in bit-bswup.progress.ts, the error panel appears but the progress bar and percentage elements remain visible showing a partial value. Consider hiding them when revealing the error panel:

case BswupMessage.error:
    // ...
    if (progressEl) progressEl.parentElement!.style.display = 'none';
    if (percentEl) percentEl.style.display = 'none';
    if (errorEl) errorEl.style.display = 'block';

🟢 Minor — README sample handler missing error case

The bitBswupHandler switch in README.md covers updateNotFound (new) but omits BswupMessage.error. Since structured error reporting is a headline feature of this PR, adding a sample case would help developers wire it up:

case BswupMessage.error:
    console.error('Bswup install error:', data.reason, data.message);
    // optionally surface data.url / data.hash for diagnostics
    return;

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves Bit.Bswup’s update experience and robustness by adding user-visible install error reporting (with retry), introducing opt-in update polling (interval and/or tab visibility), improving multi-tab update consistency, and expanding Blazor script auto-detection to cover both Blazor Web App and standalone WebAssembly entry points.

Changes:

  • Add update polling controls (updateInterval, updateOnVisibility) and expose/extend JS API docs (BitBswup.checkForUpdate, skipWaiting, forceRefresh).
  • Improve service worker install reliability (manifest validation, retries/backoff, strict vs lax error tolerance, safer multi-tab activation/claim behavior).
  • Enhance the progress UI with an error panel (message/details + retry) and corresponding styles.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/Bswup/README.md Documents new script attributes, error tolerance behavior, multi-tab behavior, security notes, and JS API.
src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css Adds styling for the new install error panel and retry button behavior.
src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts Adds idempotent initialization, update polling, better controllerchange handling, richer logging levels, and expanded Blazor script detection.
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts Adds manifest validation + error reporting, retry/backoff for asset fetches, strict/lax install semantics, safer cache update sequencing, and RegExp parsing improvements.
src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts Adds UI handling for install errors (message/details/retry) and keeps assets list visibility in sync with runtime config changes.
src/Bswup/Bit.Bswup/BswupProgress.razor Updates the progress component markup to include an error panel and changes initialization to run via a <script> block.
src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js Removes demo precachedAssetsInclude config line.
src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js Adjusts demo config to use errorTolerance = 'lax' with explanatory comment.

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
@msynk msynk requested a review from Copilot June 2, 2026 19:39
@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 2, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

@msynk: Sure, I'll kick off a fresh review of the PR now.

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts (1)

333-362: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use e.waitUntil() for async work in the service worker message handler.

Returning a Promise from the message listener doesn’t reliably keep the worker alive; use ExtendableMessageEvent.waitUntil(...) for skipWaiting(), clients.claim(), cache deletion/filling, and the follow-up postMessage.

Suggested fix
-function handleMessage(e: MessageEvent<string>) {
+function handleMessage(e: any) {
     diag('handleMessage:', e);

     if (e.data === 'SKIP_WAITING') {
-        return self.skipWaiting()
-            .then(() => self.clients.claim())
-            .then(() => deleteOldCaches())
-            .then(() => sendMessage('WAITING_SKIPPED'));
+        e.waitUntil(
+            self.skipWaiting()
+                .then(() => self.clients.claim())
+                .then(() => deleteOldCaches())
+                .then(() => sendMessage('WAITING_SKIPPED'))
+        );
+        return;
     }

     if (e.data === 'CLAIM_CLIENTS') {
-        return self.clients.claim()
-            .then(() => deleteOldCaches())
-            .then(() => e.source?.postMessage('CLIENTS_CLAIMED'));
+        e.waitUntil(
+            self.clients.claim()
+                .then(() => deleteOldCaches())
+                .then(() => e.source?.postMessage('CLIENTS_CLAIMED'))
+        );
+        return;
     }

     if (e.data === 'BLAZOR_STARTED') {
-        createAssetsCache(true);
+        e.waitUntil(createAssetsCache(true));
+        return;
     }

     if (e.data === 'CLEAN_UP') {
-        deleteOldCaches(); // remove the old caches
+        e.waitUntil(deleteOldCaches()); // remove the old caches
     }
 }
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts` around lines 333 - 362, The
message handler currently returns Promises but must use e.waitUntil(...) to keep
the service worker alive; wrap each async branch so that SKIP_WAITING uses
e.waitUntil(self.skipWaiting().then(() => self.clients.claim()).then(() =>
deleteOldCaches()).then(() => sendMessage('WAITING_SKIPPED'))), CLAIM_CLIENTS
uses e.waitUntil(self.clients.claim().then(() => deleteOldCaches()).then(() =>
e.source?.postMessage('CLIENTS_CLAIMED'))), BLAZOR_STARTED uses
e.waitUntil(createAssetsCache(true)), and CLEAN_UP uses
e.waitUntil(deleteOldCaches()), ensuring all calls to self.skipWaiting(),
self.clients.claim(), deleteOldCaches(), createAssetsCache(true), sendMessage
and e.source?.postMessage are inside the waitUntil chains.
🤖 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 `@src/Bswup/Bit.Bswup/BswupProgress.razor`:
- Around line 37-39: The inline script in BswupProgress.razor calls
BitBswupProgress.start(...) unguarded which throws if bit-bswup.progress.js
hasn't loaded; update the init to check for the global before invoking (e.g.,
use window.BitBswupProgress?.start(...) or an explicit if
(window.BitBswupProgress) call) passing the same parameters (AutoReload,
ShowLogs, ShowAssets, AppContainer, HideApp, AutoHide, Handler), or
alternatively move the startup into the component lifecycle (OnAfterRenderAsync)
and call JS interop to invoke BitBswupProgress.start to guarantee ordering.
- Line 38: The call to BitBswupProgress.start injects AppContainer and Handler
as raw single-quoted JS literals which can be broken by
quotes/backslashes/newlines; change those embeddings to JSON-encoded JS-safe
strings instead (e.g., replace the single-quoted '@(AppContainer)' and
'@(Handler)' with the output of
System.Text.Json.JsonSerializer.Serialize(AppContainer) and
JsonSerializer.Serialize(Handler) so the values are properly escaped and already
quoted), update the BswupProgress.razor to use JsonSerializer.Serialize for both
AppContainer and Handler and add the required using/import for System.Text.Json
if missing.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 86-94: The cache bucket currently uses only CACHE_VERSION (derived
from self.cacheVersion or VERSION) which allows different manifests to share a
bucket; change the cache naming so the manifest VERSION is always part of the
bucket identity. Keep the existing CACHE_VERSION calculation (using
self.cacheVersion fallback to VERSION) but construct CACHE_NAME to include both
VERSION and CACHE_VERSION (e.g. `${CACHE_NAME_PREFIX} - ${VERSION} -
${CACHE_VERSION}`), so the manifest VERSION remains in the cache bucket while
cacheVersion can still be used to pin/force refresh.
- Around line 516-540: The current logic sets isIntegrity true whenever
hasIntegrity && fetchErr instanceof TypeError, which treats all TypeError fetch
rejections as SRI failures and prevents retries; change the detection in the
addCache flow so a bare TypeError does NOT automatically mark an integrity
failure: remove fetchErr instanceof TypeError from the isIntegrity predicate and
instead only classify as integrity when there is a clear integrity signal (e.g.,
the error message matches the integrity/digest regex) or after explicitly
recomputing and comparing the response bytes to asset.hash; keep transient
TypeError cases as retryable (use lastError/continue while attempt <
MAX_RETRIES), and only increment integrityFailureCount, set sendError.reason to
'integrity', and skip retries when you have a confirmed integrity mismatch (use
the integrity regex or the explicit digest comparison to decide).

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Line 35: Guard runBswup against late script injection by checking
document.readyState before adding the listener: if the document is already
"interactive" or "complete" call runBswup() immediately, otherwise attach
window.addEventListener('DOMContentLoaded', runBswup) (refer to the runBswup
symbol). Also fix BitBswup.forceRefresh where cacheFilter.test(key) is used:
avoid inconsistent results when cacheFilter has the global (g) or sticky (y)
flags by resetting cacheFilter.lastIndex = 0 before each test or by creating a
non-global equivalent RegExp from cacheFilter (use cacheFilter.source with flags
stripped of g/y) and testing against that; reference the BitBswup.forceRefresh
method and the cacheFilter variable so the change is made exactly where repeated
RegExp.test calls occur.
- Around line 497-500: forceRefresh builds the shouldDelete predicate from
cacheFilter but when cacheFilter is a RegExp it calls cacheFilter.test()
repeatedly, which can misbehave for /.../g or /.../y because RegExp.lastIndex is
mutated; fix by updating the RegExp branch in the shouldDelete construction
(used in forceRefresh) to reset cacheFilter.lastIndex = 0 before calling
cacheFilter.test(key) so each key is tested from the start; reference the
shouldDelete variable and cacheFilter RegExp usage in forceRefresh when making
the change.

---

Outside diff comments:
In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 333-362: The message handler currently returns Promises but must
use e.waitUntil(...) to keep the service worker alive; wrap each async branch so
that SKIP_WAITING uses e.waitUntil(self.skipWaiting().then(() =>
self.clients.claim()).then(() => deleteOldCaches()).then(() =>
sendMessage('WAITING_SKIPPED'))), CLAIM_CLIENTS uses
e.waitUntil(self.clients.claim().then(() => deleteOldCaches()).then(() =>
e.source?.postMessage('CLIENTS_CLAIMED'))), BLAZOR_STARTED uses
e.waitUntil(createAssetsCache(true)), and CLEAN_UP uses
e.waitUntil(deleteOldCaches()), ensuring all calls to self.skipWaiting(),
self.clients.claim(), deleteOldCaches(), createAssetsCache(true), sendMessage
and e.source?.postMessage are inside the waitUntil chains.
🪄 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: d3c76fdc-5fec-4833-85fc-89e300ee0dd0

📥 Commits

Reviewing files that changed from the base of the PR and between 046d116 and 733a05a.

📒 Files selected for processing (8)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md
💤 Files with no reviewable changes (1)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js

Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor Outdated
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts Outdated
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
@msynk msynk requested a review from Copilot June 4, 2026 10:16
@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 4, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

@msynk: Sure, triggering a fresh review of the PR now.

✅ Action performed

Full review finished.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (1)
src/Bswup/README.md (1)

105-110: ⚡ Quick win

Consider showing complete error details in the example.

The comment documents that data.url and data.hash are available "when known", but the example only logs data.reason and data.message. For a more complete reference example, consider showing all available fields.

📝 Suggested enhancement
         case BswupMessage.error:
             // Structured install failure. data.reason is one of 'manifest' | 'integrity' |
             // 'fetch' | 'cache' | 'request' | 'install-incomplete'; data.message is human
             // readable, and data.url / data.hash point at the offending asset when known.
-            console.error('Bswup install error:', data.reason, data.message);
+            console.error('Bswup install error:', data.reason, data.message, data.url, data.hash);
             return;
🤖 Prompt for 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.

In `@src/Bswup/README.md` around lines 105 - 110, The example logging for the
BswupMessage.error case only prints data.reason and data.message; update the
handler for the BswupMessage.error branch (the switch case) to log all available
error details (data.reason, data.message, and when present data.url and
data.hash, plus the whole data object for unknown fields) so consumers see the
complete failure context; use the existing BswupMessage.error case and data
variable to append url and hash if defined and include a fallback of logging the
entire data object.
🤖 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 `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts`:
- Around line 129-149: The retry suppression omitted the 'install-incomplete'
reason so deterministic failures can still show the retry button; update the
nonRetriableReasons used where errorRetryButton is handled (the
nonRetriableReasons array and the isRetriable check in the same block) to
include 'install-incomplete' alongside 'manifest' and 'integrity' so isRetriable
becomes false for that reason, keep the existing behavior for invoking
data.reload() or window.location.reload() unchanged.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Around line 388-425: The extract() function currently assigns options =
(window[optionsName] || defaultoptions) which replaces defaults when a partial
window[optionsName] exists; change it to merge defaults with any global config
(e.g. create options by combining defaultoptions and window[optionsName] such as
Object.assign({}, defaultoptions, window[optionsName]) or using the spread
operator) so missing keys in window[optionsName] fall back to defaultoptions;
keep the subsequent attribute overrides (log, sw, scope, handlerName,
blazorScript, updateInterval, updateOnVisibility) applied on top of the merged
options.
- Around line 427-438: The current handle function permanently replaces
options.handler with a noop when window[options.handlerName] is missing,
preventing later attachment from being picked up; change it so you do not
overwrite options.handler on missing handler — instead read
window[options.handlerName] into a local variable (e.g., foundHandler) or only
assign to options.handler when a real function is present, call that local/found
handler if available, and leave options.handler untouched (so future
registrations on window[options.handlerName] can be discovered); update the
logic in the handle function that references options.handlerName and
options.handler to reflect this non-destructive check.

In `@src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css`:
- Around line 74-89: Replace the deprecated word-break: break-word in the
.bit-bswup-error-message and .bit-bswup-error-details selectors by removing
those declarations and adding overflow-wrap: anywhere (leave existing
white-space: pre-wrap intact) so the stylesheet no longer triggers the Stylelint
warning.

In `@src/Bswup/README.md`:
- Line 218: Update the README entry for the `FullOffline` mode to correct the
grammar by changing "from first time the app is loaded" to "from the first time
the app is loaded" so the description reads: "Enables the full offline mode
where all assets are cached and served from the cache from the first time the
app is loaded."
- Around line 228-234: Update the "Polling for updates" paragraph to fix the
numeric inconsistency: change "There are three ways to check more often:" to
"There are two ways to check more often:" (since only the built-in options using
updateInterval/updateOnVisibility and calling BitBswup.checkForUpdate() are
listed), ensuring the section header "Polling for updates" and references to
updateInterval, updateOnVisibility and BitBswup.checkForUpdate() remain
unchanged.
- Line 213: The README wording incorrectly states that per-asset ?v= and SRI
derive from the manifest version; update the text to accurately reflect that
CACHE_VERSION / cacheVersion only affect the cache bucket name (computed into
CACHE_NAME) while per-asset cache-buster and integrity are set inside
createNewAssetRequest() using each asset's asset.hash (with fallback to
self.assetsManifest.version) and requestInit.integrity = asset.hash; replace the
sentence to mention that ?v= primarily comes from asset.hash (fallback to
manifest.version) and SRI uses asset.hash, and remove any implication that those
values reference self.cacheVersion or CACHE_VERSION.

---

Nitpick comments:
In `@src/Bswup/README.md`:
- Around line 105-110: The example logging for the BswupMessage.error case only
prints data.reason and data.message; update the handler for the
BswupMessage.error branch (the switch case) to log all available error details
(data.reason, data.message, and when present data.url and data.hash, plus the
whole data object for unknown fields) so consumers see the complete failure
context; use the existing BswupMessage.error case and data variable to append
url and hash if defined and include a fallback of logging the entire data
object.
🪄 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: 427f1025-c5ef-456e-b74d-6a2168e132d7

📥 Commits

Reviewing files that changed from the base of the PR and between 046d116 and de9c330.

📒 Files selected for processing (9)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md
💤 Files with no reviewable changes (1)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Comment thread src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
Comment thread src/Bswup/README.md Outdated
Comment thread src/Bswup/README.md Outdated
Comment thread src/Bswup/README.md
@msynk msynk requested a review from Copilot June 6, 2026 06:44
@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 6, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

@msynk: Sure, triggering a fresh review of the PR now.

✅ Action performed

Full review finished.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
src/Bswup/Bit.Bswup/BswupProgress.razor (1)

37-46: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

JS-encode AppContainer and Handler before passing them into the inline script.

Line 43 and Line 46 still inject component parameters into JS single-quoted literals. A value containing ', \, a newline, or </script> can break the call or execute arbitrary script. Serialize these as JSON/JS strings instead of embedding raw Razor output.

In ASP.NET Core Razor, when embedding @(value) inside a JavaScript string literal in a <script> tag, is HTML encoding sufficient, or should the value be JSON/JavaScript-string encoded instead?
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/BswupProgress.razor` around lines 37 - 46, The inline
script calls window.BitBswupProgress.start with component parameters
AppContainer and Handler injected into single-quoted JS literals which can break
or lead to XSS; update the Razor to JS-encode/JSON-stringify those parameters
instead of embedding raw Razor output—use a safe serializer like
JsonSerializer.Serialize or `@Json.Encode` for AppContainer and Handler when
passing to BitBswupProgress.start (and ensure the surrounding quotes are removed
if you emit the JSON string literal directly), leaving other boolean parameters
unchanged.
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts (2)

164-169: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail the install when the manifest is invalid.

Returning at Line 169 marks this worker's install as successful even though it has no usable asset manifest. On a first install—or once the previous worker ages out—that leaves a broken worker free to activate and take control with an empty precache. Throw here after sendError(...) so the browser rejects the install and keeps the last good worker.

Suggested fix
     if (!MANIFEST_VALID) {
         // The manifest is missing/malformed - sendError already notified the page. Don't
         // build a cache from an empty/partial asset list (it would replace the previous good
         // cache with a broken one). Let the lifecycle settle without caching anything.
         diag('*** skipping install - invalid assetsManifest.');
-        return;
+        throw new Error('Invalid service-worker-assets manifest.');
     }
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts` around lines 164 - 169, The
install handler currently returns when MANIFEST_VALID is false, which treats the
install as successful; after calling sendError(...) and logging via diag('***
skipping install - invalid assetsManifest.'), change the control flow to throw
an Error (e.g., throw new Error('invalid assetsManifest')) instead of returning
so the browser rejects the install and preserves the previous worker; update the
branch that checks MANIFEST_VALID (the block that calls sendError and diag) to
throw immediately after sendError(...) to fail the install.

93-94: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep VERSION in the cache bucket identity.

cacheVersion currently replaces the entire cache name. If an app pins it across publishes, the waiting worker mutates the same bucket the active worker is serving, and the strict-mode cleanup at Line 468 can delete that active cache on failure. Include both VERSION and CACHE_VERSION in CACHE_NAME so installs stay atomic.

Suggested fix
-const CACHE_NAME = `${CACHE_NAME_PREFIX} - ${CACHE_VERSION}`;
+const CACHE_NAME = `${CACHE_NAME_PREFIX} - ${VERSION} - ${CACHE_VERSION}`;

Also applies to: 467-468

🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts` around lines 93 - 94, CACHE_NAME
currently uses only CACHE_VERSION which allows a waiting worker to mutate the
active worker's cache; change CACHE_NAME construction to include both VERSION
and CACHE_VERSION (e.g., incorporate VERSION alongside CACHE_VERSION and
CACHE_NAME_PREFIX) so cache buckets remain unique per published version; update
any other places that build or compare cache names (the occurrences around
CACHE_NAME and the cleanup logic near the strict-mode cleanup at the later block
that references CACHE_NAME) to use the new combined identity so installs remain
atomic.
🤖 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 `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 245-249: The response for prohibited URLs currently returns a 403
(in the Response constructor that returns 'This URL is prohibited!'), but the
Bswup contract expects 405; update the Response to use status 405 and an
appropriate statusText (e.g., 'Method Not Allowed') so the service-worker
preserves the documented behavior for PROHIBITED_URLS; locate the new
Response(...) call that contains the message 'This URL is prohibited!' and
change its status/statusText accordingly.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Around line 150-157: When reg.waiting becomes present in the updatefound path
we currently only log and return; updateReady must be emitted there as well.
Update the logic around reg.waiting/reg.installing in bit-bswup.ts so that
whenever reg.waiting is detected (both on initial check and inside the
registration.updatefound handler) you call handle(BswupMessage.updateReady, {
reload }) instead of only logging; also ensure the branch that logs
reg.installing still emits updateReady once installing transitions to waiting.
Reference reg.waiting, reg.installing, handle, and BswupMessage.updateReady to
locate and change the two spots (the initial check and the updatefound path).
- Around line 237-241: The CLIENTS_CLAIMED handler currently calls
Blazor.start() directly and resolves blazorStartResolver in then, which bypasses
startBlazor() validation and leaves blazorStartResolver unresolved if start
fails; change the handler to call the existing startBlazor() helper (or wrap
Blazor.start() with the same try/catch and rejection handling), ensure
blazorStartResolver is resolved or rejected in all paths, and
postMessage('BLAZOR_STARTED') only on success; update the CLIENTS_CLAIMED branch
around the Blazor.start() call to use startBlazor() (or its internal validation
flow) and ensure failures trigger the same rejection path so
createAssetsCache(true) and the first-install reload promise are not left
hanging.

---

Duplicate comments:
In `@src/Bswup/Bit.Bswup/BswupProgress.razor`:
- Around line 37-46: The inline script calls window.BitBswupProgress.start with
component parameters AppContainer and Handler injected into single-quoted JS
literals which can break or lead to XSS; update the Razor to
JS-encode/JSON-stringify those parameters instead of embedding raw Razor
output—use a safe serializer like JsonSerializer.Serialize or `@Json.Encode` for
AppContainer and Handler when passing to BitBswupProgress.start (and ensure the
surrounding quotes are removed if you emit the JSON string literal directly),
leaving other boolean parameters unchanged.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 164-169: The install handler currently returns when MANIFEST_VALID
is false, which treats the install as successful; after calling sendError(...)
and logging via diag('*** skipping install - invalid assetsManifest.'), change
the control flow to throw an Error (e.g., throw new Error('invalid
assetsManifest')) instead of returning so the browser rejects the install and
preserves the previous worker; update the branch that checks MANIFEST_VALID (the
block that calls sendError and diag) to throw immediately after sendError(...)
to fail the install.
- Around line 93-94: CACHE_NAME currently uses only CACHE_VERSION which allows a
waiting worker to mutate the active worker's cache; change CACHE_NAME
construction to include both VERSION and CACHE_VERSION (e.g., incorporate
VERSION alongside CACHE_VERSION and CACHE_NAME_PREFIX) so cache buckets remain
unique per published version; update any other places that build or compare
cache names (the occurrences around CACHE_NAME and the cleanup logic near the
strict-mode cleanup at the later block that references CACHE_NAME) to use the
new combined identity so installs remain atomic.
🪄 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: 1c746829-9104-47db-9d52-410bc2c80ac6

📥 Commits

Reviewing files that changed from the base of the PR and between 046d116 and 23b29b3.

📒 Files selected for processing (9)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md
💤 Files with no reviewable changes (1)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
@msynk msynk requested a review from Copilot June 6, 2026 07:11
@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 6, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

@msynk: Sure, triggering a fresh review of the PR now.

✅ Action performed

Full review finished.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts (2)

237-241: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle Blazor.start() failure in CLIENTS_CLAIMED to avoid hanging first-install flow.

On Line 238, Blazor.start() is invoked without a rejection path. If it rejects, blazorStartResolver is never resolved and callers awaiting data.reload() can stall indefinitely.

Suggested fix
                 if (e.data === 'CLIENTS_CLAIMED') {
                     Blazor.start().then(() => {
                         blazorStartResolver?.(undefined);
-                        e.source.postMessage('BLAZOR_STARTED');
-                    });
+                        e.source?.postMessage('BLAZOR_STARTED');
+                    }).catch((err) => {
+                        error('Blazor start failed after CLIENTS_CLAIMED', err);
+                        blazorStartResolver?.(undefined);
+                    });

                     return;
                 }
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts` around lines 237 - 241,
Blazor.start() is called in the CLIENTS_CLAIMED handler without a rejection
path, which can leave blazorStartResolver unresolved and stall callers (e.g.,
data.reload()); update the handler around Blazor.start() so that both success
and failure paths always call blazorStartResolver (resolve or reject as
appropriate) and always post back to e.source (e.g., send a success or failure
message like 'BLAZOR_STARTED' vs 'BLAZOR_START_FAILED'), ensuring
Blazor.start().then(...) has a corresponding .catch(...) that resolves/rejects
blazorStartResolver and posts the failure message so no flow can hang.

169-180: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Emit updateReady when install transitions to waiting in the same session.

On Line 173 onward, once reg.waiting appears in the statechange path, the code logs completion but never calls handle(BswupMessage.updateReady, { reload }). That leaves listeners without the public update-ready signal for updates discovered while the tab stays open.

Suggested fix
                     reg.installing.addEventListener('statechange', function (e) {
                         debug('state changed', e, 'eventPhase:', e.eventPhase, 'currentTarget.state:', e.currentTarget.state);
                         handle(BswupMessage.stateChanged, e);

-                        if (!reg.waiting) return;
+                        if (!reg.waiting) return;
+                        if ((e.currentTarget as ServiceWorker).state !== 'installed') return;
+                        handle(BswupMessage.updateReady, { reload });

                         if (hadActiveWorkerAtStartup) {
                             info('update finished.'); // not first install
                         } else {
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts` around lines 169 - 180, When
reg.installing's 'statechange' handler detects reg.waiting, besides logging,
emit the public update-ready message by calling handle(BswupMessage.updateReady,
{ reload }) so listeners get notified for updates discovered in the same
session; locate the reg.installing.addEventListener callback and add the
handle(BswupMessage.updateReady, { reload }) call when reg.waiting is true,
using the existing reload variable (or defaulting to false if reload isn't in
scope).
src/Bswup/Bit.Bswup/BswupProgress.razor (1)

43-46: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

JS-encode AppContainer and Handler before embedding into the inline script.

On Line 43 and Line 46, these values are injected as raw single-quoted JS literals. A quote/backslash/newline payload can break the string and inject script.

Suggested fix
+@using System.Text.Json
+
     <script>
         if (window.BitBswupProgress !== undefined) {
             window.BitBswupProgress.start(
                 @(AutoReload ? "true" : "false"), 
                 @(ShowLogs ? "true" : "false"), 
                 @(ShowAssets ? "true" : "false"), 
-                '@(AppContainer)', 
+                @((MarkupString)JsonSerializer.Serialize(AppContainer)), 
                 @(HideApp ? "true" : "false"), 
                 @(AutoHide ? "true" : "false"), 
-                '@(Handler)');
+                @((MarkupString)JsonSerializer.Serialize(Handler)));
         } else {
             console.error('BitBswupProgress not found');
         }
     </script>
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/BswupProgress.razor` around lines 43 - 46, The inline
script is embedding AppContainer and Handler as raw single-quoted JS literals
which allows quote/backslash/newline payloads to break out; change the embedding
to produce safe JS string literals by serializing these C# values with a JSON
serializer (e.g. System.Text.Json.JsonSerializer.Serialize) and inject the
serialized result (remove the extra single quotes around them) so AppContainer
and Handler become valid JS-encoded literals; ensure you add the required
using/import for System.Text.Json if missing and update the occurrences where
'@(AppContainer)' and '@(Handler)' are used in BswupProgress.razor.
🧹 Nitpick comments (1)
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts (1)

762-782: 💤 Low value

Static analysis flagged potential ReDoS, but risk is low.

The new RegExp(p) on line 772 constructs regex from developer-configured values (self.prohibitedUrls, self.serverHandledUrls, etc.), not untrusted user input. An attacker would need to modify the service-worker.js file itself to inject a malicious pattern.

While the catch block handles syntax errors, it doesn't guard against pathological patterns. Consider documenting best practices for pattern authors (avoid nested quantifiers like (a+)+) in the README, but adding a ReDoS validation library seems disproportionate for this threat model.

🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts` around lines 762 - 782, Static
analysis flagged potential ReDoS from constructing RegExp from
developer-configured values; keep the existing try/catch in prepareRegExpArray
but also add a short developer-facing note documenting safe pattern practices
(e.g., avoid catastrophic backtracking / nested quantifiers like (a+)+) and
recommend limiting pattern length; update the repository README or the service
worker module comment near prepareRegExpArray and reference the configuration
sources (self.prohibitedUrls, self.serverHandledUrls, etc.) so maintainers know
the threat model and how to author safe patterns.

Source: Linters/SAST tools

🤖 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.

Duplicate comments:
In `@src/Bswup/Bit.Bswup/BswupProgress.razor`:
- Around line 43-46: The inline script is embedding AppContainer and Handler as
raw single-quoted JS literals which allows quote/backslash/newline payloads to
break out; change the embedding to produce safe JS string literals by
serializing these C# values with a JSON serializer (e.g.
System.Text.Json.JsonSerializer.Serialize) and inject the serialized result
(remove the extra single quotes around them) so AppContainer and Handler become
valid JS-encoded literals; ensure you add the required using/import for
System.Text.Json if missing and update the occurrences where '@(AppContainer)'
and '@(Handler)' are used in BswupProgress.razor.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Around line 237-241: Blazor.start() is called in the CLIENTS_CLAIMED handler
without a rejection path, which can leave blazorStartResolver unresolved and
stall callers (e.g., data.reload()); update the handler around Blazor.start() so
that both success and failure paths always call blazorStartResolver (resolve or
reject as appropriate) and always post back to e.source (e.g., send a success or
failure message like 'BLAZOR_STARTED' vs 'BLAZOR_START_FAILED'), ensuring
Blazor.start().then(...) has a corresponding .catch(...) that resolves/rejects
blazorStartResolver and posts the failure message so no flow can hang.
- Around line 169-180: When reg.installing's 'statechange' handler detects
reg.waiting, besides logging, emit the public update-ready message by calling
handle(BswupMessage.updateReady, { reload }) so listeners get notified for
updates discovered in the same session; locate the
reg.installing.addEventListener callback and add the
handle(BswupMessage.updateReady, { reload }) call when reg.waiting is true,
using the existing reload variable (or defaulting to false if reload isn't in
scope).

---

Nitpick comments:
In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 762-782: Static analysis flagged potential ReDoS from constructing
RegExp from developer-configured values; keep the existing try/catch in
prepareRegExpArray but also add a short developer-facing note documenting safe
pattern practices (e.g., avoid catastrophic backtracking / nested quantifiers
like (a+)+) and recommend limiting pattern length; update the repository README
or the service worker module comment near prepareRegExpArray and reference the
configuration sources (self.prohibitedUrls, self.serverHandledUrls, etc.) so
maintainers know the threat model and how to author safe patterns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d8ed2388-cbc2-4b55-9764-b9752f60e6d8

📥 Commits

Reviewing files that changed from the base of the PR and between 046d116 and 088c6d1.

📒 Files selected for processing (9)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md
💤 Files with no reviewable changes (1)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js

@msynk
Copy link
Copy Markdown
Member Author

msynk commented Jun 6, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

@msynk: Sure, triggering a fresh review of the PR now.

✅ Action performed

Full review finished.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts (1)

559-562: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Narrow integrity classification to avoid suppressing retries on transient failures.

Line 561 still treats broad transport markers (notably ERR_FAILED) as integrity failures, which can classify transient network errors as non-retriable and skip backoff.

Suggested patch
-                const isIntegrity =
-                    hasIntegrity &&
-                    /integrity|digest|EPRPROTO|ERR_FAILED/i.test(String(fetchErr && (fetchErr as any).message || fetchErr));
+                const isIntegrity =
+                    hasIntegrity &&
+                    /integrity|digest|subresource integrity|valid digest/i.test(
+                        String((fetchErr && (fetchErr as any).message) || fetchErr)
+                    );
🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts` around lines 559 - 562, The
integrity classification currently treats broad transport errors like
"ERR_FAILED" as integrity failures; update the isIntegrity computation in the
bit-bswup.sw.ts code (the const isIntegrity assignment that tests fetchErr) to
remove/omit "ERR_FAILED" from the regex so only actual integrity indicators
(e.g., "integrity", "digest", "EPRPROTO") are matched; preserve the hasIntegrity
guard and the existing String(fetchErr... ) usage and keep the /.../i flag so
transient transport errors no longer suppress retries/backoff.
src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts (1)

243-253: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use startBlazor(true) in CLIENTS_CLAIMED to keep one startup contract.

Line 244 calls Blazor.start() directly and bypasses the validations in startBlazor() (Line 377+). This keeps first-install startup behavior inconsistent with the rest of the runtime and can surface avoidable startup failures.

Proposed fix
-                if (e.data === 'CLIENTS_CLAIMED') {
-                    Blazor.start().then(() => {
-                        blazorStartResolver?.(undefined);
-                        e.source.postMessage('BLAZOR_STARTED');
-                    }).catch((err) => {
-                        error('Blazor.start() failed after clients claimed', err);
-                        // Always settle the pending reload() promise so callers can't hang, and
-                        // notify the worker that startup failed (mirrors the success path).
-                        blazorStartResolver?.(undefined);
-                        e.source.postMessage('BLAZOR_START_FAILED');
-                    });
+                if (e.data === 'CLIENTS_CLAIMED') {
+                    Promise.resolve()
+                        .then(() => startBlazor(true))
+                        .then(() => {
+                            blazorStartResolver?.(undefined);
+                            e.source?.postMessage('BLAZOR_STARTED');
+                        })
+                        .catch((err) => {
+                            error('Blazor.start() failed after clients claimed', err);
+                            blazorStartResolver?.(undefined);
+                            e.source?.postMessage('BLAZOR_START_FAILED');
+                        });
 
                     return;
                 }

Also applies to: 377-397

🤖 Prompt for 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.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts` around lines 243 - 253, The
CLIENTS_CLAIMED handler is calling Blazor.start() directly and bypassing the
unified startup flow; replace that call with startBlazor(true) so the same
validation/retry/contract logic is used (keep existing success and failure
handling: call blazorStartResolver?.(undefined) and post 'BLAZOR_STARTED' or
'BLAZOR_START_FAILED' to e.source). Also search for other direct Blazor.start()
uses (e.g., the similar block covered by startBlazor() at the other location)
and ensure they call startBlazor(true) instead to maintain a single startup
contract.
🤖 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 `@src/Bswup/Bit.Bswup/BswupProgress.razor`:
- Line 1: The startup script in BswupProgress.razor currently uses
System.Text.Json.JsonSerializer.Serialize to embed plain string parameters into
the generated JS which triggers trimming/AOT IL2026 warnings; replace those
calls by encoding the string values with
System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode before inserting into
the script, add the using for System.Text.Encodings.Web if absent, and ensure
you pass the encoded string (not Json-serialized) into the startup args so the
embedded values are safe for JS and avoid reflection-based serialization.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 196-200: createAssetsCache() is invoked in lax-mode without
handling promise rejection, which risks unhandled rejections; wrap the call to
createAssetsCache (or append a .catch) so any thrown error is caught and handled
(e.g., log a structured error and preserve lax behavior), referencing the
errorTolerance: 'lax' flow and handleFetch lazy-fill path. Specifically, change
the invocation in the lax branch to either await createAssetsCache() inside a
try/catch or call createAssetsCache().catch(err =>
processLogger.error("createAssetsCache failed (lax):", err)) so failures are
recorded and do not surface as unhandled rejections while keeping lifecycle
progression.

---

Duplicate comments:
In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts`:
- Around line 559-562: The integrity classification currently treats broad
transport errors like "ERR_FAILED" as integrity failures; update the isIntegrity
computation in the bit-bswup.sw.ts code (the const isIntegrity assignment that
tests fetchErr) to remove/omit "ERR_FAILED" from the regex so only actual
integrity indicators (e.g., "integrity", "digest", "EPRPROTO") are matched;
preserve the hasIntegrity guard and the existing String(fetchErr... ) usage and
keep the /.../i flag so transient transport errors no longer suppress
retries/backoff.

In `@src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts`:
- Around line 243-253: The CLIENTS_CLAIMED handler is calling Blazor.start()
directly and bypassing the unified startup flow; replace that call with
startBlazor(true) so the same validation/retry/contract logic is used (keep
existing success and failure handling: call blazorStartResolver?.(undefined) and
post 'BLAZOR_STARTED' or 'BLAZOR_START_FAILED' to e.source). Also search for
other direct Blazor.start() uses (e.g., the similar block covered by
startBlazor() at the other location) and ensure they call startBlazor(true)
instead to maintain a single startup contract.
🪄 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: ecdc4731-1284-46fc-958a-9cdf16f4e399

📥 Commits

Reviewing files that changed from the base of the PR and between 29f3696 and 8c11506.

📒 Files selected for processing (9)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js
  • src/Bswup/Bit.Bswup/BswupProgress.razor
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw-cleanup.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
  • src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts
  • src/Bswup/Bit.Bswup/Styles/bit-bswup.progress.css
  • src/Bswup/README.md
💤 Files with no reviewable changes (1)
  • src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js

Comment thread src/Bswup/Bit.Bswup/BswupProgress.razor Outdated
Comment thread src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts
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.

bit Bswup improvements

2 participants