@ryuzaki13/react-foundation-api 1.1.16 → 1.1.17

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.
Files changed (36) hide show
  1. package/README.md +32 -43
  2. package/dist/chunks/{odataFetchFn-vnAXC-c0.js → odataFetchFn-B9wSQpUS.js} +11 -11
  3. package/dist/chunks/{odataFetchFn-vnAXC-c0.js.map → odataFetchFn-B9wSQpUS.js.map} +1 -1
  4. package/dist/odata/fetchCollectionData.d.ts +1 -1
  5. package/dist/odata/fetchCollectionData.d.ts.map +1 -1
  6. package/dist/odata/index.js +111 -112
  7. package/dist/odata/index.js.map +1 -1
  8. package/dist/odata/projectODataCollectionSort.d.ts +1 -1
  9. package/dist/odata/projectODataCollectionSort.d.ts.map +1 -1
  10. package/dist/odata/types.d.ts +1 -2
  11. package/dist/odata/types.d.ts.map +1 -1
  12. package/dist/odata/useODataCollection.d.ts +1 -1
  13. package/dist/odata/useODataCollection.d.ts.map +1 -1
  14. package/dist/odata/useODataCollectionQuery.d.ts +1 -1
  15. package/dist/odata/useODataCollectionQuery.d.ts.map +1 -1
  16. package/dist/odata/useODataEntity.d.ts +1 -1
  17. package/dist/odata/useODataEntity.d.ts.map +1 -1
  18. package/dist/persisted/index.js +1 -1
  19. package/package.json +2 -2
  20. package/src/adt/README.mdx +164 -0
  21. package/src/async/README.mdx +253 -0
  22. package/src/error-report/README.mdx +148 -0
  23. package/src/foundationApi.mdx +123 -0
  24. package/src/http/README.mdx +221 -0
  25. package/src/odata/README.mdx +790 -0
  26. package/src/persisted/README.mdx +454 -0
  27. package/src/resource/README.mdx +358 -0
  28. package/src/server-fn/README.mdx +194 -0
  29. package/src/transport/README.mdx +183 -0
  30. package/src/README.md +0 -937
  31. package/src/async/README.md +0 -623
  32. package/src/async/async.mdx +0 -6
  33. package/src/odata/README.md +0 -761
  34. package/src/odata/odataFetchFn.mdx +0 -6
  35. package/src/persisted/README.md +0 -598
  36. package/src/persisted/persisted.mdx +0 -6
@@ -0,0 +1,123 @@
1
+ import { Meta } from "@storybook/addon-docs/blocks";
2
+
3
+ <Meta title="Foundation API/Обзор" />
4
+
5
+ # `@ryuzaki13/react-foundation-api`
6
+
7
+ Пакет соединяет приложение с внешними API. Он содержит transport, интеграцию с TanStack Query, SAP/OData V2 infrastructure и небольшие orchestration helpers. Документ рассчитан на разработчика, который видит только опубликованный пакет и его типы.
8
+
9
+ ## Подключение
10
+
11
+ ```bash
12
+ npm install @ryuzaki13/react-foundation-api @ryuzaki13/react-foundation-lib
13
+ ```
14
+
15
+ Корневого импорта нет. Всегда указывайте точный subpath:
16
+
17
+ ```ts
18
+ import { httpFetchPayload } from "@ryuzaki13/react-foundation-api/http";
19
+ import { useResourceQuery } from "@ryuzaki13/react-foundation-api/resource";
20
+ import { useODataMetadataQuery } from "@ryuzaki13/react-foundation-api/odata";
21
+ ```
22
+
23
+ ## Где находится пакет в архитектуре
24
+
25
+ ```text
26
+ host application: entities / features / widgets / pages
27
+
28
+
29
+ @ryuzaki13/react-foundation-api
30
+ transport + query orchestration
31
+
32
+
33
+ @ryuzaki13/react-foundation-lib
34
+ pure helpers + metadata + query policies
35
+ ```
36
+
37
+ `foundation-api` не должен знать конкретную бизнес-сущность, экран или UI-компонент. DTO/domain mapping конкретного проекта остаётся у host-приложения. UI primitives находятся в `@ryuzaki13/react-foundation-ui`.
38
+
39
+ ## Как выбрать entrypoint
40
+
41
+ | Нужно сделать | Entrypoint | Важная граница |
42
+ | --- | --- | --- |
43
+ | Вызвать обычный JSON REST endpoint | `http` | Нет SAP/SSO/X-CSRF политики |
44
+ | Вызвать SAP Gateway/OData V2 | `odata` | Metadata, envelope, SSO и X-CSRF принадлежат этому слою |
45
+ | Описать произвольные queries/mutations | `resource` | Transport передаётся операциями |
46
+ | Описать сохранённые records с фиксированными capability | `persisted` | Только list/latest/history/save/create/delete |
47
+ | Подключить функцию вида `fn({ data })` | `server-fn` | Это adapter операции, не сервер и не security boundary |
48
+ | Выполнить пакет независимых задач | `async` | Выберите all-at-once или bounded concurrency |
49
+ | Прочитать ADT transport XML | `adt` | Endpoint `/sap/bc/adt`, XML |
50
+ | Прочитать UI2 workbench/customizing requests | `transport` | Это SAP transport requests, а не generic HTTP transport |
51
+ | Отправить error-report draft | `error-report` | Сбор/хранение draft принадлежит `foundation-lib` |
52
+
53
+ ## Самая частая ошибка: одинаковые слова, разные уровни
54
+
55
+ ### `http` и `odata`
56
+
57
+ `http` подходит для нейтрального REST. Он не получает CSRF token, не распознаёт SAML form и не снимает OData V2 envelope. Для SAP Gateway используйте `odata`, даже если технически запрос тоже выполняется через `fetch`.
58
+
59
+ ### `resource` и `persisted`
60
+
61
+ `resource` позволяет задавать любые имена операций: например `search`, `details`, `archive`. `persisted` — специализированный facade для сохранённых записей с именами `list`, `latest`, `history`, `save`, `create`, `delete`.
62
+
63
+ ### `adt` и `transport`
64
+
65
+ Оба entrypoint читают SAP transports, но из разных API:
66
+
67
+ | Entrypoint | Endpoint/format | Результат |
68
+ | --- | --- | --- |
69
+ | `adt` | `/sap/bc/adt/cts/transports`, XML | Общий `UserTransport` из ADT header |
70
+ | `transport` | UI2 OData endpoints, JSON | Нормализованные workbench/customizing requests |
71
+
72
+ ## TanStack Query: кто чем владеет
73
+
74
+ `resource`, `persisted` и часть `odata` работают внутри `QueryClientProvider`. Query key определяет identity кеша; `staleTime` — свежесть; `gcTime` — время хранения неиспользуемого query; `AbortSignal` позволяет отменять чтение. Mutation не обновляет все связанные query автоматически: это делает явно выбранная cache strategy.
75
+
76
+ ```tsx
77
+ import { QueryClientProvider } from "@tanstack/react-query";
78
+ import { createQueryClient } from "@ryuzaki13/react-foundation-lib/query-client";
79
+
80
+ const queryClient = createQueryClient();
81
+
82
+ export function App() {
83
+ return <QueryClientProvider client={queryClient}>{/* приложение */}</QueryClientProvider>;
84
+ }
85
+ ```
86
+
87
+ Не создавайте новый `QueryClient` при каждом render.
88
+
89
+ ## Runtime
90
+
91
+ | API | Browser | Server runtime |
92
+ | --- | :---: | :---: |
93
+ | `async` и pure normalization | Да | Да |
94
+ | `http` | Да | Да, если доступны Web Fetch API |
95
+ | pure `resource` factories | Да | Да |
96
+ | React hooks | Да | Только в поддерживаемом React runtime/provider |
97
+ | OData SSO recovery | Да | Нет: использует `window`, `document`, iframe |
98
+ | SAP transport fetchers | Да | Только при корректном fetch/proxy/cookie окружении |
99
+
100
+ OData transport использует compile-time constants и проектный adapter. Host-сборка должна предоставлять ожидаемую конфигурацию; подробности приведены на странице OData.
101
+
102
+ ## Service Worker cache
103
+
104
+ Некоторые query factories передают строку политики через header `x-sw-cache`. Сам header ничего не кеширует: Service Worker host-приложения должен понимать тот же protocol.
105
+
106
+ ```text
107
+ off
108
+ ttl=24h
109
+ ttl=10m;max=200;name=ui
110
+ bust=24h;name=ref
111
+ ```
112
+
113
+ `bust` означает принудительное обновление сетевого snapshot в соответствующей cache policy. Не используйте SW cache для персональных/секретных payload без отдельной модели изоляции.
114
+
115
+ ## Каталог страниц
116
+
117
+ | Область | Документы |
118
+ | --- | --- |
119
+ | Transport | [HTTP](./http/README.mdx), [OData](./odata/README.mdx), [ADT](./adt/README.mdx), [SAP Transport Requests](./transport/README.mdx) |
120
+ | Query infrastructure | [Resource](./resource/README.mdx), [Persisted](./persisted/README.mdx), [Server Function](./server-fn/README.mdx) |
121
+ | Orchestration и diagnostics | [Async](./async/README.mdx), [Error Report](./error-report/README.mdx) |
122
+
123
+ Каждая страница содержит точный import, пошаговый guide, runtime/lifecycle, ошибки и таблицу публичных exports. Не импортируйте файл глубоким путём, если его символ не опубликован соответствующим entrypoint.
@@ -0,0 +1,221 @@
1
+ import { Meta } from "@storybook/addon-docs/blocks";
2
+
3
+ <Meta title="Foundation API/Transport/HTTP" />
4
+
5
+ # Обычный HTTP через `@ryuzaki13/react-foundation-api/http`
6
+
7
+ Модуль выполняет нейтральные HTTP/REST запросы и создаёт функции, совместимые с TanStack Query. Внешний payload остаётся `unknown`, пока consumer явно не проверит его через `parse`.
8
+
9
+ ## Содержание
10
+
11
+ - [Когда использовать](#когда-использовать)
12
+ - [Импорт](#импорт)
13
+ - [`httpFetch`](#httpfetch)
14
+ - [`httpFetchPayload`](#httpfetchpayload)
15
+ - [`httpJsonQueryFn`](#httpjsonqueryfn)
16
+ - [`httpJsonMutationFn`](#httpjsonmutationfn)
17
+ - [`RouteError`](#routeerror)
18
+ - [Ошибки, runtime и безопасность](#ошибки-runtime-и-безопасность)
19
+ - [Полный API](#полный-api)
20
+
21
+ ## Когда использовать
22
+
23
+ ```text
24
+ обычный REST/HTTP ──► /http
25
+ SAP Gateway/OData ──► /odata
26
+ ```
27
+
28
+ `http` не добавляет SAP headers, `credentials: include`, X-CSRF token, SSO recovery, OData V2 headers и не снимает envelope `{ d: { results } }`. Для SAP/OData используйте [`/odata`](../odata/README.mdx).
29
+
30
+ ## Импорт
31
+
32
+ ```ts
33
+ import {
34
+ httpFetch,
35
+ httpFetchPayload,
36
+ httpJsonMutationFn,
37
+ httpJsonQueryFn,
38
+ RouteError
39
+ } from "@ryuzaki13/react-foundation-api/http";
40
+
41
+ import type {
42
+ HttpMutationFnOptions,
43
+ HttpQueryFnOptions,
44
+ HttpRequestOptions
45
+ } from "@ryuzaki13/react-foundation-api/http";
46
+ ```
47
+
48
+ Нужны глобальные Web Fetch API: `fetch`, `Request`, `Response`, `Headers`, `URL`.
49
+
50
+ ## `httpFetch`
51
+
52
+ ```ts
53
+ const response = await httpFetch("/health", {
54
+ baseUrl: "https://api.example.com",
55
+ init: {
56
+ method: "GET",
57
+ headers: { Accept: "text/plain" }
58
+ }
59
+ });
60
+
61
+ console.log(await response.text());
62
+ ```
63
+
64
+ Функция возвращает исходный `Response`, если `response.ok === true`. Body ещё не прочитан.
65
+
66
+ Для string input URL строится простой конкатенацией:
67
+
68
+ ```text
69
+ baseUrl + input
70
+ ```
71
+
72
+ Helper не исправляет slash:
73
+
74
+ ```ts
75
+ // Правильно.
76
+ { baseUrl: "https://api.example.com", input: "/users" }
77
+
78
+ // Получится https://api.example.comusers — неверно.
79
+ { baseUrl: "https://api.example.com", input: "users" }
80
+ ```
81
+
82
+ Если input является `Request` или `URL`, `baseUrl` игнорируется. Это не proxy/url resolver.
83
+
84
+ ## `httpFetchPayload`
85
+
86
+ ```ts
87
+ const payload: unknown = await httpFetchPayload("/api/profile");
88
+ ```
89
+
90
+ Правила parser-а:
91
+
92
+ | Response | Результат |
93
+ | --- | --- |
94
+ | status `204` или `205` | `null` |
95
+ | `Content-Type` содержит `application/json` | `response.json()` |
96
+ | любой другой content type | `response.text()` |
97
+
98
+ Результат намеренно `unknown`. Generic type не может проверить данные, пришедшие по сети:
99
+
100
+ ```ts
101
+ function parseProfile(value: unknown): Profile {
102
+ if (!isProfile(value)) throw new Error("Некорректный Profile payload");
103
+ return value;
104
+ }
105
+
106
+ const profile = parseProfile(await httpFetchPayload("/api/profile"));
107
+ ```
108
+
109
+ Если сервер отдаёт JSON без корректного `Content-Type`, модуль вернёт JSON как string. Исправьте backend или разберите string на своей boundary.
110
+
111
+ ## `httpJsonQueryFn`
112
+
113
+ Factory возвращает async-функцию с формой TanStack Query `queryFn`:
114
+
115
+ ```ts
116
+ const loadProfile = httpJsonQueryFn("/api/profile", {
117
+ baseUrl: "https://api.example.com",
118
+ parse: parseProfile,
119
+ swCache: "ttl=10m;name=profile"
120
+ });
121
+
122
+ const profile = await loadProfile({ signal });
123
+ ```
124
+
125
+ С React Query:
126
+
127
+ ```tsx
128
+ const profileQuery = useQuery({
129
+ queryKey: ["profile", userId],
130
+ queryFn: httpJsonQueryFn(`/api/users/${userId}`, {
131
+ parse: parseProfile
132
+ })
133
+ });
134
+ ```
135
+
136
+ Factory:
137
+
138
+ - использует `init.signal`, если он задан явно;
139
+ - иначе подставляет signal, переданный TanStack Query;
140
+ - добавляет `x-sw-cache`, только если `swCache` truthy;
141
+ - вызывает обязательный `parse(payload)` после успешного запроса.
142
+
143
+ Header `x-sw-cache` — только protocol с Service Worker. Без соответствующего SW он не создаёт cache.
144
+
145
+ `parse` может вернуть преобразованную domain model, но обычно лучше ограничиться runtime validation и вынести бизнес-mapping владельцу entity.
146
+
147
+ ## `httpJsonMutationFn`
148
+
149
+ ```ts
150
+ type CreateUserInput = { name: string };
151
+
152
+ const createUser = httpJsonMutationFn<CreateUserInput, User>("/api/users", {
153
+ method: "POST",
154
+ mapBody: (input) => ({ displayName: input.name }),
155
+ parse: parseUser
156
+ });
157
+
158
+ const user = await createUser({ name: "Анна" });
159
+ ```
160
+
161
+ С `useMutation`:
162
+
163
+ ```tsx
164
+ const mutation = useMutation({ mutationFn: createUser });
165
+ mutation.mutate({ name: "Анна" });
166
+ ```
167
+
168
+ Поведение:
169
+
170
+ 1. Вычисляет body через `mapBody(input)` или использует input.
171
+ 2. Сериализует body через `JSON.stringify`.
172
+ 3. Использует method `POST` по умолчанию; поддерживаются `POST`, `PUT`, `PATCH`, `DELETE`.
173
+ 4. Добавляет `Content-Type: application/json`, только если header ещё не задан.
174
+ 5. Выполняет запрос и передаёт payload в обязательный `parse`.
175
+
176
+ Сигнатура mutation function также допускает второй `AbortSignal` при ручном вызове. Стандартный `useMutation` TanStack Query не передаёт signal автоматически. Если `options.init.signal` уже задан, второй signal не заменит его.
177
+
178
+ `JSON.stringify` может бросить ошибку для `BigInt`/циклического объекта. `undefined` body сериализуется в `undefined`; убедитесь, что это соответствует endpoint.
179
+
180
+ ## HTTP errors
181
+
182
+ Для `response.ok === false` обе низкоуровневые функции бросают обычный `Error`:
183
+
184
+ ```text
185
+ HTTP error: <message>
186
+ ```
187
+
188
+ Если error body — непустой text, он становится message. Для JSON error object поля `message`, `error` и `details` не извлекаются; fallback — `<status> <statusText>`.
189
+
190
+ Network error/CORS/abort от глобального `fetch` пробрасывается без обёртки.
191
+
192
+ ## `RouteError`
193
+
194
+ ```ts
195
+ throw new RouteError(404);
196
+ ```
197
+
198
+ Это маленький `Error` с числовым полем `status` и пустым message. `httpFetch` сам его не создаёт. Класс нужен для routing/error-boundary policy приложения, если оно договорилось понимать такой error.
199
+
200
+ ## Ошибки, runtime и безопасность
201
+
202
+ - TypeScript generic не валидирует сеть; используйте `parse`/schema guard.
203
+ - `baseUrl` и path не нормализуются и не кодируются.
204
+ - Authorization, cookies, CORS и retry задаёт consumer через `init` или внешний слой.
205
+ - Модуль не делает timeout. Для него используйте `AbortController`.
206
+ - Не помещайте token/secret в query string и cache key.
207
+ - `x-sw-cache` для персональных данных требует изолированной cache policy.
208
+ - На SSR абсолютный/относительный URL и cookies должны быть настроены server runtime-ом.
209
+
210
+ ## Полный API
211
+
212
+ | Export | Результат |
213
+ | --- | --- |
214
+ | `httpFetch` | Успешный `Response` |
215
+ | `httpFetchPayload` | JSON/text/`null` как `unknown` |
216
+ | `httpJsonQueryFn` | Query-compatible async function |
217
+ | `httpJsonMutationFn` | Mutation-compatible async function |
218
+ | `RouteError` | Error с `status` |
219
+ | `HttpRequestOptions` | `baseUrl` и `init` |
220
+ | `HttpQueryFnOptions` | Request options + `swCache` + `parse` |
221
+ | `HttpMutationFnOptions` | Request options + method/body mapper/parse |