@ryuzaki13/react-foundation-lib 1.1.2 → 1.1.4

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
@@ -2,8 +2,69 @@
2
2
 
3
3
  Библиотечный пакет нижнего технического слоя: здесь лежат чистые функции, общие React/browser hooks и сериализуемые runtime-контракты без знания конкретных features, widgets, pages или apps.
4
4
 
5
+ Пакет публикуется в npm как публичная библиотека, но его основная задача практическая: вынести повторно используемый `shared/lib` слой из собственных проектов автора и подключать его одинаково в нескольких host-приложениях. Это не универсальный набор утилит на все случаи жизни; контракты и состав модулей в первую очередь оптимизируются под семейство проектов, где поверх этого слоя используются `@ryuzaki13/react-foundation-api` и `@ryuzaki13/react-foundation-ui`.
6
+
7
+ `react-foundation-lib` является нижним пакетом в этой цепочке. Он не должен зависеть от `api` или `ui`; наоборот, `api` и `ui` используют его как общий технический фундамент.
8
+
9
+ ## Установка
10
+
11
+ ```bash
12
+ npm install @ryuzaki13/react-foundation-lib
13
+ ```
14
+
15
+ Пакет распространяется как ESM и не открывает корневой импорт. Используйте только точечные entrypoints из `exports`:
16
+
17
+ ```ts
18
+ import { formatDateAsDate } from "@ryuzaki13/react-foundation-lib/formatters";
19
+ import { createQueryClient } from "@ryuzaki13/react-foundation-lib/query-client";
20
+ import { buildODataFilter } from "@ryuzaki13/react-foundation-lib/odata-service";
21
+ import type { RowRecord } from "@ryuzaki13/react-foundation-lib/types";
22
+ ```
23
+
24
+ Импорт вида `@ryuzaki13/react-foundation-lib` намеренно недоступен. Так consumer явно выбирает нужный модуль, а сборщик host-проекта не получает общий barrel со всем пакетом. Типы экспортируются теми же subpath entrypoints через `exports.types`.
25
+
26
+ ## Peer-зависимости
27
+
28
+ Большинство внешних пакетов объявлены как optional peers. Это означает только то, что их не нужно ставить для каждого сценария. Если host использует entrypoint, который импортирует внешний пакет, совместимая peer-зависимость должна быть установлена в host-проекте.
29
+
30
+ | Entry point | Что может потребоваться в host-проекте |
31
+ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
32
+ | `formatters`, `array`, `utils`, `validators` | Обычно не требуют дополнительных runtime-зависимостей. |
33
+ | `hooks`, `dom`, `copy`, `media`, `pwa` | `react`, если используются React hooks. |
34
+ | `query-client`, `error-report` | `@tanstack/react-query`, `@tanstack/query-persist-client-core`, `@tanstack/query-broadcast-client-experimental`. |
35
+ | `odata-service` | `zod`; отдельные helpers типизируются вокруг `@tanstack/react-query`. |
36
+ | `odata`, `notifications` | `zustand`, для отдельных store-helpers также `immer`. |
37
+ | `table` | `@tanstack/react-table` и `react` для selection hooks. |
38
+ | `virtualizer` | `@tanstack/react-virtual` и `react`. |
39
+ | `hooks` search helpers | `@tanstack/react-router`. |
40
+ | `hooks` DnD helpers | `@dnd-kit/core`, `@dnd-kit/sortable`. |
41
+ | `hooks` floating/listbox и DOM references | `@floating-ui/react`. |
42
+ | `excel` | `write-excel-file`. |
43
+
44
+ Такая схема оставляет пакет библиотечным: зависимости не зашиваются в bundle, а host-приложение контролирует версии React, TanStack, Zod и других runtime-библиотек.
45
+
46
+ ## Проверка пакета
47
+
48
+ Основные команды разработки:
49
+
50
+ ```bash
51
+ npm run lint
52
+ npm run typecheck
53
+ npm run test
54
+ npm run build
55
+ ```
56
+
57
+ Перед публикацией можно проверить полный npm-артефакт:
58
+
59
+ ```bash
60
+ npm run pack:dry-run
61
+ ```
62
+
5
63
  ## Содержание
6
64
 
65
+ - [Установка](#установка)
66
+ - [Peer-зависимости](#peer-зависимости)
67
+ - [Проверка пакета](#проверка-пакета)
7
68
  - [Быстрый выбор модуля](#быстрый-выбор-модуля)
8
69
  - [Форматирование и значения](#форматирование-и-значения)
9
70
  - [OData, таблицы и экспорт](#odata-таблицы-и-экспорт)
@@ -612,7 +673,7 @@ Service worker runtime helpers живут в [`pwa/serviceWorker.ts`](./src/pwa/
612
673
  | `isObject`, `isRecord`, `asRecord`, `isPlainObject` | Guards для object-like values; массивы не считаются record. |
613
674
  | `IMAGE_EXTENSIONS`, `isImageExtension` | Проверка image extension без учета регистра. |
614
675
  | `isDomReference` | Floating UI reference -> DOM `Element`. |
615
- | `State`, `AbapBoolean`, `Primitive`, `BaseType`, `InputType`, `RangeType`, `ChangeHandler`, `RowRecord` | Общие технические типы. |
676
+ | `State`, `AbapBoolean`, `Primitive`, `BaseType`, `InputType`, `RangeType`, `RowRecord` | Общие технические типы. |
616
677
 
617
678
  ### `string-comparison`
618
679
 
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getClientFingerprint.d.ts","sourceRoot":"","sources":["../../src/fingerprint/getClientFingerprint.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnE"}
@@ -0,0 +1,2 @@
1
+ export * from "./getClientFingerprint";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fingerprint/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
package/dist/pwa/index.js CHANGED
@@ -1,207 +1,141 @@
1
1
  import { i as e } from "../chunks/isRecord-DXDTW-jA.js";
2
2
  import { logError as t } from "../utils/index.js";
3
- import { useCallback as n, useEffect as r, useState as i } from "react";
4
- //#region src/pwa/swCachePolicy.ts
5
- var a = 100, o = 5e3, s = 365 * 24 * 60 * 60 * 1e3;
6
- function c(e) {
7
- return e?.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || void 0;
8
- }
9
- function l(e) {
10
- let t = e.trim().toLowerCase();
11
- if (t === "forever") return {
12
- durationLabel: "forever",
13
- ttlMs: Infinity
14
- };
15
- let n = t.match(/^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/);
16
- if (!n) return;
17
- let r = Number(n[1]), i = n[2];
18
- if (!Number.isFinite(r) || r <= 0) return;
19
- let a = Math.round(r * {
20
- ms: 1,
21
- s: 1e3,
22
- m: 60 * 1e3,
23
- h: 3600 * 1e3,
24
- d: 1440 * 60 * 1e3
25
- }[i]);
26
- if (!(!Number.isFinite(a) || a <= 0 || a > s)) return {
27
- durationLabel: `${n[1]}${i}`.replace(".", "_"),
28
- amount: r,
29
- unit: i,
30
- ttlMs: a
31
- };
32
- }
33
- function u(e) {
34
- if (!e) return;
35
- let t = Number(e.trim());
36
- if (!(!Number.isInteger(t) || t <= 0)) return Math.min(t, o);
37
- }
38
- function d(e, t, n) {
39
- return `odata-${t ? `${t}-${e.durationLabel}` : e.durationLabel}${n ? `-max${n}` : ""}`;
40
- }
41
- function f(e) {
42
- let t = e?.trim();
43
- if (!t) return;
44
- if (t.toLowerCase() === "off") return { mode: "off" };
45
- let [n, ...r] = t.split(/[;,]/).map((e) => e.trim()).filter(Boolean), [i, o] = n?.split("=", 2) ?? [], s = i?.toLowerCase();
46
- if (s !== "ttl" && s !== "bust" || !o) return;
47
- let f = l(o);
48
- if (!f) return;
49
- let p = new Map(r.map((e) => e.split("=", 2).map((e) => e.trim())).map(([e, t]) => [e.toLowerCase(), t]).filter(([e, t]) => !!(e && t))), m = u(p.get("maxentries") ?? p.get("max") ?? p.get("entries")), h = c(p.get("cachename") ?? p.get("cache") ?? p.get("name")), g = m ?? a;
50
- return {
51
- mode: s,
52
- amount: f.amount,
53
- unit: f.unit,
54
- name: h,
55
- maxEntries: m,
56
- resolvedMaxEntries: g,
57
- ttlMs: f.ttlMs,
58
- cacheName: d(f, h, m)
59
- };
60
- }
61
- function p(e) {
62
- let t = f(e);
63
- return t?.mode === "ttl" || t?.mode === "bust" ? t.cacheName : void 0;
64
- }
65
- function m(e) {
66
- if (e.mode === "off") return "off";
67
- let t = typeof e.amount == "number" && Number.isFinite(e.amount) && e.amount > 0 ? e.amount : 24, n = e.unit ?? "h", r = typeof e.maxEntries == "number" && Number.isInteger(e.maxEntries) && e.maxEntries > 0 ? Math.min(e.maxEntries, o) : void 0, i = c(e.name), a = [r ? `max=${r}` : void 0, i ? `name=${i}` : void 0].filter(Boolean);
68
- return `${e.mode}=${t}${n}${a.length ? `;${a.join(";")}` : ""}`;
69
- }
70
- //#endregion
3
+ import { buildSwCachePolicyValue as n, normalizeSwCacheName as r, parseSwCachePolicy as i, resolveSwCacheCacheNameByPolicy as a } from "./sw-cache-policy.js";
4
+ import { useCallback as o, useEffect as s, useState as c } from "react";
71
5
  //#region src/pwa/serviceWorker.ts
72
- var h = __PREVIEW__ ? "/" : "/arm/", g = `${h}sw.js`, _ = "SW_ACTIVATED_BY_CLIENT", v = `${__APP_ID__}.service-worker.update-reload-count.v1`, y = 5, b = /* @__PURE__ */ new WeakSet(), x = /* @__PURE__ */ new Set(), S = null, C = !1, w = !1, T = !1, E = !1;
73
- function D(t) {
74
- return e(t) && t.type === _ ? { type: _ } : null;
6
+ var l = __SERVICE_WORKER_SCOPE__, u = __SERVICE_WORKER_URL__, d = "SW_ACTIVATED_BY_CLIENT", f = __SERVICE_WORKER_UPDATE_RELOAD_COUNT_KEY__, p = __SERVICE_WORKER_MAX_AUTO_UPDATE_RELOADS__, m = /* @__PURE__ */ new WeakSet(), h = /* @__PURE__ */ new Set(), g = null, _ = !1, v = !1, y = !1, b = !1;
7
+ function x(t) {
8
+ return e(t) && t.type === d ? { type: d } : null;
75
9
  }
76
- function O() {
10
+ function S() {
77
11
  try {
78
- let e = window.sessionStorage.getItem(v), t = Number(e);
12
+ let e = window.sessionStorage.getItem(f), t = Number(e);
79
13
  return Number.isInteger(t) && t > 0 ? t : 0;
80
14
  } catch {
81
15
  return 0;
82
16
  }
83
17
  }
84
- function k() {
18
+ function C() {
85
19
  try {
86
- let e = Math.min(O() + 1, y);
87
- window.sessionStorage.setItem(v, String(e));
20
+ let e = Math.min(S() + 1, p);
21
+ window.sessionStorage.setItem(f, String(e));
88
22
  } catch {}
89
23
  }
90
- function A() {
91
- return O() < y;
24
+ function w() {
25
+ return S() < p;
92
26
  }
93
- function j() {
27
+ function T() {
94
28
  try {
95
- window.sessionStorage.removeItem(v);
29
+ window.sessionStorage.removeItem(f);
96
30
  } catch {}
97
31
  }
98
- function M() {
99
- C || (C = !0, window.location.reload());
32
+ function E() {
33
+ _ || (_ = !0, window.location.reload());
100
34
  }
101
- function N() {
102
- T || (T = !0, navigator.serviceWorker.addEventListener("controllerchange", () => {
103
- w && M();
35
+ function D() {
36
+ y || (y = !0, navigator.serviceWorker.addEventListener("controllerchange", () => {
37
+ v && E();
104
38
  }));
105
39
  }
106
- function P() {
107
- E || (E = !0, navigator.serviceWorker.addEventListener("message", (e) => {
108
- D(e.data) && (C || (k(), M()));
40
+ function O() {
41
+ b || (b = !0, navigator.serviceWorker.addEventListener("message", (e) => {
42
+ x(e.data) && (_ || (C(), E()));
109
43
  }));
110
44
  }
111
- function F(e) {
112
- for (let t of x) t(e);
45
+ function k(e) {
46
+ for (let t of h) t(e);
113
47
  }
114
- function I(e) {
115
- b.has(e) || (b.add(e), e.addEventListener("updatefound", () => {
48
+ function A(e) {
49
+ m.has(e) || (m.add(e), e.addEventListener("updatefound", () => {
116
50
  let t = e.installing;
117
51
  t && t.addEventListener("statechange", () => {
118
- t.state === "installed" && navigator.serviceWorker.controller && (S = e, F(e));
52
+ t.state === "installed" && navigator.serviceWorker.controller && (g = e, k(e));
119
53
  });
120
- }), N(), P());
54
+ }), D(), O());
121
55
  }
122
- async function L(e = {}) {
56
+ async function j(e = {}) {
123
57
  if (!("serviceWorker" in navigator)) return null;
124
- e.onUpdate && x.add(e.onUpdate);
58
+ e.onUpdate && h.add(e.onUpdate);
125
59
  try {
126
- let t = S ?? await navigator.serviceWorker.getRegistration(h) ?? await navigator.serviceWorker.register(g, {
127
- scope: h,
60
+ let t = g ?? await navigator.serviceWorker.getRegistration(l) ?? await navigator.serviceWorker.register(u, {
61
+ scope: l,
128
62
  updateViaCache: "none"
129
63
  });
130
- return S = t, I(t), e.onReady?.(t), t;
64
+ return g = t, A(t), e.onReady?.(t), t;
131
65
  } catch (e) {
132
66
  return t("[SW] registration failed:", e), null;
133
67
  }
134
68
  }
135
- function R(e) {
136
- x.delete(e);
69
+ function M(e) {
70
+ h.delete(e);
137
71
  }
138
- function z(e) {
72
+ function N(e) {
139
73
  return !!e?.waiting;
140
74
  }
141
- function B(e = S) {
75
+ function P(e = g) {
142
76
  let t = e?.waiting;
143
- return t ? (w = !0, k(), t.postMessage({ type: "SKIP_WAITING" }), !0) : !1;
77
+ return t ? (v = !0, C(), t.postMessage({ type: "SKIP_WAITING" }), !0) : !1;
144
78
  }
145
- function V() {
146
- return k(), M(), !0;
79
+ function F() {
80
+ return C(), E(), !0;
147
81
  }
148
- async function H() {
82
+ async function I() {
149
83
  if (!("serviceWorker" in navigator)) return null;
150
84
  try {
151
- let e = S ?? await navigator.serviceWorker.getRegistration(h);
152
- return e ? (S = e, I(e), await e.update(), e) : null;
85
+ let e = g ?? await navigator.serviceWorker.getRegistration(l);
86
+ return e ? (g = e, A(e), await e.update(), e) : null;
153
87
  } catch (e) {
154
88
  return t("[SW] update check failed:", e), null;
155
89
  }
156
90
  }
157
91
  //#endregion
158
92
  //#region src/pwa/useServiceWorkerUpdate.ts
159
- var U = 300 * 1e3, W = 30 * 1e3;
160
- function G() {
93
+ var L = 300 * 1e3, R = 30 * 1e3;
94
+ function z() {
161
95
  let [e] = performance.getEntriesByType("navigation");
162
96
  return e?.type === "reload" || e?.type === "navigate";
163
97
  }
164
- function K() {
165
- let [e, t] = i(null), a = e !== null, o = n(() => B(e) ? !0 : e ? V() : !1, [e]), s = n(() => {
98
+ function B() {
99
+ let [e, t] = c(null), n = e !== null, r = o(() => P(e) ? !0 : e ? F() : !1, [e]), i = o(() => {
166
100
  t(null);
167
101
  }, []);
168
- return r(() => {
102
+ return s(() => {
169
103
  if (__DEV__) return;
170
- let e = !0, n = Date.now(), r = null, i = !1, a = () => G() && Date.now() - n <= W && A(), o = (e) => {
104
+ let e = !0, n = Date.now(), r = null, i = !1, a = () => z() && Date.now() - n <= R && w(), o = (e) => {
171
105
  if (i) return !0;
172
106
  if (!a()) return !1;
173
- let t = B(e);
107
+ let t = P(e);
174
108
  return i = t, t;
175
109
  }, s = () => {
176
- let t = Date.now() - n, i = Math.max(W - t, 0);
110
+ let t = Date.now() - n, i = Math.max(R - t, 0);
177
111
  r = window.setTimeout(() => {
178
- e && j();
112
+ e && T();
179
113
  }, i);
180
114
  }, c = (n) => {
181
115
  e && (o(n) || t(n));
182
116
  };
183
117
  async function l() {
184
- let n = await L({ onUpdate: c });
185
- if (!e || z(n) && o(n)) return;
186
- let r = await H();
187
- e && (i || z(r) && o(r) || (z(r) && t(r), s()));
118
+ let n = await j({ onUpdate: c });
119
+ if (!e || N(n) && o(n)) return;
120
+ let r = await I();
121
+ e && (i || N(r) && o(r) || (N(r) && t(r), s()));
188
122
  }
189
123
  l();
190
124
  let u = window.setInterval(() => {
191
- H().then((n) => {
192
- e && z(n) && t(n);
125
+ I().then((n) => {
126
+ e && N(n) && t(n);
193
127
  });
194
- }, U);
128
+ }, L);
195
129
  return () => {
196
- e = !1, r !== null && window.clearTimeout(r), window.clearInterval(u), R(c);
130
+ e = !1, r !== null && window.clearTimeout(r), window.clearInterval(u), M(c);
197
131
  };
198
132
  }, []), {
199
- hasUpdate: a,
200
- applyUpdate: o,
201
- dismiss: s
133
+ hasUpdate: n,
134
+ applyUpdate: r,
135
+ dismiss: i
202
136
  };
203
137
  }
204
138
  //#endregion
205
- export { m as buildSwCachePolicyValue, c as normalizeSwCacheName, f as parseSwCachePolicy, p as resolveSwCacheCacheNameByPolicy, K as useServiceWorkerUpdate };
139
+ export { n as buildSwCachePolicyValue, r as normalizeSwCacheName, i as parseSwCachePolicy, a as resolveSwCacheCacheNameByPolicy, B as useServiceWorkerUpdate };
206
140
 
207
141
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/pwa/swCachePolicy.ts","../../src/pwa/serviceWorker.ts","../../src/pwa/useServiceWorkerUpdate.ts"],"sourcesContent":["export type SwCachePolicyMode = \"off\" | \"ttl\" | \"bust\";\nexport type SwCacheDurationUnit = \"ms\" | \"s\" | \"m\" | \"h\" | \"d\";\n\nexport interface ParsedSwCachePolicy {\n\tmode: SwCachePolicyMode;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tname?: string;\n\tmaxEntries?: number;\n\tresolvedMaxEntries?: number;\n\tttlMs?: number;\n\tcacheName?: string;\n}\n\nexport interface SwCachePolicyDraft {\n\tmode: SwCachePolicyMode;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tname?: string;\n\tmaxEntries?: number;\n}\n\nconst DEFAULT_ODATA_CACHE_MAX_ENTRIES = 100;\nconst MAX_ODATA_CACHE_MAX_ENTRIES = 5000;\nconst MAX_ODATA_CACHE_TTL_MS = 365 * 24 * 60 * 60 * 1000;\n\ntype DurationParseResult = {\n\tdurationLabel: string;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tttlMs: number;\n};\n\nexport function normalizeSwCacheName(value: string | undefined): string | undefined {\n\tconst normalized = value\n\t\t?.trim()\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\treturn normalized || undefined;\n}\n\nfunction parseDurationToken(value: string): DurationParseResult | undefined {\n\tconst normalized = value.trim().toLowerCase();\n\tif (normalized === \"forever\") {\n\t\treturn {\n\t\t\tdurationLabel: \"forever\",\n\t\t\tttlMs: Infinity\n\t\t};\n\t}\n\n\tconst match = normalized.match(/^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/);\n\tif (!match) return undefined;\n\n\tconst amount = Number(match[1]);\n\tconst unit = match[2] as SwCacheDurationUnit;\n\tif (!Number.isFinite(amount) || amount <= 0) return undefined;\n\n\tconst multiplierByUnit: Record<SwCacheDurationUnit, number> = {\n\t\tms: 1,\n\t\ts: 1000,\n\t\tm: 60 * 1000,\n\t\th: 60 * 60 * 1000,\n\t\td: 24 * 60 * 60 * 1000\n\t};\n\tconst ttlMs = Math.round(amount * multiplierByUnit[unit]);\n\tif (!Number.isFinite(ttlMs) || ttlMs <= 0 || ttlMs > MAX_ODATA_CACHE_TTL_MS) return undefined;\n\n\tconst durationLabel = `${match[1]}${unit}`.replace(\".\", \"_\");\n\treturn {\n\t\tdurationLabel,\n\t\tamount,\n\t\tunit,\n\t\tttlMs\n\t};\n}\n\nfunction parseMaxEntries(value: string | undefined): number | undefined {\n\tif (!value) return undefined;\n\n\tconst parsed = Number(value.trim());\n\tif (!Number.isInteger(parsed) || parsed <= 0) return undefined;\n\n\treturn Math.min(parsed, MAX_ODATA_CACHE_MAX_ENTRIES);\n}\n\nfunction createCacheName(duration: DurationParseResult, name: string | undefined, maxEntries: number | undefined) {\n\tconst cacheLabel = name ? `${name}-${duration.durationLabel}` : duration.durationLabel;\n\treturn `odata-${cacheLabel}${maxEntries ? `-max${maxEntries}` : \"\"}`;\n}\n\nexport function parseSwCachePolicy(value: string | null | undefined): ParsedSwCachePolicy | undefined {\n\tconst normalized = value?.trim();\n\tif (!normalized) return undefined;\n\tif (normalized.toLowerCase() === \"off\") return { mode: \"off\" };\n\n\tconst parts = normalized\n\t\t.split(/[;,]/)\n\t\t.map((part) => part.trim())\n\t\t.filter(Boolean);\n\tconst [modePart, ...optionParts] = parts;\n\tconst [rawMode, rawDuration] = modePart?.split(\"=\", 2) ?? [];\n\tconst mode = rawMode?.toLowerCase();\n\tif ((mode !== \"ttl\" && mode !== \"bust\") || !rawDuration) return undefined;\n\n\tconst duration = parseDurationToken(rawDuration);\n\tif (!duration) return undefined;\n\n\tconst options = new Map(\n\t\toptionParts\n\t\t\t.map((part) => part.split(\"=\", 2).map((item) => item.trim()) as [string, string | undefined])\n\t\t\t.map(([key, optionValue]) => [key.toLowerCase(), optionValue] as [string, string | undefined])\n\t\t\t.filter(([key, optionValue]) => Boolean(key && optionValue))\n\t);\n\tconst maxEntries = parseMaxEntries(options.get(\"maxentries\") ?? options.get(\"max\") ?? options.get(\"entries\"));\n\tconst name = normalizeSwCacheName(options.get(\"cachename\") ?? options.get(\"cache\") ?? options.get(\"name\"));\n\tconst resolvedMaxEntries = maxEntries ?? DEFAULT_ODATA_CACHE_MAX_ENTRIES;\n\n\treturn {\n\t\tmode,\n\t\tamount: duration.amount,\n\t\tunit: duration.unit,\n\t\tname,\n\t\tmaxEntries,\n\t\tresolvedMaxEntries,\n\t\tttlMs: duration.ttlMs,\n\t\tcacheName: createCacheName(duration, name, maxEntries)\n\t};\n}\n\nexport function resolveSwCacheCacheNameByPolicy(value: string | undefined): string | undefined {\n\tconst parsed = parseSwCachePolicy(value);\n\treturn parsed?.mode === \"ttl\" || parsed?.mode === \"bust\" ? parsed.cacheName : undefined;\n}\n\nexport function buildSwCachePolicyValue(draft: SwCachePolicyDraft): string {\n\tif (draft.mode === \"off\") return \"off\";\n\n\tconst amount = typeof draft.amount === \"number\" && Number.isFinite(draft.amount) && draft.amount > 0 ? draft.amount : 24;\n\tconst unit = draft.unit ?? \"h\";\n\tconst maxEntries =\n\t\ttypeof draft.maxEntries === \"number\" && Number.isInteger(draft.maxEntries) && draft.maxEntries > 0\n\t\t\t? Math.min(draft.maxEntries, MAX_ODATA_CACHE_MAX_ENTRIES)\n\t\t\t: undefined;\n\tconst name = normalizeSwCacheName(draft.name);\n\tconst options = [maxEntries ? `max=${maxEntries}` : undefined, name ? `name=${name}` : undefined].filter(Boolean);\n\n\treturn `${draft.mode}=${amount}${unit}${options.length ? `;${options.join(\";\")}` : \"\"}`;\n}\n","import { logError } from \"../utils\";\nimport { isRecord } from \"../validators\";\n\nconst SW_BASE = __PREVIEW__ ? \"/\" : \"/arm/\";\nconst SW_URL = `${SW_BASE}sw.js`;\nconst SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE = \"SW_ACTIVATED_BY_CLIENT\";\n// TODO: Vite env\nconst SW_UPDATE_RELOAD_COUNT_KEY = `${__APP_ID__}.service-worker.update-reload-count.v1`;\n// TODO: Vite env\nconst MAX_SW_AUTO_UPDATE_RELOADS = 5;\n\ntype SWUpdateHandler = (reg: ServiceWorkerRegistration) => void;\n\ntype ServiceWorkerClientMessage = {\n\ttype: typeof SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE;\n};\n\ntype RegisterSWOptions = {\n\t/**\n\t * Вызывается, когда новый SW установлен и ждёт активации.\n\t */\n\tonUpdate?: SWUpdateHandler;\n\n\t/**\n\t * Вызывается после успешной регистрации.\n\t */\n\tonReady?: (reg: ServiceWorkerRegistration) => void;\n};\n\nconst observedRegistrations = new WeakSet<ServiceWorkerRegistration>();\nconst updateHandlers = new Set<SWUpdateHandler>();\n\nlet latestRegistration: ServiceWorkerRegistration | null = null;\nlet refreshing = false;\nlet updateRequested = false;\nlet controllerChangeSubscribed = false;\nlet serviceWorkerMessageSubscribed = false;\n\nfunction readServiceWorkerClientMessage(value: unknown): ServiceWorkerClientMessage | null {\n\tif (!isRecord(value)) return null;\n\n\treturn value.type === SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE ? { type: SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE } : null;\n}\n\nfunction readServiceWorkerUpdateReloadCount(): number {\n\ttry {\n\t\tconst value = window.sessionStorage.getItem(SW_UPDATE_RELOAD_COUNT_KEY);\n\t\tconst count = Number(value);\n\n\t\treturn Number.isInteger(count) && count > 0 ? count : 0;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport function recordServiceWorkerUpdateReload() {\n\ttry {\n\t\tconst nextCount = Math.min(readServiceWorkerUpdateReloadCount() + 1, MAX_SW_AUTO_UPDATE_RELOADS);\n\t\twindow.sessionStorage.setItem(SW_UPDATE_RELOAD_COUNT_KEY, String(nextCount));\n\t} catch {\n\t\t/**\n\t\t * sessionStorage может быть недоступен в приватном режиме или при\n\t\t * жёстких browser policy. Обновление всё равно должно продолжиться.\n\t\t */\n\t}\n}\n\nexport function canAutoApplyServiceWorkerUpdate(): boolean {\n\treturn readServiceWorkerUpdateReloadCount() < MAX_SW_AUTO_UPDATE_RELOADS;\n}\n\nexport function clearServiceWorkerUpdateReloadChain() {\n\ttry {\n\t\twindow.sessionStorage.removeItem(SW_UPDATE_RELOAD_COUNT_KEY);\n\t} catch {\n\t\t/**\n\t\t * Очистка маркера не критична: максимум пользователь увидит обычный\n\t\t * баннер вместо автоматического продолжения цепочки обновлений.\n\t\t */\n\t}\n}\n\nfunction reloadPage() {\n\tif (refreshing) return;\n\n\trefreshing = true;\n\twindow.location.reload();\n}\n\nfunction subscribeControllerChangeReload() {\n\tif (controllerChangeSubscribed) return;\n\n\tcontrollerChangeSubscribed = true;\n\n\tnavigator.serviceWorker.addEventListener(\"controllerchange\", () => {\n\t\t/**\n\t\t * Не перезагружаем страницу от любого controllerchange.\n\t\t * Перезагрузка нужна только когда мы сами вызвали SKIP_WAITING.\n\t\t */\n\t\tif (!updateRequested) return;\n\n\t\treloadPage();\n\t});\n}\n\nfunction subscribeServiceWorkerMessages() {\n\tif (serviceWorkerMessageSubscribed) return;\n\n\tserviceWorkerMessageSubscribed = true;\n\n\tnavigator.serviceWorker.addEventListener(\"message\", (event) => {\n\t\tconst message = readServiceWorkerClientMessage(event.data);\n\t\tif (!message) return;\n\t\tif (refreshing) return;\n\n\t\trecordServiceWorkerUpdateReload();\n\t\treloadPage();\n\t});\n}\n\nfunction emitUpdate(reg: ServiceWorkerRegistration) {\n\tfor (const handler of updateHandlers) {\n\t\thandler(reg);\n\t}\n}\n\nfunction observeRegistration(reg: ServiceWorkerRegistration) {\n\tif (observedRegistrations.has(reg)) return;\n\n\tobservedRegistrations.add(reg);\n\n\treg.addEventListener(\"updatefound\", () => {\n\t\tconst newWorker = reg.installing;\n\t\tif (!newWorker) return;\n\n\t\tnewWorker.addEventListener(\"statechange\", () => {\n\t\t\t/**\n\t\t\t * installed + есть controller = новая версия установлена,\n\t\t\t * но текущая страница всё ещё под старым SW.\n\t\t\t */\n\t\t\tif (newWorker.state === \"installed\" && navigator.serviceWorker.controller) {\n\t\t\t\tlatestRegistration = reg;\n\t\t\t\temitUpdate(reg);\n\t\t\t}\n\t\t});\n\t});\n\n\tsubscribeControllerChangeReload();\n\tsubscribeServiceWorkerMessages();\n}\n\nexport async function getServiceWorkerRegistration(): Promise<ServiceWorkerRegistration | null> {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\ttry {\n\t\tconst reg = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_BASE));\n\n\t\tif (!reg) return null;\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] get registration failed:\", err);\n\t\treturn null;\n\t}\n}\n\nexport async function registerServiceWorker(options: RegisterSWOptions = {}) {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\tif (options.onUpdate) {\n\t\tupdateHandlers.add(options.onUpdate);\n\t}\n\n\ttry {\n\t\tconst existingRegistration = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_BASE));\n\n\t\tconst reg =\n\t\t\texistingRegistration ??\n\t\t\t(await navigator.serviceWorker.register(SW_URL, {\n\t\t\t\tscope: SW_BASE,\n\t\t\t\tupdateViaCache: \"none\"\n\t\t\t}));\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\toptions.onReady?.(reg);\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] registration failed:\", err);\n\t\treturn null;\n\t}\n}\n\nexport function unsubscribeServiceWorkerUpdate(handler: SWUpdateHandler) {\n\tupdateHandlers.delete(handler);\n}\n\nexport function hasWaitingServiceWorker(reg: ServiceWorkerRegistration | null | undefined): reg is ServiceWorkerRegistration {\n\treturn Boolean(reg?.waiting);\n}\n\nexport function applyServiceWorkerUpdate(reg: ServiceWorkerRegistration | null = latestRegistration) {\n\tconst waiting = reg?.waiting;\n\tif (!waiting) return false;\n\n\tupdateRequested = true;\n\trecordServiceWorkerUpdateReload();\n\n\twaiting.postMessage({ type: \"SKIP_WAITING\" });\n\n\treturn true;\n}\n\nexport function reloadPageForServiceWorkerUpdate() {\n\trecordServiceWorkerUpdateReload();\n\treloadPage();\n\n\treturn true;\n}\n\nexport async function checkServiceWorkerUpdate() {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\ttry {\n\t\tconst reg = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_BASE));\n\t\tif (!reg) return null;\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\tawait reg.update();\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] update check failed:\", err);\n\t\treturn null;\n\t}\n}\n\n/**\n * Очищает SW-кеш OData для указанной политики.\n *\n * Примеры: `\"ttl=24h;name=ref\"`, `\"ttl=10m;max=200;name=ui\"`.\n *\n * После вызова следующий запрос с той же политикой пойдёт в сеть.\n */\nexport function invalidateSwCacheProfile(profile: string) {\n\tnavigator.serviceWorker?.controller?.postMessage({\n\t\ttype: \"invalidateProfile\",\n\t\tprofile\n\t});\n}\n","import { useCallback, useEffect, useState } from \"react\";\n\nimport {\n\tapplyServiceWorkerUpdate,\n\tcanAutoApplyServiceWorkerUpdate,\n\tcheckServiceWorkerUpdate,\n\tclearServiceWorkerUpdateReloadChain,\n\thasWaitingServiceWorker,\n\tregisterServiceWorker,\n\treloadPageForServiceWorkerUpdate,\n\tunsubscribeServiceWorkerUpdate\n} from \"./serviceWorker\";\n\n// TODO: Vite env\nconst SW_UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000;\n// TODO: Vite env\nconst SW_STARTUP_AUTO_UPDATE_GRACE_MS = 30 * 1000;\n\nfunction isStartupNavigationForAutoUpdate(): boolean {\n\tconst [entry] = performance.getEntriesByType(\"navigation\") as PerformanceNavigationTiming[];\n\n\t/**\n\t * reload — пользователь явно обновил страницу.\n\t * navigate — пользователь открыл страницу по ссылке/закладке.\n\t */\n\treturn entry?.type === \"reload\" || entry?.type === \"navigate\";\n}\n\nexport function useServiceWorkerUpdate() {\n\tconst [waitingRegistration, setWaitingRegistration] = useState<ServiceWorkerRegistration | null>(null);\n\n\tconst hasUpdate = waitingRegistration !== null;\n\n\tconst applyUpdate = useCallback(() => {\n\t\tif (applyServiceWorkerUpdate(waitingRegistration)) return true;\n\t\tif (!waitingRegistration) return false;\n\n\t\t/**\n\t\t * Если другой клиент уже активировал waiting worker, в текущей вкладке\n\t\t * registration.waiting станет пустым, но баннер ещё может быть видимым.\n\t\t */\n\t\treturn reloadPageForServiceWorkerUpdate();\n\t}, [waitingRegistration]);\n\n\tconst dismiss = useCallback(() => {\n\t\tsetWaitingRegistration(null);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (__DEV__) return;\n\n\t\tlet isActive = true;\n\t\tconst startupStartedAt = Date.now();\n\t\tlet startupResetTimerId: number | null = null;\n\t\tlet startupUpdateApplied = false;\n\n\t\tconst canApplyStartupUpdate = () =>\n\t\t\tisStartupNavigationForAutoUpdate() &&\n\t\t\tDate.now() - startupStartedAt <= SW_STARTUP_AUTO_UPDATE_GRACE_MS &&\n\t\t\tcanAutoApplyServiceWorkerUpdate();\n\n\t\tconst tryApplyStartupUpdate = (reg: ServiceWorkerRegistration) => {\n\t\t\tif (startupUpdateApplied) return true;\n\t\t\tif (!canApplyStartupUpdate()) return false;\n\n\t\t\tconst applied = applyServiceWorkerUpdate(reg);\n\t\t\tstartupUpdateApplied = applied;\n\n\t\t\treturn applied;\n\t\t};\n\n\t\tconst scheduleStartupChainReset = () => {\n\t\t\tconst elapsedMs = Date.now() - startupStartedAt;\n\t\t\tconst timeoutMs = Math.max(SW_STARTUP_AUTO_UPDATE_GRACE_MS - elapsedMs, 0);\n\n\t\t\tstartupResetTimerId = window.setTimeout(() => {\n\t\t\t\tif (!isActive) return;\n\n\t\t\t\tclearServiceWorkerUpdateReloadChain();\n\t\t\t}, timeoutMs);\n\t\t};\n\n\t\tconst handleUpdate = (reg: ServiceWorkerRegistration) => {\n\t\t\tif (!isActive) return;\n\n\t\t\t/**\n\t\t\t * Если update найден вскоре после reload/navigate, применяем его\n\t\t\t * автоматически. Это закрывает цепочку из нескольких деплоев,\n\t\t\t * накопленных во время простоя вкладки.\n\t\t\t */\n\t\t\tif (tryApplyStartupUpdate(reg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Если update найден уже во время активной сессии —\n\t\t\t * показываем баннер.\n\t\t\t */\n\t\t\tsetWaitingRegistration(reg);\n\t\t};\n\n\t\tasync function init() {\n\t\t\tconst reg = await registerServiceWorker({\n\t\t\t\tonUpdate: handleUpdate\n\t\t\t});\n\n\t\t\tif (!isActive) return;\n\n\t\t\t/**\n\t\t\t * Важный сценарий:\n\t\t\t * пользователь перезагрузил страницу, а новый SW уже был в waiting.\n\t\t\t * В этом случае не показываем баннер, а применяем update сразу.\n\t\t\t */\n\t\t\tif (hasWaitingServiceWorker(reg) && tryApplyStartupUpdate(reg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst checkedReg = await checkServiceWorkerUpdate();\n\n\t\t\tif (!isActive) return;\n\t\t\tif (startupUpdateApplied) return;\n\n\t\t\tif (hasWaitingServiceWorker(checkedReg) && tryApplyStartupUpdate(checkedReg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (hasWaitingServiceWorker(checkedReg)) {\n\t\t\t\tsetWaitingRegistration(checkedReg);\n\t\t\t}\n\n\t\t\tscheduleStartupChainReset();\n\t\t}\n\n\t\tvoid init();\n\n\t\tconst intervalId = window.setInterval(() => {\n\t\t\tvoid checkServiceWorkerUpdate().then((reg) => {\n\t\t\t\tif (!isActive) return;\n\n\t\t\t\tif (hasWaitingServiceWorker(reg)) {\n\t\t\t\t\tsetWaitingRegistration(reg);\n\t\t\t\t}\n\t\t\t});\n\t\t}, SW_UPDATE_CHECK_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tisActive = false;\n\t\t\tif (startupResetTimerId !== null) {\n\t\t\t\twindow.clearTimeout(startupResetTimerId);\n\t\t\t}\n\t\t\twindow.clearInterval(intervalId);\n\t\t\tunsubscribeServiceWorkerUpdate(handleUpdate);\n\t\t};\n\t}, []);\n\n\treturn {\n\t\thasUpdate,\n\t\tapplyUpdate,\n\t\tdismiss\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/pwa/serviceWorker.ts","../../src/pwa/useServiceWorkerUpdate.ts"],"sourcesContent":["import { logError } from \"../utils\";\nimport { isRecord } from \"../validators\";\n\nconst SW_SCOPE = __SERVICE_WORKER_SCOPE__;\nconst SW_URL = __SERVICE_WORKER_URL__;\nconst SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE = \"SW_ACTIVATED_BY_CLIENT\";\nconst SW_UPDATE_RELOAD_COUNT_KEY = __SERVICE_WORKER_UPDATE_RELOAD_COUNT_KEY__;\nconst MAX_SW_AUTO_UPDATE_RELOADS = __SERVICE_WORKER_MAX_AUTO_UPDATE_RELOADS__;\n\ntype SWUpdateHandler = (reg: ServiceWorkerRegistration) => void;\n\ntype ServiceWorkerClientMessage = {\n\ttype: typeof SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE;\n};\n\ntype RegisterSWOptions = {\n\t/**\n\t * Вызывается, когда новый SW установлен и ждёт активации.\n\t */\n\tonUpdate?: SWUpdateHandler;\n\n\t/**\n\t * Вызывается после успешной регистрации.\n\t */\n\tonReady?: (reg: ServiceWorkerRegistration) => void;\n};\n\nconst observedRegistrations = new WeakSet<ServiceWorkerRegistration>();\nconst updateHandlers = new Set<SWUpdateHandler>();\n\nlet latestRegistration: ServiceWorkerRegistration | null = null;\nlet refreshing = false;\nlet updateRequested = false;\nlet controllerChangeSubscribed = false;\nlet serviceWorkerMessageSubscribed = false;\n\nfunction readServiceWorkerClientMessage(value: unknown): ServiceWorkerClientMessage | null {\n\tif (!isRecord(value)) return null;\n\n\treturn value.type === SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE ? { type: SW_ACTIVATED_BY_CLIENT_MESSAGE_TYPE } : null;\n}\n\nfunction readServiceWorkerUpdateReloadCount(): number {\n\ttry {\n\t\tconst value = window.sessionStorage.getItem(SW_UPDATE_RELOAD_COUNT_KEY);\n\t\tconst count = Number(value);\n\n\t\treturn Number.isInteger(count) && count > 0 ? count : 0;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\nexport function recordServiceWorkerUpdateReload() {\n\ttry {\n\t\tconst nextCount = Math.min(readServiceWorkerUpdateReloadCount() + 1, MAX_SW_AUTO_UPDATE_RELOADS);\n\t\twindow.sessionStorage.setItem(SW_UPDATE_RELOAD_COUNT_KEY, String(nextCount));\n\t} catch {\n\t\t/**\n\t\t * sessionStorage может быть недоступен в приватном режиме или при\n\t\t * жёстких browser policy. Обновление всё равно должно продолжиться.\n\t\t */\n\t}\n}\n\nexport function canAutoApplyServiceWorkerUpdate(): boolean {\n\treturn readServiceWorkerUpdateReloadCount() < MAX_SW_AUTO_UPDATE_RELOADS;\n}\n\nexport function clearServiceWorkerUpdateReloadChain() {\n\ttry {\n\t\twindow.sessionStorage.removeItem(SW_UPDATE_RELOAD_COUNT_KEY);\n\t} catch {\n\t\t/**\n\t\t * Очистка маркера не критична: максимум пользователь увидит обычный\n\t\t * баннер вместо автоматического продолжения цепочки обновлений.\n\t\t */\n\t}\n}\n\nfunction reloadPage() {\n\tif (refreshing) return;\n\n\trefreshing = true;\n\twindow.location.reload();\n}\n\nfunction subscribeControllerChangeReload() {\n\tif (controllerChangeSubscribed) return;\n\n\tcontrollerChangeSubscribed = true;\n\n\tnavigator.serviceWorker.addEventListener(\"controllerchange\", () => {\n\t\t/**\n\t\t * Не перезагружаем страницу от любого controllerchange.\n\t\t * Перезагрузка нужна только когда мы сами вызвали SKIP_WAITING.\n\t\t */\n\t\tif (!updateRequested) return;\n\n\t\treloadPage();\n\t});\n}\n\nfunction subscribeServiceWorkerMessages() {\n\tif (serviceWorkerMessageSubscribed) return;\n\n\tserviceWorkerMessageSubscribed = true;\n\n\tnavigator.serviceWorker.addEventListener(\"message\", (event) => {\n\t\tconst message = readServiceWorkerClientMessage(event.data);\n\t\tif (!message) return;\n\t\tif (refreshing) return;\n\n\t\trecordServiceWorkerUpdateReload();\n\t\treloadPage();\n\t});\n}\n\nfunction emitUpdate(reg: ServiceWorkerRegistration) {\n\tfor (const handler of updateHandlers) {\n\t\thandler(reg);\n\t}\n}\n\nfunction observeRegistration(reg: ServiceWorkerRegistration) {\n\tif (observedRegistrations.has(reg)) return;\n\n\tobservedRegistrations.add(reg);\n\n\treg.addEventListener(\"updatefound\", () => {\n\t\tconst newWorker = reg.installing;\n\t\tif (!newWorker) return;\n\n\t\tnewWorker.addEventListener(\"statechange\", () => {\n\t\t\t/**\n\t\t\t * installed + есть controller = новая версия установлена,\n\t\t\t * но текущая страница всё ещё под старым SW.\n\t\t\t */\n\t\t\tif (newWorker.state === \"installed\" && navigator.serviceWorker.controller) {\n\t\t\t\tlatestRegistration = reg;\n\t\t\t\temitUpdate(reg);\n\t\t\t}\n\t\t});\n\t});\n\n\tsubscribeControllerChangeReload();\n\tsubscribeServiceWorkerMessages();\n}\n\nexport async function getServiceWorkerRegistration(): Promise<ServiceWorkerRegistration | null> {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\ttry {\n\t\tconst reg = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_SCOPE));\n\n\t\tif (!reg) return null;\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] get registration failed:\", err);\n\t\treturn null;\n\t}\n}\n\nexport async function registerServiceWorker(options: RegisterSWOptions = {}) {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\tif (options.onUpdate) {\n\t\tupdateHandlers.add(options.onUpdate);\n\t}\n\n\ttry {\n\t\tconst existingRegistration = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_SCOPE));\n\n\t\tconst reg =\n\t\t\texistingRegistration ??\n\t\t\t(await navigator.serviceWorker.register(SW_URL, {\n\t\t\t\tscope: SW_SCOPE,\n\t\t\t\tupdateViaCache: \"none\"\n\t\t\t}));\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\toptions.onReady?.(reg);\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] registration failed:\", err);\n\t\treturn null;\n\t}\n}\n\nexport function unsubscribeServiceWorkerUpdate(handler: SWUpdateHandler) {\n\tupdateHandlers.delete(handler);\n}\n\nexport function hasWaitingServiceWorker(reg: ServiceWorkerRegistration | null | undefined): reg is ServiceWorkerRegistration {\n\treturn Boolean(reg?.waiting);\n}\n\nexport function applyServiceWorkerUpdate(reg: ServiceWorkerRegistration | null = latestRegistration) {\n\tconst waiting = reg?.waiting;\n\tif (!waiting) return false;\n\n\tupdateRequested = true;\n\trecordServiceWorkerUpdateReload();\n\n\twaiting.postMessage({ type: \"SKIP_WAITING\" });\n\n\treturn true;\n}\n\nexport function reloadPageForServiceWorkerUpdate() {\n\trecordServiceWorkerUpdateReload();\n\treloadPage();\n\n\treturn true;\n}\n\nexport async function checkServiceWorkerUpdate() {\n\tif (!(\"serviceWorker\" in navigator)) return null;\n\n\ttry {\n\t\tconst reg = latestRegistration ?? (await navigator.serviceWorker.getRegistration(SW_SCOPE));\n\t\tif (!reg) return null;\n\n\t\tlatestRegistration = reg;\n\t\tobserveRegistration(reg);\n\n\t\tawait reg.update();\n\n\t\treturn reg;\n\t} catch (err) {\n\t\tlogError(\"[SW] update check failed:\", err);\n\t\treturn null;\n\t}\n}\n\n/**\n * Очищает SW-кеш OData для указанной политики.\n *\n * Примеры: `\"ttl=24h;name=ref\"`, `\"ttl=10m;max=200;name=ui\"`.\n *\n * После вызова следующий запрос с той же политикой пойдёт в сеть.\n */\nexport function invalidateSwCacheProfile(profile: string) {\n\tnavigator.serviceWorker?.controller?.postMessage({\n\t\ttype: \"invalidateProfile\",\n\t\tprofile\n\t});\n}\n","import { useCallback, useEffect, useState } from \"react\";\n\nimport {\n\tapplyServiceWorkerUpdate,\n\tcanAutoApplyServiceWorkerUpdate,\n\tcheckServiceWorkerUpdate,\n\tclearServiceWorkerUpdateReloadChain,\n\thasWaitingServiceWorker,\n\tregisterServiceWorker,\n\treloadPageForServiceWorkerUpdate,\n\tunsubscribeServiceWorkerUpdate\n} from \"./serviceWorker\";\n\n// TODO: Vite env\nconst SW_UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1000;\n// TODO: Vite env\nconst SW_STARTUP_AUTO_UPDATE_GRACE_MS = 30 * 1000;\n\nfunction isStartupNavigationForAutoUpdate(): boolean {\n\tconst [entry] = performance.getEntriesByType(\"navigation\") as PerformanceNavigationTiming[];\n\n\t/**\n\t * reload — пользователь явно обновил страницу.\n\t * navigate — пользователь открыл страницу по ссылке/закладке.\n\t */\n\treturn entry?.type === \"reload\" || entry?.type === \"navigate\";\n}\n\nexport function useServiceWorkerUpdate() {\n\tconst [waitingRegistration, setWaitingRegistration] = useState<ServiceWorkerRegistration | null>(null);\n\n\tconst hasUpdate = waitingRegistration !== null;\n\n\tconst applyUpdate = useCallback(() => {\n\t\tif (applyServiceWorkerUpdate(waitingRegistration)) return true;\n\t\tif (!waitingRegistration) return false;\n\n\t\t/**\n\t\t * Если другой клиент уже активировал waiting worker, в текущей вкладке\n\t\t * registration.waiting станет пустым, но баннер ещё может быть видимым.\n\t\t */\n\t\treturn reloadPageForServiceWorkerUpdate();\n\t}, [waitingRegistration]);\n\n\tconst dismiss = useCallback(() => {\n\t\tsetWaitingRegistration(null);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (__DEV__) return;\n\n\t\tlet isActive = true;\n\t\tconst startupStartedAt = Date.now();\n\t\tlet startupResetTimerId: number | null = null;\n\t\tlet startupUpdateApplied = false;\n\n\t\tconst canApplyStartupUpdate = () =>\n\t\t\tisStartupNavigationForAutoUpdate() &&\n\t\t\tDate.now() - startupStartedAt <= SW_STARTUP_AUTO_UPDATE_GRACE_MS &&\n\t\t\tcanAutoApplyServiceWorkerUpdate();\n\n\t\tconst tryApplyStartupUpdate = (reg: ServiceWorkerRegistration) => {\n\t\t\tif (startupUpdateApplied) return true;\n\t\t\tif (!canApplyStartupUpdate()) return false;\n\n\t\t\tconst applied = applyServiceWorkerUpdate(reg);\n\t\t\tstartupUpdateApplied = applied;\n\n\t\t\treturn applied;\n\t\t};\n\n\t\tconst scheduleStartupChainReset = () => {\n\t\t\tconst elapsedMs = Date.now() - startupStartedAt;\n\t\t\tconst timeoutMs = Math.max(SW_STARTUP_AUTO_UPDATE_GRACE_MS - elapsedMs, 0);\n\n\t\t\tstartupResetTimerId = window.setTimeout(() => {\n\t\t\t\tif (!isActive) return;\n\n\t\t\t\tclearServiceWorkerUpdateReloadChain();\n\t\t\t}, timeoutMs);\n\t\t};\n\n\t\tconst handleUpdate = (reg: ServiceWorkerRegistration) => {\n\t\t\tif (!isActive) return;\n\n\t\t\t/**\n\t\t\t * Если update найден вскоре после reload/navigate, применяем его\n\t\t\t * автоматически. Это закрывает цепочку из нескольких деплоев,\n\t\t\t * накопленных во время простоя вкладки.\n\t\t\t */\n\t\t\tif (tryApplyStartupUpdate(reg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Если update найден уже во время активной сессии —\n\t\t\t * показываем баннер.\n\t\t\t */\n\t\t\tsetWaitingRegistration(reg);\n\t\t};\n\n\t\tasync function init() {\n\t\t\tconst reg = await registerServiceWorker({\n\t\t\t\tonUpdate: handleUpdate\n\t\t\t});\n\n\t\t\tif (!isActive) return;\n\n\t\t\t/**\n\t\t\t * Важный сценарий:\n\t\t\t * пользователь перезагрузил страницу, а новый SW уже был в waiting.\n\t\t\t * В этом случае не показываем баннер, а применяем update сразу.\n\t\t\t */\n\t\t\tif (hasWaitingServiceWorker(reg) && tryApplyStartupUpdate(reg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst checkedReg = await checkServiceWorkerUpdate();\n\n\t\t\tif (!isActive) return;\n\t\t\tif (startupUpdateApplied) return;\n\n\t\t\tif (hasWaitingServiceWorker(checkedReg) && tryApplyStartupUpdate(checkedReg)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (hasWaitingServiceWorker(checkedReg)) {\n\t\t\t\tsetWaitingRegistration(checkedReg);\n\t\t\t}\n\n\t\t\tscheduleStartupChainReset();\n\t\t}\n\n\t\tvoid init();\n\n\t\tconst intervalId = window.setInterval(() => {\n\t\t\tvoid checkServiceWorkerUpdate().then((reg) => {\n\t\t\t\tif (!isActive) return;\n\n\t\t\t\tif (hasWaitingServiceWorker(reg)) {\n\t\t\t\t\tsetWaitingRegistration(reg);\n\t\t\t\t}\n\t\t\t});\n\t\t}, SW_UPDATE_CHECK_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tisActive = false;\n\t\t\tif (startupResetTimerId !== null) {\n\t\t\t\twindow.clearTimeout(startupResetTimerId);\n\t\t\t}\n\t\t\twindow.clearInterval(intervalId);\n\t\t\tunsubscribeServiceWorkerUpdate(handleUpdate);\n\t\t};\n\t}, []);\n\n\treturn {\n\t\thasUpdate,\n\t\tapplyUpdate,\n\t\tdismiss\n\t};\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"serviceWorker.d.ts","sourceRoot":"","sources":["../../src/pwa/serviceWorker.ts"],"names":[],"mappings":"AAWA,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAC;AAMhE,KAAK,iBAAiB,GAAG;IACxB;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAC;CACnD,CAAC;AA4BF,wBAAgB,+BAA+B,SAU9C;AAED,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED,wBAAgB,mCAAmC,SASlD;AAuED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAgB9F;AAED,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,iBAAsB,6CA2B1E;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,QAEtE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,yBAAyB,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,IAAI,yBAAyB,CAE3H;AAED,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,yBAAyB,GAAG,IAAyB,WAUlG;AAED,wBAAgB,gCAAgC,YAK/C;AAED,wBAAsB,wBAAwB,8CAiB7C;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,QAKvD"}
1
+ {"version":3,"file":"serviceWorker.d.ts","sourceRoot":"","sources":["../../src/pwa/serviceWorker.ts"],"names":[],"mappings":"AASA,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAC;AAMhE,KAAK,iBAAiB,GAAG;IACxB;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAC;CACnD,CAAC;AA4BF,wBAAgB,+BAA+B,SAU9C;AAED,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED,wBAAgB,mCAAmC,SASlD;AAuED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAgB9F;AAED,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,iBAAsB,6CA2B1E;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,eAAe,QAEtE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,yBAAyB,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,IAAI,yBAAyB,CAE3H;AAED,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,yBAAyB,GAAG,IAAyB,WAUlG;AAED,wBAAgB,gCAAgC,YAK/C;AAED,wBAAsB,wBAAwB,8CAiB7C;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,QAKvD"}
@@ -0,0 +1,70 @@
1
+ //#region src/pwa/swCachePolicy.ts
2
+ var e = 5e3;
3
+ function t(e) {
4
+ return e?.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || void 0;
5
+ }
6
+ function n(e) {
7
+ let t = e.trim().toLowerCase();
8
+ if (t === "forever") return {
9
+ durationLabel: "forever",
10
+ ttlMs: Infinity
11
+ };
12
+ let n = t.match(/^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/);
13
+ if (!n) return;
14
+ let r = Number(n[1]), i = n[2];
15
+ if (!Number.isFinite(r) || r <= 0) return;
16
+ let a = Math.round(r * {
17
+ ms: 1,
18
+ s: 1e3,
19
+ m: 60 * 1e3,
20
+ h: 3600 * 1e3,
21
+ d: 1440 * 60 * 1e3
22
+ }[i]);
23
+ if (!(!Number.isFinite(a) || a <= 0 || a > 31536e6)) return {
24
+ durationLabel: `${n[1]}${i}`.replace(".", "_"),
25
+ amount: r,
26
+ unit: i,
27
+ ttlMs: a
28
+ };
29
+ }
30
+ function r(t) {
31
+ if (!t) return;
32
+ let n = Number(t.trim());
33
+ if (!(!Number.isInteger(n) || n <= 0)) return Math.min(n, e);
34
+ }
35
+ function i(e, t, n) {
36
+ return `odata-${t ? `${t}-${e.durationLabel}` : e.durationLabel}${n ? `-max${n}` : ""}`;
37
+ }
38
+ function a(e) {
39
+ let a = e?.trim();
40
+ if (!a) return;
41
+ if (a.toLowerCase() === "off") return { mode: "off" };
42
+ let [o, ...s] = a.split(/[;,]/).map((e) => e.trim()).filter(Boolean), [c, l] = o?.split("=", 2) ?? [], u = c?.toLowerCase();
43
+ if (u !== "ttl" && u !== "bust" || !l) return;
44
+ let d = n(l);
45
+ if (!d) return;
46
+ let f = new Map(s.map((e) => e.split("=", 2).map((e) => e.trim())).map(([e, t]) => [e.toLowerCase(), t]).filter(([e, t]) => !!(e && t))), p = r(f.get("maxentries") ?? f.get("max") ?? f.get("entries")), m = t(f.get("cachename") ?? f.get("cache") ?? f.get("name")), h = p ?? 100;
47
+ return {
48
+ mode: u,
49
+ amount: d.amount,
50
+ unit: d.unit,
51
+ name: m,
52
+ maxEntries: p,
53
+ resolvedMaxEntries: h,
54
+ ttlMs: d.ttlMs,
55
+ cacheName: i(d, m, p)
56
+ };
57
+ }
58
+ function o(e) {
59
+ let t = a(e);
60
+ return t?.mode === "ttl" || t?.mode === "bust" ? t.cacheName : void 0;
61
+ }
62
+ function s(n) {
63
+ if (n.mode === "off") return "off";
64
+ let r = typeof n.amount == "number" && Number.isFinite(n.amount) && n.amount > 0 ? n.amount : 24, i = n.unit ?? "h", a = typeof n.maxEntries == "number" && Number.isInteger(n.maxEntries) && n.maxEntries > 0 ? Math.min(n.maxEntries, e) : void 0, o = t(n.name), s = [a ? `max=${a}` : void 0, o ? `name=${o}` : void 0].filter(Boolean);
65
+ return `${n.mode}=${r}${i}${s.length ? `;${s.join(";")}` : ""}`;
66
+ }
67
+ //#endregion
68
+ export { s as buildSwCachePolicyValue, t as normalizeSwCacheName, a as parseSwCachePolicy, o as resolveSwCacheCacheNameByPolicy };
69
+
70
+ //# sourceMappingURL=sw-cache-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sw-cache-policy.js","names":[],"sources":["../../src/pwa/swCachePolicy.ts"],"sourcesContent":["export type SwCachePolicyMode = \"off\" | \"ttl\" | \"bust\";\nexport type SwCacheDurationUnit = \"ms\" | \"s\" | \"m\" | \"h\" | \"d\";\n\nexport interface ParsedSwCachePolicy {\n\tmode: SwCachePolicyMode;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tname?: string;\n\tmaxEntries?: number;\n\tresolvedMaxEntries?: number;\n\tttlMs?: number;\n\tcacheName?: string;\n}\n\nexport interface SwCachePolicyDraft {\n\tmode: SwCachePolicyMode;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tname?: string;\n\tmaxEntries?: number;\n}\n\nconst DEFAULT_ODATA_CACHE_MAX_ENTRIES = 100;\nconst MAX_ODATA_CACHE_MAX_ENTRIES = 5000;\nconst MAX_ODATA_CACHE_TTL_MS = 365 * 24 * 60 * 60 * 1000;\n\ntype DurationParseResult = {\n\tdurationLabel: string;\n\tamount?: number;\n\tunit?: SwCacheDurationUnit;\n\tttlMs: number;\n};\n\nexport function normalizeSwCacheName(value: string | undefined): string | undefined {\n\tconst normalized = value\n\t\t?.trim()\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\treturn normalized || undefined;\n}\n\nfunction parseDurationToken(value: string): DurationParseResult | undefined {\n\tconst normalized = value.trim().toLowerCase();\n\tif (normalized === \"forever\") {\n\t\treturn {\n\t\t\tdurationLabel: \"forever\",\n\t\t\tttlMs: Infinity\n\t\t};\n\t}\n\n\tconst match = normalized.match(/^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/);\n\tif (!match) return undefined;\n\n\tconst amount = Number(match[1]);\n\tconst unit = match[2] as SwCacheDurationUnit;\n\tif (!Number.isFinite(amount) || amount <= 0) return undefined;\n\n\tconst multiplierByUnit: Record<SwCacheDurationUnit, number> = {\n\t\tms: 1,\n\t\ts: 1000,\n\t\tm: 60 * 1000,\n\t\th: 60 * 60 * 1000,\n\t\td: 24 * 60 * 60 * 1000\n\t};\n\tconst ttlMs = Math.round(amount * multiplierByUnit[unit]);\n\tif (!Number.isFinite(ttlMs) || ttlMs <= 0 || ttlMs > MAX_ODATA_CACHE_TTL_MS) return undefined;\n\n\tconst durationLabel = `${match[1]}${unit}`.replace(\".\", \"_\");\n\treturn {\n\t\tdurationLabel,\n\t\tamount,\n\t\tunit,\n\t\tttlMs\n\t};\n}\n\nfunction parseMaxEntries(value: string | undefined): number | undefined {\n\tif (!value) return undefined;\n\n\tconst parsed = Number(value.trim());\n\tif (!Number.isInteger(parsed) || parsed <= 0) return undefined;\n\n\treturn Math.min(parsed, MAX_ODATA_CACHE_MAX_ENTRIES);\n}\n\nfunction createCacheName(duration: DurationParseResult, name: string | undefined, maxEntries: number | undefined) {\n\tconst cacheLabel = name ? `${name}-${duration.durationLabel}` : duration.durationLabel;\n\treturn `odata-${cacheLabel}${maxEntries ? `-max${maxEntries}` : \"\"}`;\n}\n\nexport function parseSwCachePolicy(value: string | null | undefined): ParsedSwCachePolicy | undefined {\n\tconst normalized = value?.trim();\n\tif (!normalized) return undefined;\n\tif (normalized.toLowerCase() === \"off\") return { mode: \"off\" };\n\n\tconst parts = normalized\n\t\t.split(/[;,]/)\n\t\t.map((part) => part.trim())\n\t\t.filter(Boolean);\n\tconst [modePart, ...optionParts] = parts;\n\tconst [rawMode, rawDuration] = modePart?.split(\"=\", 2) ?? [];\n\tconst mode = rawMode?.toLowerCase();\n\tif ((mode !== \"ttl\" && mode !== \"bust\") || !rawDuration) return undefined;\n\n\tconst duration = parseDurationToken(rawDuration);\n\tif (!duration) return undefined;\n\n\tconst options = new Map(\n\t\toptionParts\n\t\t\t.map((part) => part.split(\"=\", 2).map((item) => item.trim()) as [string, string | undefined])\n\t\t\t.map(([key, optionValue]) => [key.toLowerCase(), optionValue] as [string, string | undefined])\n\t\t\t.filter(([key, optionValue]) => Boolean(key && optionValue))\n\t);\n\tconst maxEntries = parseMaxEntries(options.get(\"maxentries\") ?? options.get(\"max\") ?? options.get(\"entries\"));\n\tconst name = normalizeSwCacheName(options.get(\"cachename\") ?? options.get(\"cache\") ?? options.get(\"name\"));\n\tconst resolvedMaxEntries = maxEntries ?? DEFAULT_ODATA_CACHE_MAX_ENTRIES;\n\n\treturn {\n\t\tmode,\n\t\tamount: duration.amount,\n\t\tunit: duration.unit,\n\t\tname,\n\t\tmaxEntries,\n\t\tresolvedMaxEntries,\n\t\tttlMs: duration.ttlMs,\n\t\tcacheName: createCacheName(duration, name, maxEntries)\n\t};\n}\n\nexport function resolveSwCacheCacheNameByPolicy(value: string | undefined): string | undefined {\n\tconst parsed = parseSwCachePolicy(value);\n\treturn parsed?.mode === \"ttl\" || parsed?.mode === \"bust\" ? parsed.cacheName : undefined;\n}\n\nexport function buildSwCachePolicyValue(draft: SwCachePolicyDraft): string {\n\tif (draft.mode === \"off\") return \"off\";\n\n\tconst amount = typeof draft.amount === \"number\" && Number.isFinite(draft.amount) && draft.amount > 0 ? draft.amount : 24;\n\tconst unit = draft.unit ?? \"h\";\n\tconst maxEntries =\n\t\ttypeof draft.maxEntries === \"number\" && Number.isInteger(draft.maxEntries) && draft.maxEntries > 0\n\t\t\t? Math.min(draft.maxEntries, MAX_ODATA_CACHE_MAX_ENTRIES)\n\t\t\t: undefined;\n\tconst name = normalizeSwCacheName(draft.name);\n\tconst options = [maxEntries ? `max=${maxEntries}` : undefined, name ? `name=${name}` : undefined].filter(Boolean);\n\n\treturn `${draft.mode}=${amount}${unit}${options.length ? `;${options.join(\";\")}` : \"\"}`;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/types/types.ts"],"sourcesContent":["export type State = \"\" | \"none\" | \"information\" | \"success\" | \"warning\" | \"error\";\n\nexport type AbapBoolean = \"X\" | \" \";\n\nexport type Primitive = string | number | boolean | null | undefined;\nexport type BaseType = Date | Primitive;\n\nexport function isBaseType(value: unknown): value is BaseType {\n\tif (value === null || value === undefined) return true;\n\tif (value instanceof Date) return Number.isFinite(value.getTime());\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\n\treturn typeof value === \"string\" || typeof value === \"boolean\";\n}\n\nexport type ArrayType = BaseType[];\nexport type ObjectType = Readonly<Record<string, BaseType>> | object;\nexport type InputType = BaseType | ArrayType | ObjectType;\n\nexport type RangeType = readonly [BaseType | null, BaseType | null];\n\n// Тип для обработчиков изменений\nexport type ChangeHandler<T extends InputType> = (value: T) => void;\n\nexport type RowRecord = Record<string, unknown>;\n"],"mappings":";AAOA,SAAgB,EAAW,GAAmC;CAK7D,OAJI,KAAU,OAAoC,KAC9C,aAAiB,OAAa,OAAO,SAAS,EAAM,QAAQ,CAAC,IAC7D,OAAO,KAAU,WAAiB,OAAO,SAAS,CAAK,IAEpD,OAAO,KAAU,YAAY,OAAO,KAAU;AACtD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/types/types.ts"],"sourcesContent":["export type State = \"\" | \"none\" | \"information\" | \"success\" | \"warning\" | \"error\";\n\nexport type AbapBoolean = \"X\" | \" \";\n\nexport type Primitive = string | number | boolean | null | undefined;\nexport type BaseType = Date | Primitive;\n\nexport function isBaseType(value: unknown): value is BaseType {\n\tif (value === null || value === undefined) return true;\n\tif (value instanceof Date) return Number.isFinite(value.getTime());\n\tif (typeof value === \"number\") return Number.isFinite(value);\n\n\treturn typeof value === \"string\" || typeof value === \"boolean\";\n}\n\nexport type ArrayType = BaseType[];\nexport type ObjectType = Readonly<Record<string, BaseType>> | object;\nexport type InputType = BaseType | ArrayType | ObjectType;\n\nexport type RangeType = readonly [BaseType | null, BaseType | null];\n\nexport type RowRecord = Record<string, unknown>;\n"],"mappings":";AAOA,SAAgB,EAAW,GAAmC;CAK7D,OAJI,KAAU,OAAoC,KAC9C,aAAiB,OAAa,OAAO,SAAS,EAAM,QAAQ,CAAC,IAC7D,OAAO,KAAU,WAAiB,OAAO,SAAS,CAAK,IAEpD,OAAO,KAAU,YAAY,OAAO,KAAU;AACtD"}
@@ -7,6 +7,5 @@ export type ArrayType = BaseType[];
7
7
  export type ObjectType = Readonly<Record<string, BaseType>> | object;
8
8
  export type InputType = BaseType | ArrayType | ObjectType;
9
9
  export type RangeType = readonly [BaseType | null, BaseType | null];
10
- export type ChangeHandler<T extends InputType> = (value: T) => void;
11
10
  export type RowRecord = Record<string, unknown>;
12
11
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,EAAE,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAElF,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,CAAC;AAEpC,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AACrE,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;AAExC,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAM5D;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,EAAE,CAAC;AACnC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC;AACrE,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC;AAGpE,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAEpE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,EAAE,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAElF,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,CAAC;AAEpC,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AACrE,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;AAExC,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAM5D;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,EAAE,CAAC;AACnC,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC;AACrE,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC;AAEpE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuzaki13/react-foundation-lib",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
5
5
  "license": "MIT",
6
6
  "author": "Ryuzaki13",
@@ -131,6 +131,10 @@
131
131
  "types": "./dist/pwa/index.d.ts",
132
132
  "import": "./dist/pwa/index.js"
133
133
  },
134
+ "./pwa/sw-cache-policy": {
135
+ "types": "./dist/pwa/swCachePolicy.d.ts",
136
+ "import": "./dist/pwa/sw-cache-policy.js"
137
+ },
134
138
  "./query-client": {
135
139
  "types": "./dist/query-client/index.d.ts",
136
140
  "import": "./dist/query-client/index.js"
@@ -262,6 +266,9 @@
262
266
  "pwa": [
263
267
  "dist/pwa/index.d.ts"
264
268
  ],
269
+ "pwa/sw-cache-policy": [
270
+ "dist/pwa/swCachePolicy.d.ts"
271
+ ],
265
272
  "query-client": [
266
273
  "dist/query-client/index.d.ts"
267
274
  ],
@@ -1 +0,0 @@
1
- {"version":3,"file":"getClientFingerprint.d.ts","sourceRoot":"","sources":["../../src/auth/getClientFingerprint.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnE"}