@ryuzaki13/react-foundation-lib 1.8.2 → 1.9.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.
@@ -253,6 +253,9 @@ await downloadExcelWorkbook({
253
253
  import { downloadExcelMatrixWorkbook } from "@ryuzaki13/react-foundation-lib/excel";
254
254
 
255
255
  await downloadExcelMatrixWorkbook("Расписание.xlsx", {
256
+ // Базовый шрифт Normal определяет, сколько пикселей занимает символьная
257
+ // ширина из `columns[].width`, даже если ячейки используют другой шрифт.
258
+ defaultFont: { fontFamily: "Verdana", fontSize: 12 },
256
259
  sheets: [
257
260
  {
258
261
  name: "Неделя",
@@ -280,6 +283,11 @@ await downloadExcelMatrixWorkbook("Расписание.xlsx", {
280
283
  быть целыми неотрицательными: ноль означает автоматическое число страниц по
281
284
  этой оси.
282
285
 
286
+ `defaultFont` задаёт шрифт стиля Normal всей книги. Excel измеряет символьную
287
+ ширину столбца относительно этого шрифта, поэтому одинаковое значение
288
+ `columns[].width` при разных базовых шрифтах может давать разную ширину в
289
+ пикселях. Стили отдельных ячеек при этом остаются независимыми.
290
+
283
291
  `createExcelMatrixWorkbookBlob` не инициирует скачивание и подходит для preview
284
292
  и проверки сформированного файла. `downloadExcelMatrixWorkbook` делегирует
285
293
  browser download общему `downloadFileFromBlob`.
@@ -13,6 +13,9 @@ import { Meta } from "@storybook/addon-docs/blocks";
13
13
 
14
14
  Используйте точный subpath. Внутренний файл `serviceWorker` не является самостоятельным публичным export, даже если отдельные функции используются самим hook.
15
15
 
16
+ `useBrowserOnlineStatus()` возвращает browser hint `navigator.onLine` через `useSyncExternalStore` и подписывается на события
17
+ `online`/`offline`. Это сигнал для UX и запуска revalidation, а не доказательство доступности конкретного backend.
18
+
16
19
  ## Часть 1. Обновление Service Worker
17
20
 
18
21
  ### Задача hook
@@ -378,6 +381,7 @@ Waiting worker остаётся установленным. Следующая
378
381
  | Экспорт | Публичный path | Назначение |
379
382
  | ----------------------------------- | -------------- | -------------------------------------------------- |
380
383
  | `useServiceWorkerUpdate()` | `/pwa` | Регистрация, проверка и UI-состояние обновления SW |
384
+ | `useBrowserOnlineStatus()` | `/pwa` | Реактивный browser online/offline hint |
381
385
  | `parseSwCachePolicy()` | оба | Разобрать текстовую политику |
382
386
  | `buildSwCachePolicyValue()` | оба | Собрать каноническую строку из draft |
383
387
  | `normalizeSwCacheName()` | оба | Подготовить безопасную часть cache name |
@@ -274,12 +274,14 @@ Stale не означает «удалена». Data может продолжа
274
274
  | Нужны данные только пока открыта вкладка? | Обычная query без `persistedQueryMeta`. |
275
275
  | Данные публичные, тяжёлые и могут пережить reload? | Рассмотреть `persistedQueryMeta`. |
276
276
  | Данные зависят от пользователя, роли или server session? | `sessionScopedQueryMeta`. |
277
+ | Session snapshot явно разрешено читать offline? | `createSessionQueryPersistenceMeta` + active scope. |
277
278
  | Нужно передать обновление другой открытой вкладке? | Broadcast installation и подходящее cache event. |
278
279
  | UI должен измениться до ответа mutation? | Optimistic update с обязательным rollback policy. |
279
280
  | Server response авторитетен и уже содержит новое состояние? | `setQueryData`/`onSuccessOptimistic`. |
280
281
  | После mutation нужен свежий server snapshot? | Scoped `invalidateQueries` с выбранным `refetchType`. |
281
282
 
282
- `persistedQueryMeta` и `sessionScopedQueryMeta` взаимоисключающие. Session data не должна переживать смену пользователя.
283
+ `persistedQueryMeta` и `sessionScopedQueryMeta` взаимоисключающие. Для узкого offline allowlist session data используется
284
+ отдельный `sessionPersistence` contract: он требует активную server-session identity и абсолютный `expiresAt`.
283
285
 
284
286
  ## Быстрый старт
285
287
 
@@ -489,7 +491,8 @@ queryOptions({
489
491
  });
490
492
  ```
491
493
 
492
- `Record<string, unknown>` разрешает consumer-проекту добавлять собственную metadata. Infrastructure этого модуля читает только `persist` и `sessionScoped`.
494
+ `Record<string, unknown>` разрешает consumer-проекту добавлять собственную metadata. Infrastructure этого модуля читает
495
+ `persist`, `sessionScoped` и явный `sessionPersistence` contract.
493
496
 
494
497
  ### `persistedQueryMeta`
495
498
 
@@ -533,6 +536,36 @@ queryOptions({
533
536
 
534
537
  При смене сессии host обязан вызвать `resetSessionScopedQueries`.
535
538
 
539
+ ### Session-scoped offline persistence
540
+
541
+ Только для явно согласованного offline allowlist создайте bounded metadata:
542
+
543
+ ```ts
544
+ import {
545
+ createSessionQueryPersistenceMeta,
546
+ setSessionQueryPersistenceScope
547
+ } from "@ryuzaki13/react-foundation-lib/query-client";
548
+
549
+ await setSessionQueryPersistenceScope({
550
+ id: session.id,
551
+ expiresAt: session.expiresAt
552
+ });
553
+
554
+ queryOptions({
555
+ queryKey: ["opened-week", weekStart],
556
+ queryFn: fetchWeek,
557
+ meta: createSessionQueryPersistenceMeta({
558
+ cacheName: "opened-weeks",
559
+ maxEntries: 24
560
+ }),
561
+ networkMode: "offlineFirst"
562
+ });
563
+ ```
564
+
565
+ Scope устанавливается отдельно от query contract, не содержит cookie secret/access token и меняет in-memory границу до очистки IndexedDB.
566
+ При `null`, истёкшем `expiresAt` или смене `id` старые session snapshots удаляются. `networkMode: "offlineFirst"` нужен, чтобы
567
+ TanStack вызвал persister до network pause. Cached data остаётся неавторитетным: write после reconnect обязан повторно получить серверный state.
568
+
536
569
  ### Meta нельзя совмещать
537
570
 
538
571
  Неверно:
@@ -550,13 +583,14 @@ meta: {
550
583
 
551
584
  - публичный/безопасный долгоживущий справочник → `persistedQueryMeta`;
552
585
  - auth/session dependent data → `sessionScopedQueryMeta`;
586
+ - разрешённый bounded offline snapshot → `createSessionQueryPersistenceMeta`;
553
587
  - обычные transient data → meta не нужна.
554
588
 
555
589
  ## Настройка project adapter
556
590
 
557
591
  ### Зачем нужен adapter
558
592
 
559
- Foundation-пакет не знает имя SAP system, client, tenant или окружение конкретного приложения. `QueryPersistenceProjectAdapter` позволяет host передать устойчивый namespace и собственный buster:
593
+ Foundation-пакет не знает имя SAP system, client, tenant или окружение конкретного приложения. `QueryPersistenceProjectAdapter` позволяет host передать устойчивый namespace и собственный buster. Текущая session identity задаётся отдельным runtime API и не входит в adapter:
560
594
 
561
595
  ```ts
562
596
  type QueryPersistenceProjectAdapter = {
@@ -778,7 +812,8 @@ Storage не скрывает ошибки. Promise отклоняется, ес
778
812
  const persister = createReactQueryPersister();
779
813
  ```
780
814
 
781
- Возвращает результат `experimental_createQueryPersister` либо `undefined`, если IndexedDB недоступен.
815
+ Возвращает routing-обёртку над `experimental_createQueryPersister` либо `undefined`, если IndexedDB недоступен. Обычные public query
816
+ идут в прежний persister, а query с `sessionPersistence` — в отдельный cache активной сессии с независимым `maxEntries`.
782
817
 
783
818
  Настройки foundation wrapper:
784
819
 
@@ -2356,9 +2391,11 @@ Profile tests подтверждают:
2356
2391
 
2357
2392
  | Symbol | Вид | Назначение |
2358
2393
  | ------------------------ | ----- | ----------------------------------------------- |
2359
- | `AppQueryMeta` | type | `persist`, `sessionScoped` и consumer metadata. |
2360
- | `persistedQueryMeta` | const | Opt-in public persistence marker. |
2361
- | `sessionScopedQueryMeta` | const | Session reset marker. |
2394
+ | `AppQueryMeta` | type | Public/session persistence и consumer metadata. |
2395
+ | `SessionQueryPersistencePolicy` | type | Имя и bounded limit session cache. |
2396
+ | `persistedQueryMeta` | const | Opt-in public persistence marker. |
2397
+ | `sessionScopedQueryMeta` | const | Session reset marker без browser persistence. |
2398
+ | `createSessionQueryPersistenceMeta` | function | Явный opt-in bounded persistence текущей server session. |
2362
2399
 
2363
2400
  ### Persistence project adapter
2364
2401
 
@@ -2377,6 +2414,10 @@ Profile tests подтверждают:
2377
2414
  | `shouldPersistQuery` | function | Opt-in predicate с session safety. |
2378
2415
  | `createIndexedDbQueryStorage` | function | AsyncStorage поверх IndexedDB. |
2379
2416
  | `createReactQueryPersister` | function | Настроенный per-query TanStack persister. |
2417
+ | `SessionQueryPersistenceScope` | type | Public session id и абсолютный expiresAt. |
2418
+ | `getSessionQueryPersistenceScope` | function | Читает валидный активный browser scope. |
2419
+ | `setSessionQueryPersistenceScope` | function | Меняет scope и очищает чужие snapshots. |
2420
+ | `clearSessionQueryPersistence` | function | Удаляет один или все session snapshots. |
2380
2421
 
2381
2422
  Options type `createIndexedDbQueryStorage` не экспортируется отдельным symbol; используйте структурный object или `Parameters<typeof createIndexedDbQueryStorage>[0]`.
2382
2423