@ovineko/spa-guard 0.0.2-alpha-0 → 0.0.3-alpha-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,9 +92,12 @@ The orchestrator serializes state into URL params for cross-reload continuity:
92
92
 
93
93
  When `options.html.loading.content` is configured, `showLoadingUI(attempt)` is called before the reload timer fires. It injects the loading HTML into the target element (selector from `options.html.fallback.selector`, defaulting to `body`), reveals the `data-spa-guard-section="retrying"` element, fills attempt numbers into `[data-spa-guard-content="attempt"]` elements, applies i18n via `applyI18n`/`getI18n`, and optionally hides or replaces the spinner based on `options.html.spinner`. If loading content is not configured or the target element is not found, `showLoadingUI` returns silently — the retry still proceeds normally.
94
94
 
95
+ Default fallback/loading templates are theme-aware: they set `color-scheme: light dark` and apply neutral dark colors automatically via `@media (prefers-color-scheme: dark)`.
96
+
95
97
  ### Unhandledrejection serialization
96
98
 
97
99
  `serializeError` handles `PromiseRejectionEvent` with strict redaction guardrails. For HTTP-like errors it extracts only safe metadata: `status`, `statusText`, `url`, `method`, `response.type`, and `X-Request-ID` from response headers when present. Response body, request/response payload, and the full headers object are **never** included in serialized output. For request wrappers (`reason.request` / `reason.config`) only `method`, `url`, and `baseURL` are extracted. Deep object traversal is bounded by `MAX_DEPTH=4`, `MAX_KEYS=20`, and `MAX_STRING_LEN=500` to prevent oversized beacons. Circular references are handled via a `WeakSet` visited tracker. The output also includes `isTrusted`, `timeStamp` from the event, and runtime context (`pageUrl`, `constructorName`).
100
+ This HTTP-like extraction also applies to `Error` subclasses that carry a `response` field (for example `ResponseError`), not only to plain objects.
98
101
 
99
102
  ### Retry reset
100
103
 
package/dist/_internal.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  isChunkError,
4
4
  listenInternal,
5
5
  serializeError
6
- } from "./chunk-4K3AQHLC.js";
6
+ } from "./chunk-XIFXSNSD.js";
7
7
  import {
8
8
  SPINNER_ID,
9
9
  defaultSpinnerSvg,
10
10
  extractVersionFromHtml,
11
11
  sanitizeCssValue
12
- } from "./chunk-T42DLOQP.js";
12
+ } from "./chunk-3UJ67DPX.js";
13
13
  import {
14
14
  dispatchAsyncRuntimeError,
15
15
  dispatchChunkLoadError,
@@ -18,7 +18,7 @@ import {
18
18
  dispatchNetworkTimeout,
19
19
  dispatchSyncRuntimeError,
20
20
  dispatchUnhandledRejection
21
- } from "./chunk-ORYUDYHU.js";
21
+ } from "./chunk-PERG4557.js";
22
22
  import {
23
23
  applyI18n,
24
24
  debugSyncErrorEventType,
@@ -40,7 +40,7 @@ import {
40
40
  shouldIgnoreMessages,
41
41
  subscribe,
42
42
  triggerRetry
43
- } from "./chunk-LIKSIU75.js";
43
+ } from "./chunk-GE63YJOT.js";
44
44
  import "./chunk-MLKGABMK.js";
45
45
 
46
46
  // src/common/handleErrorWithSpaGuard.ts
@@ -2,7 +2,7 @@ import {
2
2
  defaultSpinnerHtml,
3
3
  getOptions,
4
4
  spinnerStateWindowKey
5
- } from "./chunk-LIKSIU75.js";
5
+ } from "./chunk-GE63YJOT.js";
6
6
 
7
7
  // src/common/parseVersion.ts
8
8
  function extractVersionFromHtml(html) {
@@ -135,8 +135,8 @@ function setTranslations(translations) {
135
135
  }
136
136
 
137
137
  // src/common/html.generated.ts
138
- var defaultErrorFallbackHtml = `<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",Menlo,monospace}</style><div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif"><div style="text-align:center;max-width:480px"><div style="margin-bottom:1.5rem"><svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 24 24" stroke="#b0b0b0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div><h1 data-spa-guard-content="heading" style="font-size:1.375rem;font-weight:600;margin:0 0 .5rem;color:#1a1a1a;line-height:1.3">Something went wrong</h1><p data-spa-guard-content="message" style="max-width:600px;margin:0 auto 1.5rem;color:#666;font-size:.9375rem;line-height:1.5">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap"><button data-spa-guard-action="try-again" type="button" style="display:none;padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid #d0d0d0;background:#fff;color:#333;cursor:pointer;line-height:1.5">Try again</button> <button data-spa-guard-action="reload" type="button" style="padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid transparent;background:#111;color:#fff;cursor:pointer;line-height:1.5">Reload page</button></div><p class="spa-guard-error-id" style="margin-top:1.5rem;font-size:.6875rem;color:#999">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>`;
139
- var defaultLoadingFallbackHtml = `<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif"><div style="text-align:center"><div data-spa-guard-spinner style="margin-bottom:1.25rem"></div><h2 data-spa-guard-content="loading" style="font-size:1.125rem;font-weight:600;margin:0 0 .25rem;color:#1a1a1a">Loading...</h2><p data-spa-guard-section="retrying" style="display:none;font-size:.8125rem;color:#999;margin:.5rem 0 0"><span data-spa-guard-content="retrying">Retry attempt</span> <span data-spa-guard-content="attempt"></span></p></div></div>`;
138
+ var defaultErrorFallbackHtml = `<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",Menlo,monospace}.spa-guard-fallback-root{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif;background:#fff;color:#1a1a1a;color-scheme:light dark}.spa-guard-fallback-icon{stroke:#b0b0b0}.spa-guard-fallback-message{color:#666}.spa-guard-fallback-muted{color:#999}.spa-guard-btn-secondary{border:1px solid #d0d0d0;background:#fff;color:#333}.spa-guard-btn-primary{border:1px solid transparent;background:#111;color:#fff}@media (prefers-color-scheme:dark){.spa-guard-fallback-root{background:#111318;color:#e7eaf0}.spa-guard-fallback-icon{stroke:#8b95a7}.spa-guard-fallback-message{color:#b8bfca}.spa-guard-fallback-muted{color:#8b95a7}.spa-guard-btn-secondary{border-color:#3b4351;background:#1a1f28;color:#d8deea}.spa-guard-btn-primary{background:#e7eaf0;color:#151922}}</style><div class="spa-guard-fallback-root"><div style="text-align:center;max-width:480px"><div style="margin-bottom:1.5rem"><svg class="spa-guard-fallback-icon" xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div><h1 data-spa-guard-content="heading" style="font-size:1.375rem;font-weight:600;margin:0 0 .5rem;line-height:1.3">Something went wrong</h1><p data-spa-guard-content="message" class="spa-guard-fallback-message" style="max-width:600px;margin:0 auto 1.5rem;font-size:.9375rem;line-height:1.5">Please refresh the page to continue.</p><div style="display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap"><button data-spa-guard-action="try-again" type="button" class="spa-guard-btn-secondary" style="display:none;padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;cursor:pointer;line-height:1.5">Try again</button> <button data-spa-guard-action="reload" type="button" class="spa-guard-btn-primary" style="padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;cursor:pointer;line-height:1.5">Reload page</button></div><p class="spa-guard-error-id spa-guard-fallback-muted" style="margin-top:1.5rem;font-size:.6875rem">Error ID: <span class="spa-guard-retry-id"></span></p></div></div>`;
139
+ var defaultLoadingFallbackHtml = `<style>.spa-guard-loading-root{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif;background:#fff;color:#1a1a1a;color-scheme:light dark}.spa-guard-loading-muted{color:#999}@media (prefers-color-scheme:dark){.spa-guard-loading-root{background:#111318;color:#e7eaf0}.spa-guard-loading-muted{color:#8b95a7}}</style><div class="spa-guard-loading-root"><div style="text-align:center"><div data-spa-guard-spinner style="margin-bottom:1.25rem"></div><h2 data-spa-guard-content="loading" style="font-size:1.125rem;font-weight:600;margin:0 0 .25rem">Loading...</h2><p data-spa-guard-section="retrying" class="spa-guard-loading-muted" style="display:none;font-size:.8125rem;margin:.5rem 0 0"><span data-spa-guard-content="retrying">Retry attempt</span> <span data-spa-guard-content="attempt"></span></p></div></div>`;
140
140
  var defaultSpinnerHtml = `<svg width="40" height="40" viewBox="0 0 40 40" style="animation:spa-guard-spin .8s linear infinite"><circle cx="20" cy="20" r="16" fill="none" stroke="#e8e8e8" stroke-width="3"/><circle cx="20" cy="20" r="16" fill="none" stroke="#666" stroke-width="3" stroke-dasharray="80" stroke-dashoffset="60" stroke-linecap="round"/></svg><style>@keyframes spa-guard-spin{to{transform:rotate(360deg)}}</style>`;
141
141
 
142
142
  // src/common/options.ts
@@ -4,7 +4,7 @@ import {
4
4
  emitEvent,
5
5
  getOptions,
6
6
  setFallbackStateForDebug
7
- } from "./chunk-LIKSIU75.js";
7
+ } from "./chunk-GE63YJOT.js";
8
8
 
9
9
  // src/runtime/debug/errorDispatchers.ts
10
10
  function dispatchAsyncRuntimeError() {
@@ -3,7 +3,7 @@ import {
3
3
  getRetryAttemptFromUrl,
4
4
  getRetryStateFromUrl,
5
5
  subscribe
6
- } from "./chunk-LIKSIU75.js";
6
+ } from "./chunk-GE63YJOT.js";
7
7
 
8
8
  // src/runtime/state.ts
9
9
  var getInitialStateFromUrl = () => {
@@ -11,7 +11,7 @@ import {
11
11
  shouldIgnoreMessages,
12
12
  staticAssetRecoveryKey,
13
13
  triggerRetry
14
- } from "./chunk-LIKSIU75.js";
14
+ } from "./chunk-GE63YJOT.js";
15
15
 
16
16
  // src/common/isChunkError.ts
17
17
  var isChunkError = (error) => {
@@ -157,9 +157,6 @@ var serializeRejectionReason = (reason, visited, depth) => {
157
157
  type: "Error"
158
158
  };
159
159
  }
160
- if (reason instanceof Error) {
161
- return serializeSafeError(reason, visited, depth);
162
- }
163
160
  const reasonObj = reason;
164
161
  if (reasonObj.response != null) {
165
162
  const response = reasonObj.response;
@@ -212,6 +209,9 @@ var serializeRejectionReason = (reason, visited, depth) => {
212
209
  }
213
210
  return result;
214
211
  }
212
+ if (reason instanceof Error) {
213
+ return serializeSafeError(reason, visited, depth);
214
+ }
215
215
  return {
216
216
  type: "object",
217
217
  value: extractBoundedObject(reasonObj, visited, depth)
@@ -1,3 +1,3 @@
1
- export declare const defaultErrorFallbackHtml = "<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:ui-monospace,SFMono-Regular,Consolas,\"Liberation Mono\",Menlo,monospace}</style><div style=\"display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif\"><div style=\"text-align:center;max-width:480px\"><div style=\"margin-bottom:1.5rem\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"#b0b0b0\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg></div><h1 data-spa-guard-content=\"heading\" style=\"font-size:1.375rem;font-weight:600;margin:0 0 .5rem;color:#1a1a1a;line-height:1.3\">Something went wrong</h1><p data-spa-guard-content=\"message\" style=\"max-width:600px;margin:0 auto 1.5rem;color:#666;font-size:.9375rem;line-height:1.5\">Please refresh the page to continue.</p><div style=\"display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap\"><button data-spa-guard-action=\"try-again\" type=\"button\" style=\"display:none;padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid #d0d0d0;background:#fff;color:#333;cursor:pointer;line-height:1.5\">Try again</button> <button data-spa-guard-action=\"reload\" type=\"button\" style=\"padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;border:1px solid transparent;background:#111;color:#fff;cursor:pointer;line-height:1.5\">Reload page</button></div><p class=\"spa-guard-error-id\" style=\"margin-top:1.5rem;font-size:.6875rem;color:#999\">Error ID: <span class=\"spa-guard-retry-id\"></span></p></div></div>";
2
- export declare const defaultLoadingFallbackHtml = "<div style=\"display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif\"><div style=\"text-align:center\"><div data-spa-guard-spinner style=\"margin-bottom:1.25rem\"></div><h2 data-spa-guard-content=\"loading\" style=\"font-size:1.125rem;font-weight:600;margin:0 0 .25rem;color:#1a1a1a\">Loading...</h2><p data-spa-guard-section=\"retrying\" style=\"display:none;font-size:.8125rem;color:#999;margin:.5rem 0 0\"><span data-spa-guard-content=\"retrying\">Retry attempt</span> <span data-spa-guard-content=\"attempt\"></span></p></div></div>";
1
+ export declare const defaultErrorFallbackHtml = "<style>.spa-guard-error-id:has(.spa-guard-retry-id:empty){display:none}.spa-guard-error-id{font-family:ui-monospace,SFMono-Regular,Consolas,\"Liberation Mono\",Menlo,monospace}.spa-guard-fallback-root{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif;background:#fff;color:#1a1a1a;color-scheme:light dark}.spa-guard-fallback-icon{stroke:#b0b0b0}.spa-guard-fallback-message{color:#666}.spa-guard-fallback-muted{color:#999}.spa-guard-btn-secondary{border:1px solid #d0d0d0;background:#fff;color:#333}.spa-guard-btn-primary{border:1px solid transparent;background:#111;color:#fff}@media (prefers-color-scheme:dark){.spa-guard-fallback-root{background:#111318;color:#e7eaf0}.spa-guard-fallback-icon{stroke:#8b95a7}.spa-guard-fallback-message{color:#b8bfca}.spa-guard-fallback-muted{color:#8b95a7}.spa-guard-btn-secondary{border-color:#3b4351;background:#1a1f28;color:#d8deea}.spa-guard-btn-primary{background:#e7eaf0;color:#151922}}</style><div class=\"spa-guard-fallback-root\"><div style=\"text-align:center;max-width:480px\"><div style=\"margin-bottom:1.5rem\"><svg class=\"spa-guard-fallback-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg></div><h1 data-spa-guard-content=\"heading\" style=\"font-size:1.375rem;font-weight:600;margin:0 0 .5rem;line-height:1.3\">Something went wrong</h1><p data-spa-guard-content=\"message\" class=\"spa-guard-fallback-message\" style=\"max-width:600px;margin:0 auto 1.5rem;font-size:.9375rem;line-height:1.5\">Please refresh the page to continue.</p><div style=\"display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap\"><button data-spa-guard-action=\"try-again\" type=\"button\" class=\"spa-guard-btn-secondary\" style=\"display:none;padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;cursor:pointer;line-height:1.5\">Try again</button> <button data-spa-guard-action=\"reload\" type=\"button\" class=\"spa-guard-btn-primary\" style=\"padding:.5rem 1.25rem;font-size:.875rem;font-family:inherit;border-radius:6px;cursor:pointer;line-height:1.5\">Reload page</button></div><p class=\"spa-guard-error-id spa-guard-fallback-muted\" style=\"margin-top:1.5rem;font-size:.6875rem\">Error ID: <span class=\"spa-guard-retry-id\"></span></p></div></div>";
2
+ export declare const defaultLoadingFallbackHtml = "<style>.spa-guard-loading-root{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem;font-family:system-ui,sans-serif;background:#fff;color:#1a1a1a;color-scheme:light dark}.spa-guard-loading-muted{color:#999}@media (prefers-color-scheme:dark){.spa-guard-loading-root{background:#111318;color:#e7eaf0}.spa-guard-loading-muted{color:#8b95a7}}</style><div class=\"spa-guard-loading-root\"><div style=\"text-align:center\"><div data-spa-guard-spinner style=\"margin-bottom:1.25rem\"></div><h2 data-spa-guard-content=\"loading\" style=\"font-size:1.125rem;font-weight:600;margin:0 0 .25rem\">Loading...</h2><p data-spa-guard-section=\"retrying\" class=\"spa-guard-loading-muted\" style=\"display:none;font-size:.8125rem;margin:.5rem 0 0\"><span data-spa-guard-content=\"retrying\">Retry attempt</span> <span data-spa-guard-content=\"attempt\"></span></p></div></div>";
3
3
  export declare const defaultSpinnerHtml = "<svg width=\"40\" height=\"40\" viewBox=\"0 0 40 40\" style=\"animation:spa-guard-spin .8s linear infinite\"><circle cx=\"20\" cy=\"20\" r=\"16\" fill=\"none\" stroke=\"#e8e8e8\" stroke-width=\"3\"/><circle cx=\"20\" cy=\"20\" r=\"16\" fill=\"none\" stroke=\"#666\" stroke-width=\"3\" stroke-dasharray=\"80\" stroke-dashoffset=\"60\" stroke-linecap=\"round\"/></svg><style>@keyframes spa-guard-spin{to{transform:rotate(360deg)}}</style>";
@@ -2,7 +2,7 @@ import {
2
2
  createLogger,
3
3
  listenInternal,
4
4
  serializeError
5
- } from "../chunk-4K3AQHLC.js";
5
+ } from "../chunk-XIFXSNSD.js";
6
6
  import {
7
7
  ForceRetryError,
8
8
  disableDefaultRetry,
@@ -16,7 +16,7 @@ import {
16
16
  resetFallbackMode,
17
17
  subscribe,
18
18
  triggerRetry
19
- } from "../chunk-LIKSIU75.js";
19
+ } from "../chunk-GE63YJOT.js";
20
20
  import {
21
21
  __export
22
22
  } from "../chunk-MLKGABMK.js";
@@ -8,13 +8,13 @@ import {
8
8
  dispatchStaticAsset404,
9
9
  dispatchSyncRuntimeError,
10
10
  dispatchUnhandledRejection
11
- } from "../../chunk-ORYUDYHU.js";
11
+ } from "../../chunk-PERG4557.js";
12
12
  import {
13
13
  subscribeToState
14
- } from "../../chunk-IZAXNDHZ.js";
14
+ } from "../../chunk-VZ2DLGXX.js";
15
15
  import {
16
16
  subscribe
17
- } from "../../chunk-LIKSIU75.js";
17
+ } from "../../chunk-GE63YJOT.js";
18
18
  import "../../chunk-MLKGABMK.js";
19
19
 
20
20
  // src/runtime/debug/index.ts
@@ -3,11 +3,11 @@ import {
3
3
  extractVersionFromHtml,
4
4
  getSpinnerHtml,
5
5
  showSpinner
6
- } from "../chunk-T42DLOQP.js";
6
+ } from "../chunk-3UJ67DPX.js";
7
7
  import {
8
8
  getState,
9
9
  subscribeToState
10
- } from "../chunk-IZAXNDHZ.js";
10
+ } from "../chunk-VZ2DLGXX.js";
11
11
  import {
12
12
  ForceRetryError,
13
13
  getLogger,
@@ -17,7 +17,7 @@ import {
17
17
  markRetryHealthyBoot,
18
18
  setTranslations,
19
19
  versionCheckStateWindowKey
20
- } from "../chunk-LIKSIU75.js";
20
+ } from "../chunk-GE63YJOT.js";
21
21
  import "../chunk-MLKGABMK.js";
22
22
 
23
23
  // src/common/checkVersion.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovineko/spa-guard",
3
- "version": "0.0.2-alpha-0",
3
+ "version": "0.0.3-alpha-0",
4
4
  "description": "Chunk load error handling for SPAs — core runtime, error handling, schema, i18n",
5
5
  "keywords": [
6
6
  "spa",