@ovineko/spa-guard 0.0.1-alpha-24 → 0.0.1-alpha-25

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/dist/_internal.js CHANGED
@@ -1,14 +1,14 @@
1
- import {
2
- SPINNER_ID,
3
- defaultSpinnerSvg,
4
- extractVersionFromHtml
5
- } from "./chunk-UF7QAEI7.js";
6
1
  import {
7
2
  createLogger,
8
3
  isChunkError,
9
4
  listenInternal,
10
5
  serializeError
11
- } from "./chunk-DUR4QNQE.js";
6
+ } from "./chunk-ZVYB2746.js";
7
+ import {
8
+ SPINNER_ID,
9
+ defaultSpinnerSvg,
10
+ extractVersionFromHtml
11
+ } from "./chunk-Z75UPJWV.js";
12
12
  import {
13
13
  dispatchAsyncRuntimeError,
14
14
  dispatchChunkLoadError,
@@ -17,13 +17,13 @@ import {
17
17
  dispatchNetworkTimeout,
18
18
  dispatchSyncRuntimeError,
19
19
  dispatchUnhandledRejection
20
- } from "./chunk-H4DWKO5I.js";
20
+ } from "./chunk-XFILAQ7P.js";
21
21
  import {
22
22
  attemptReload,
23
23
  sendBeacon,
24
24
  shouldForceRetry,
25
25
  shouldIgnoreMessages
26
- } from "./chunk-VZCUDNEG.js";
26
+ } from "./chunk-CSN7MQGX.js";
27
27
  import {
28
28
  applyI18n,
29
29
  debugSyncErrorEventType,
@@ -38,7 +38,7 @@ import {
38
38
  isDefaultRetryEnabled,
39
39
  optionsWindowKey,
40
40
  subscribe
41
- } from "./chunk-5DE7AKYB.js";
41
+ } from "./chunk-X6E7KUDK.js";
42
42
  import "./chunk-MLKGABMK.js";
43
43
 
44
44
  // src/common/handleErrorWithSpaGuard.ts
@@ -18,7 +18,7 @@ import {
18
18
  setLastReloadTime,
19
19
  setLastRetryResetInfo,
20
20
  shouldResetRetryCycle
21
- } from "./chunk-5DE7AKYB.js";
21
+ } from "./chunk-X6E7KUDK.js";
22
22
 
23
23
  // src/common/shouldIgnore.ts
24
24
  var shouldIgnoreMessages = (messages) => {
@@ -3,7 +3,7 @@ import {
3
3
  getRetryAttemptFromUrl,
4
4
  getRetryStateFromUrl,
5
5
  subscribe
6
- } from "./chunk-5DE7AKYB.js";
6
+ } from "./chunk-X6E7KUDK.js";
7
7
 
8
8
  // src/runtime/state.ts
9
9
  var getInitialStateFromUrl = () => {
@@ -13,6 +13,7 @@ var initializedKey = /* @__PURE__ */ Symbol.for(`${name}:initialized`);
13
13
  var loggerWindowKey = /* @__PURE__ */ Symbol.for(`${name}:logger`);
14
14
  var RETRY_ID_PARAM = "spaGuardRetryId";
15
15
  var RETRY_ATTEMPT_PARAM = "spaGuardRetryAttempt";
16
+ var versionCheckStateWindowKey = /* @__PURE__ */ Symbol.for(`${name}:version-check-state`);
16
17
  var debugSyncErrorEventType = "spa-guard:debug-sync-error";
17
18
 
18
19
  // src/common/events/internal.ts
@@ -141,6 +142,7 @@ __export(options_exports, {
141
142
  });
142
143
  var defaultOptions = {
143
144
  checkVersion: {
145
+ cache: "no-store",
144
146
  interval: 3e5,
145
147
  mode: "html",
146
148
  onUpdate: "reload"
@@ -418,6 +420,7 @@ export {
418
420
  optionsWindowKey,
419
421
  RETRY_ID_PARAM,
420
422
  RETRY_ATTEMPT_PARAM,
423
+ versionCheckStateWindowKey,
421
424
  debugSyncErrorEventType,
422
425
  setLogger,
423
426
  getLogger,
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  showFallbackUI
3
- } from "./chunk-VZCUDNEG.js";
3
+ } from "./chunk-CSN7MQGX.js";
4
4
  import {
5
5
  ForceRetryError,
6
6
  debugSyncErrorEventType,
7
7
  emitEvent,
8
8
  getOptions
9
- } from "./chunk-5DE7AKYB.js";
9
+ } from "./chunk-X6E7KUDK.js";
10
10
 
11
11
  // src/runtime/debug/errorDispatchers.ts
12
12
  function dispatchAsyncRuntimeError() {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defaultSpinnerHtml,
3
3
  getOptions
4
- } from "./chunk-5DE7AKYB.js";
4
+ } from "./chunk-X6E7KUDK.js";
5
5
 
6
6
  // src/common/parseVersion.ts
7
7
  function extractVersionFromHtml(html) {
@@ -3,7 +3,7 @@ import {
3
3
  sendBeacon,
4
4
  shouldForceRetry,
5
5
  shouldIgnoreMessages
6
- } from "./chunk-VZCUDNEG.js";
6
+ } from "./chunk-CSN7MQGX.js";
7
7
  import {
8
8
  getLogger,
9
9
  getOptions,
@@ -13,7 +13,7 @@ import {
13
13
  markInitialized,
14
14
  setLogger,
15
15
  updateRetryStateInUrl
16
- } from "./chunk-5DE7AKYB.js";
16
+ } from "./chunk-X6E7KUDK.js";
17
17
 
18
18
  // src/common/isChunkError.ts
19
19
  var isChunkError = (error) => {
@@ -5,4 +5,5 @@ export declare const initializedKey: unique symbol;
5
5
  export declare const loggerWindowKey: unique symbol;
6
6
  export declare const RETRY_ID_PARAM = "spaGuardRetryId";
7
7
  export declare const RETRY_ATTEMPT_PARAM = "spaGuardRetryAttempt";
8
+ export declare const versionCheckStateWindowKey: unique symbol;
8
9
  export declare const debugSyncErrorEventType = "spa-guard:debug-sync-error";
@@ -2,8 +2,8 @@ import {
2
2
  createLogger,
3
3
  listenInternal,
4
4
  serializeError
5
- } from "../chunk-DUR4QNQE.js";
6
- import "../chunk-VZCUDNEG.js";
5
+ } from "../chunk-ZVYB2746.js";
6
+ import "../chunk-CSN7MQGX.js";
7
7
  import {
8
8
  ForceRetryError,
9
9
  disableDefaultRetry,
@@ -12,7 +12,7 @@ import {
12
12
  isDefaultRetryEnabled,
13
13
  options_exports,
14
14
  subscribe
15
- } from "../chunk-5DE7AKYB.js";
15
+ } from "../chunk-X6E7KUDK.js";
16
16
  import {
17
17
  __export
18
18
  } from "../chunk-MLKGABMK.js";
@@ -11,6 +11,13 @@ export interface Options {
11
11
  * and dispatches a `spa-guard:version-change` CustomEvent on the window.
12
12
  */
13
13
  checkVersion?: {
14
+ /**
15
+ * Cache mode for the fetch request used to check the version.
16
+ * - "no-store": Bypass the HTTP cache entirely (default).
17
+ * - "no-cache": Revalidate with the server before using cached response.
18
+ * @default "no-store"
19
+ */
20
+ cache?: "no-cache" | "no-store";
14
21
  /**
15
22
  * Endpoint URL for JSON mode version checking.
16
23
  * Required when mode is "json".
@@ -7,14 +7,14 @@ import {
7
7
  dispatchRetryExhausted,
8
8
  dispatchSyncRuntimeError,
9
9
  dispatchUnhandledRejection
10
- } from "../../chunk-H4DWKO5I.js";
10
+ } from "../../chunk-XFILAQ7P.js";
11
+ import "../../chunk-CSN7MQGX.js";
11
12
  import {
12
13
  subscribeToState
13
- } from "../../chunk-CQ5IVYGC.js";
14
- import "../../chunk-VZCUDNEG.js";
14
+ } from "../../chunk-T72DERME.js";
15
15
  import {
16
16
  subscribe
17
- } from "../../chunk-5DE7AKYB.js";
17
+ } from "../../chunk-X6E7KUDK.js";
18
18
  import "../../chunk-MLKGABMK.js";
19
19
 
20
20
  // src/runtime/debug/index.ts
@@ -3,29 +3,38 @@ import {
3
3
  extractVersionFromHtml,
4
4
  getSpinnerHtml,
5
5
  showSpinner
6
- } from "../chunk-UF7QAEI7.js";
6
+ } from "../chunk-Z75UPJWV.js";
7
7
  import {
8
8
  getState,
9
9
  subscribeToState
10
- } from "../chunk-CQ5IVYGC.js";
10
+ } from "../chunk-T72DERME.js";
11
11
  import {
12
12
  ForceRetryError,
13
13
  getLogger,
14
14
  getOptions,
15
- setTranslations
16
- } from "../chunk-5DE7AKYB.js";
15
+ setTranslations,
16
+ versionCheckStateWindowKey
17
+ } from "../chunk-X6E7KUDK.js";
17
18
  import "../chunk-MLKGABMK.js";
18
19
 
19
20
  // src/common/checkVersion.ts
20
- var versionCheckInterval = null;
21
- var versionCheckTimeout = null;
22
- var lastKnownVersion = null;
23
- var lastCheckTimestamp = null;
24
- var visibilityHandler = null;
25
- var focusHandler = null;
26
- var blurHandler = null;
27
- var checkInProgress = false;
28
- var runEpoch = 0;
21
+ var createInitialState = () => ({
22
+ blurHandler: null,
23
+ checkInProgress: false,
24
+ focusHandler: null,
25
+ lastCheckTimestamp: null,
26
+ lastKnownVersion: null,
27
+ runEpoch: 0,
28
+ versionCheckInterval: null,
29
+ versionCheckTimeout: null,
30
+ visibilityHandler: null
31
+ });
32
+ if (globalThis.window && !globalThis.window[versionCheckStateWindowKey]) {
33
+ globalThis.window[versionCheckStateWindowKey] = createInitialState();
34
+ }
35
+ var getState2 = () => {
36
+ return globalThis.window?.[versionCheckStateWindowKey] ?? (globalThis.window[versionCheckStateWindowKey] = createInitialState());
37
+ };
29
38
  var fetchJsonVersion = async () => {
30
39
  const endpoint = getOptions().checkVersion?.endpoint;
31
40
  if (!endpoint) {
@@ -33,7 +42,7 @@ var fetchJsonVersion = async () => {
33
42
  return null;
34
43
  }
35
44
  const response = await fetch(endpoint, {
36
- cache: "no-store",
45
+ cache: getOptions().checkVersion?.cache ?? "no-store",
37
46
  headers: { Accept: "application/json" }
38
47
  });
39
48
  if (!response.ok) {
@@ -51,7 +60,7 @@ var fetchHtmlVersion = async () => {
51
60
  url.search = "";
52
61
  url.hash = "";
53
62
  const response = await fetch(url.toString(), {
54
- cache: "no-store",
63
+ cache: getOptions().checkVersion?.cache ?? "no-store",
55
64
  headers: { Accept: "text/html" }
56
65
  });
57
66
  if (!response.ok) {
@@ -83,44 +92,47 @@ var onVersionChange = (oldVersion, latestVersion) => {
83
92
  }
84
93
  };
85
94
  var checkVersionOnce = async (mode) => {
86
- if (checkInProgress) {
95
+ const s = getState2();
96
+ if (s.checkInProgress) {
87
97
  return;
88
98
  }
89
- checkInProgress = true;
90
- const epochAtStart = runEpoch;
99
+ s.checkInProgress = true;
100
+ const epochAtStart = s.runEpoch;
91
101
  try {
92
102
  const remoteVersion = await fetchRemoteVersion(mode);
93
- if (epochAtStart !== runEpoch) {
103
+ if (epochAtStart !== s.runEpoch) {
94
104
  return;
95
105
  }
96
- if (remoteVersion && remoteVersion !== lastKnownVersion) {
97
- const oldVersion = lastKnownVersion;
98
- lastKnownVersion = remoteVersion;
106
+ if (remoteVersion && remoteVersion !== s.lastKnownVersion) {
107
+ const oldVersion = s.lastKnownVersion;
108
+ s.lastKnownVersion = remoteVersion;
99
109
  onVersionChange(oldVersion, remoteVersion);
100
110
  }
101
111
  } catch (error) {
102
112
  getLogger()?.versionCheckFailed(error);
103
113
  } finally {
104
- if (epochAtStart === runEpoch) {
105
- checkInProgress = false;
114
+ if (epochAtStart === s.runEpoch) {
115
+ s.checkInProgress = false;
106
116
  }
107
117
  }
108
118
  };
109
119
  var startPolling = (mode, interval) => {
120
+ const s = getState2();
110
121
  clearTimers();
111
- versionCheckInterval = setInterval(async () => {
112
- lastCheckTimestamp = Date.now();
122
+ s.versionCheckInterval = setInterval(async () => {
123
+ s.lastCheckTimestamp = Date.now();
113
124
  await checkVersionOnce(mode);
114
125
  }, interval);
115
126
  };
116
127
  var clearTimers = () => {
117
- if (versionCheckInterval !== null) {
118
- clearInterval(versionCheckInterval);
119
- versionCheckInterval = null;
128
+ const s = getState2();
129
+ if (s.versionCheckInterval !== null) {
130
+ clearInterval(s.versionCheckInterval);
131
+ s.versionCheckInterval = null;
120
132
  }
121
- if (versionCheckTimeout !== null) {
122
- clearTimeout(versionCheckTimeout);
123
- versionCheckTimeout = null;
133
+ if (s.versionCheckTimeout !== null) {
134
+ clearTimeout(s.versionCheckTimeout);
135
+ s.versionCheckTimeout = null;
124
136
  }
125
137
  };
126
138
  var handleVisibilityHidden = () => {
@@ -128,22 +140,23 @@ var handleVisibilityHidden = () => {
128
140
  getLogger()?.versionCheckPaused();
129
141
  };
130
142
  var handleResume = (mode, interval) => {
131
- if (versionCheckInterval !== null || versionCheckTimeout !== null) {
143
+ const s = getState2();
144
+ if (s.versionCheckInterval !== null || s.versionCheckTimeout !== null) {
132
145
  return;
133
146
  }
134
- const elapsed = Date.now() - (lastCheckTimestamp ?? 0);
147
+ const elapsed = Date.now() - (s.lastCheckTimestamp ?? 0);
135
148
  if (elapsed >= interval) {
136
149
  getLogger()?.versionCheckResumedImmediate();
137
- lastCheckTimestamp = Date.now();
150
+ s.lastCheckTimestamp = Date.now();
138
151
  void checkVersionOnce(mode);
139
152
  startPolling(mode, interval);
140
153
  return;
141
154
  }
142
155
  getLogger()?.versionCheckResumed();
143
156
  const remaining = interval - elapsed;
144
- versionCheckTimeout = setTimeout(() => {
145
- versionCheckTimeout = null;
146
- lastCheckTimestamp = Date.now();
157
+ s.versionCheckTimeout = setTimeout(() => {
158
+ s.versionCheckTimeout = null;
159
+ s.lastCheckTimestamp = Date.now();
147
160
  void checkVersionOnce(mode);
148
161
  startPolling(mode, interval);
149
162
  }, remaining);
@@ -157,57 +170,62 @@ var startVersionCheck = () => {
157
170
  getLogger()?.versionCheckDisabled();
158
171
  return;
159
172
  }
160
- if (versionCheckInterval !== null || visibilityHandler !== null) {
173
+ const s = getState2();
174
+ if (s.versionCheckInterval !== null || s.visibilityHandler !== null) {
161
175
  getLogger()?.versionCheckAlreadyRunning();
162
176
  return;
163
177
  }
164
- runEpoch++;
165
- lastKnownVersion = options.version;
178
+ s.runEpoch++;
179
+ s.lastKnownVersion = options.version;
166
180
  const interval = options.checkVersion?.interval ?? 3e5;
167
181
  const mode = options.checkVersion?.mode ?? "html";
168
- getLogger()?.versionCheckStarted(mode, interval, lastKnownVersion);
182
+ getLogger()?.versionCheckStarted(mode, interval, s.lastKnownVersion);
169
183
  const isTabVisible = document.visibilityState === "visible";
170
184
  const isWindowFocused = document.hasFocus();
171
185
  if (isTabVisible && isWindowFocused) {
172
- lastCheckTimestamp = Date.now();
186
+ s.lastCheckTimestamp = Date.now();
173
187
  startPolling(mode, interval);
174
188
  } else {
175
- lastCheckTimestamp = 0;
189
+ s.lastCheckTimestamp = 0;
176
190
  getLogger()?.versionCheckPaused();
177
191
  }
178
- visibilityHandler = () => {
192
+ s.visibilityHandler = () => {
179
193
  if (document.visibilityState === "hidden") {
180
194
  handleVisibilityHidden();
181
195
  } else {
182
196
  handleResume(mode, interval);
183
197
  }
184
198
  };
185
- focusHandler = () => {
199
+ s.focusHandler = () => {
186
200
  handleResume(mode, interval);
187
201
  };
188
- blurHandler = () => {
202
+ s.blurHandler = () => {
189
203
  handleVisibilityHidden();
190
204
  };
191
- document.addEventListener("visibilitychange", visibilityHandler);
192
- globalThis.addEventListener("focus", focusHandler);
193
- globalThis.addEventListener("blur", blurHandler);
205
+ document.addEventListener("visibilitychange", s.visibilityHandler);
206
+ globalThis.addEventListener("focus", s.focusHandler);
207
+ globalThis.addEventListener("blur", s.blurHandler);
194
208
  };
195
209
  var stopVersionCheck = () => {
196
- runEpoch++;
197
- checkInProgress = false;
198
- const wasRunning = versionCheckInterval !== null || versionCheckTimeout !== null || visibilityHandler !== null;
210
+ if (globalThis.window === void 0) {
211
+ return;
212
+ }
213
+ const s = getState2();
214
+ s.runEpoch++;
215
+ s.checkInProgress = false;
216
+ const wasRunning = s.versionCheckInterval !== null || s.versionCheckTimeout !== null || s.visibilityHandler !== null;
199
217
  clearTimers();
200
- if (visibilityHandler !== null) {
201
- document.removeEventListener("visibilitychange", visibilityHandler);
202
- visibilityHandler = null;
218
+ if (s.visibilityHandler !== null) {
219
+ document.removeEventListener("visibilitychange", s.visibilityHandler);
220
+ s.visibilityHandler = null;
203
221
  }
204
- if (focusHandler !== null) {
205
- globalThis.removeEventListener("focus", focusHandler);
206
- focusHandler = null;
222
+ if (s.focusHandler !== null) {
223
+ globalThis.removeEventListener("focus", s.focusHandler);
224
+ s.focusHandler = null;
207
225
  }
208
- if (blurHandler !== null) {
209
- globalThis.removeEventListener("blur", blurHandler);
210
- blurHandler = null;
226
+ if (s.blurHandler !== null) {
227
+ globalThis.removeEventListener("blur", s.blurHandler);
228
+ s.blurHandler = null;
211
229
  }
212
230
  if (wasRunning) {
213
231
  getLogger()?.versionCheckStopped();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovineko/spa-guard",
3
- "version": "0.0.1-alpha-24",
3
+ "version": "0.0.1-alpha-25",
4
4
  "description": "Chunk load error handling for SPAs — core runtime, error handling, schema, i18n",
5
5
  "keywords": [
6
6
  "spa",