@brickflow/http 0.0.15 → 0.0.16
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 +249 -284
- package/dist/create-get.d.ts +12 -0
- package/dist/create-get.d.ts.map +1 -0
- package/dist/http.d.ts +26 -41
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +48 -21
- package/dist/index.mjs.map +1 -1
- package/dist/nuxt-DAmJOX58.js +233 -0
- package/dist/nuxt-DAmJOX58.js.map +1 -0
- package/dist/nuxt.d.ts +25 -42
- package/dist/nuxt.d.ts.map +1 -1
- package/dist/nuxt.mjs +2 -222
- package/package.json +10 -9
- package/src/app.d.ts +11 -0
- package/src/create-get.ts +43 -0
- package/src/http.ts +286 -0
- package/src/index.ts +3 -0
- package/src/nuxt.ts +355 -0
- package/src/utils.ts +50 -0
- package/dist/nuxt.mjs.map +0 -1
- package/dist/utils-DMSINHi5.js +0 -39
- package/dist/utils-DMSINHi5.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,425 +1,390 @@
|
|
|
1
1
|
# @brickflow/http
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Минимальный HTTP-пакет для brickflow:
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
- Nuxt
|
|
7
|
-
-
|
|
8
|
-
-
|
|
5
|
+
- `createHttp` для низкоуровневых `GET`/`POST`
|
|
6
|
+
- `createUseHttp` для Nuxt state-обёртки с cache, SSR и ручным `fetch`
|
|
7
|
+
- `defineGet` для типизированных endpoint-фабрик
|
|
8
|
+
- `createUseCase` для DI-паттерна в доменных модулях
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Актуальные примеры в репозитории лежат в `apps/playground`.
|
|
11
11
|
|
|
12
12
|
## Exports
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Рекомендуемый импорт:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createHttp, createUseHttp, createUseCase, defineGet } from '@brickflow/http'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Из пакета также экспортируются:
|
|
15
21
|
|
|
16
|
-
- `createHttp`
|
|
17
22
|
- `createURL`
|
|
18
23
|
- `hashData`
|
|
19
|
-
-
|
|
24
|
+
- типы `HttpClient`, `HttpConfig`, `HttpEndpoint`, `HttpKey`, `HttpParam`, `HttpResponse`, `HttpResponseData`
|
|
25
|
+
- Nuxt-типы `UseHttpFn`, `UseHttpOptions`, `UseHttpResult`
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
Сабпуть `@brickflow/http/nuxt` остаётся доступным, но в текущей кодовой базе `playground` использует импорты из корня.
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
- `createGet`
|
|
25
|
-
- Nuxt types: `UseHttpOptions`, `UseHttpResult`, `UseHttpFn`
|
|
29
|
+
## Типизация API
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
Пакет не знает схему вашего API заранее. Её нужно объявить в проекте через `declare module '@brickflow/http'`.
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
Актуальный пример из `apps/playground/types/http.d.ts`:
|
|
30
34
|
|
|
31
35
|
```ts
|
|
32
|
-
import type {
|
|
33
|
-
|
|
34
|
-
type
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
import type { HttpKey, HttpResponseData } from '@brickflow/http'
|
|
37
|
+
|
|
38
|
+
type Convention = {
|
|
39
|
+
'/api/http-error-demo': null
|
|
40
|
+
'/products':
|
|
41
|
+
| {
|
|
42
|
+
limit: number
|
|
43
|
+
products: {
|
|
44
|
+
category: string
|
|
45
|
+
id: number
|
|
46
|
+
price: number
|
|
47
|
+
rating: number
|
|
48
|
+
thumbnail: string
|
|
49
|
+
title: string
|
|
50
|
+
}[]
|
|
51
|
+
skip: number
|
|
52
|
+
total: number
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
message: 'error'
|
|
56
|
+
}
|
|
37
57
|
}
|
|
38
58
|
|
|
39
59
|
declare module '@brickflow/http' {
|
|
40
|
-
interface
|
|
41
|
-
|
|
42
|
-
error: BrickHttpError
|
|
60
|
+
interface HttpConfig<TKey extends HttpKey = HttpKey> {
|
|
61
|
+
ignore?: (data: HttpResponseData<TKey>) => boolean
|
|
43
62
|
}
|
|
63
|
+
|
|
64
|
+
interface HttpEndpoint extends Convention {}
|
|
44
65
|
}
|
|
66
|
+
|
|
67
|
+
export {}
|
|
45
68
|
```
|
|
46
69
|
|
|
47
|
-
|
|
70
|
+
Что это даёт:
|
|
48
71
|
|
|
49
|
-
- `HttpKey`
|
|
50
|
-
- `
|
|
51
|
-
- `
|
|
52
|
-
- `
|
|
72
|
+
- `HttpKey` становится union из endpoint-ключей
|
|
73
|
+
- `httpClient.get('/products')` получает корректный тип ответа
|
|
74
|
+
- `createGet<Params>()('/products')` наследует тот же контракт
|
|
75
|
+
- `HttpConfig` можно расширять своими полями, как в playground через `ignore`
|
|
53
76
|
|
|
54
|
-
|
|
77
|
+
Если нужен типизированный `error` в `UseHttpResult`, дополнительно расширьте `HttpErrorMap`. По умолчанию `error` имеет тип `never`, а разделение между `data` и `error` зависит только от runtime `isError`.
|
|
55
78
|
|
|
56
|
-
##
|
|
79
|
+
## createHttp
|
|
57
80
|
|
|
58
|
-
|
|
81
|
+
Актуальный пример из `apps/playground/plugins/01.di.ts`:
|
|
59
82
|
|
|
60
83
|
```ts
|
|
61
84
|
import { createHttp } from '@brickflow/http'
|
|
62
85
|
|
|
86
|
+
import { handlePlaygroundResponse } from '~/core/http-error'
|
|
87
|
+
|
|
63
88
|
const httpClient = createHttp({
|
|
64
|
-
baseURL:
|
|
65
|
-
headers:
|
|
66
|
-
'
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
'X-Requested-With': 'XMLHttpRequest',
|
|
70
|
-
}),
|
|
71
|
-
onResponseError: responseInterceptor,
|
|
89
|
+
baseURL: 'https://dummyjson.com',
|
|
90
|
+
headers: {
|
|
91
|
+
'X-Playground-Http': 'playground-runtime',
|
|
92
|
+
},
|
|
93
|
+
onResponse: handlePlaygroundResponse,
|
|
72
94
|
})
|
|
73
95
|
```
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
Поддерживаемые опции:
|
|
76
98
|
|
|
77
|
-
- `baseURL`
|
|
78
|
-
- `
|
|
79
|
-
- `
|
|
80
|
-
- `
|
|
81
|
-
- `
|
|
82
|
-
- `timeout`
|
|
99
|
+
- `baseURL: string`
|
|
100
|
+
- `arrayMode?: 'json' | 'repeat'`
|
|
101
|
+
- `fetch?: typeof fetch`
|
|
102
|
+
- `headers?: Record<string, string | undefined> | (() => Record<string, string | undefined>)`
|
|
103
|
+
- `onResponse?: (response) => void | Promise<void>`
|
|
104
|
+
- `timeout?: number`
|
|
83
105
|
|
|
84
|
-
|
|
106
|
+
### GET
|
|
85
107
|
|
|
86
108
|
```ts
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
109
|
+
const response = await httpClient.get('/products', {
|
|
110
|
+
params: {
|
|
111
|
+
limit: 10,
|
|
112
|
+
},
|
|
113
|
+
retry: {
|
|
114
|
+
delay: 300,
|
|
115
|
+
retries: 3,
|
|
116
|
+
},
|
|
117
|
+
})
|
|
91
118
|
```
|
|
92
119
|
|
|
93
|
-
|
|
120
|
+
Особенности:
|
|
94
121
|
|
|
95
|
-
|
|
122
|
+
- query строится из `params`
|
|
123
|
+
- `GET` поддерживает retry для `429` и `5xx`
|
|
124
|
+
- delay экспоненциальный: `delay * 2 ** attempt`
|
|
125
|
+
- `signal` можно передать как один `AbortSignal` или массив `AbortSignal[]`
|
|
96
126
|
|
|
97
|
-
|
|
127
|
+
### POST
|
|
98
128
|
|
|
99
129
|
```ts
|
|
100
|
-
|
|
101
|
-
const { data } = await httpClient.post('content/create', {
|
|
102
|
-
description,
|
|
103
|
-
title,
|
|
104
|
-
type,
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
return data
|
|
108
|
-
}
|
|
130
|
+
const { data } = await httpClient.post('/api/http-error-demo')
|
|
109
131
|
```
|
|
110
132
|
|
|
111
|
-
|
|
133
|
+
Для `POST`:
|
|
134
|
+
|
|
135
|
+
- `Record<string, unknown>` сериализуется в JSON
|
|
136
|
+
- `FormData` отправляется как есть
|
|
137
|
+
- retry нет
|
|
138
|
+
- если response не JSON, пакет вернёт пустой объект в `data`
|
|
139
|
+
|
|
140
|
+
### Расширение `HttpConfig`
|
|
141
|
+
|
|
142
|
+
Так как `HttpConfig` расширяемый, можно прокидывать свои поля в запрос и читать их в `onResponse`.
|
|
143
|
+
|
|
144
|
+
Пример из playground:
|
|
112
145
|
|
|
113
146
|
```ts
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
})
|
|
147
|
+
httpClient.get('/products', {
|
|
148
|
+
ignore: (data) => data?.limit === 20,
|
|
149
|
+
})
|
|
150
|
+
```
|
|
119
151
|
|
|
120
|
-
|
|
152
|
+
```ts
|
|
153
|
+
export function handlePlaygroundResponse(
|
|
154
|
+
response: Parameters<NonNullable<CreateHttpOptions['onResponse']>>[0],
|
|
155
|
+
): void {
|
|
156
|
+
console.log(response.config.ignore?.(response.data))
|
|
121
157
|
}
|
|
122
158
|
```
|
|
123
159
|
|
|
124
|
-
|
|
160
|
+
## createUseHttp
|
|
125
161
|
|
|
126
|
-
-
|
|
127
|
-
- you do not need `pending`, `hasFirstData`, cache, SSR sync, or `fetch()`
|
|
162
|
+
В `apps/playground/core/http.ts` app-level инстанс создаётся один раз:
|
|
128
163
|
|
|
129
|
-
|
|
164
|
+
```ts
|
|
165
|
+
import { createUseHttp, defineGet } from '@brickflow/http'
|
|
130
166
|
|
|
131
|
-
|
|
167
|
+
import { dbDeleteKeysWithPart, dbGet, dbSafeSet } from './indexdb'
|
|
132
168
|
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
const CACHE_DB_NAME = 'smart-cache-v2'
|
|
170
|
+
const CACHE_STORE_NAME = 'playground-http'
|
|
135
171
|
|
|
136
172
|
const useHttp = createUseHttp({
|
|
137
173
|
getCache: () => ({
|
|
138
|
-
deleteKeysWithPart: async (part) => await dbDeleteKeysWithPart(part,
|
|
139
|
-
get: async (key) => await dbGet(key,
|
|
140
|
-
set: async (key, value, ttl) =>
|
|
174
|
+
deleteKeysWithPart: async (part: string) => await dbDeleteKeysWithPart(part, CACHE_DB_NAME, CACHE_STORE_NAME),
|
|
175
|
+
get: async <T>(key: string) => await dbGet<T>(key, CACHE_DB_NAME, CACHE_STORE_NAME),
|
|
176
|
+
set: async <T>(key: string, value: T, ttl: number) =>
|
|
177
|
+
await dbSafeSet<T>(key, value, CACHE_DB_NAME, CACHE_STORE_NAME, ttl),
|
|
141
178
|
}),
|
|
142
179
|
getHttpClient: () => useNuxtApp().$http,
|
|
143
|
-
isDev: () =>
|
|
180
|
+
isDev: () => false,
|
|
144
181
|
})
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Dependencies:
|
|
148
|
-
|
|
149
|
-
- `getHttpClient`
|
|
150
|
-
- `getCache`
|
|
151
|
-
- `isError`
|
|
152
|
-
- `isDev`
|
|
153
|
-
- `ttl`
|
|
154
|
-
- `channelName`
|
|
155
182
|
|
|
156
|
-
|
|
183
|
+
export const createGet = defineGet(useHttp)
|
|
184
|
+
```
|
|
157
185
|
|
|
158
|
-
|
|
186
|
+
Зависимости:
|
|
159
187
|
|
|
160
|
-
|
|
188
|
+
- `getHttpClient` обязательно
|
|
189
|
+
- `getCache` опционально
|
|
190
|
+
- `isDev` опционально
|
|
191
|
+
- `isError` опционально
|
|
192
|
+
- `ttl` опционально
|
|
193
|
+
- `channelName` опционально
|
|
161
194
|
|
|
162
|
-
|
|
195
|
+
`createUseHttp` возвращает функцию `useHttp(options)`, которая создаёт реактивное состояние запроса:
|
|
163
196
|
|
|
164
|
-
|
|
165
|
-
|
|
197
|
+
- `data`
|
|
198
|
+
- `error`
|
|
199
|
+
- `pending`
|
|
200
|
+
- `pendingCache`
|
|
201
|
+
- `hasFirstData`
|
|
202
|
+
- `hasFreshData`
|
|
203
|
+
- `fetch(params?, { signal? })`
|
|
166
204
|
|
|
167
|
-
|
|
205
|
+
### Опции useHttp
|
|
168
206
|
|
|
169
|
-
|
|
170
|
-
|
|
207
|
+
- `url`
|
|
208
|
+
- `initParams`
|
|
209
|
+
- `mapParams`
|
|
210
|
+
- `effect`
|
|
211
|
+
- `isError`
|
|
212
|
+
- `lazy`
|
|
213
|
+
- `server`
|
|
171
214
|
|
|
172
|
-
|
|
215
|
+
`effect(data, config)` вызывается и для cache, и для fresh-response:
|
|
173
216
|
|
|
174
|
-
|
|
217
|
+
- `config.cached === true` для cache
|
|
218
|
+
- `config.cached === false` для сети
|
|
219
|
+
- `config.params` содержит уже применённые params
|
|
175
220
|
|
|
176
|
-
|
|
221
|
+
## defineGet
|
|
177
222
|
|
|
178
|
-
|
|
223
|
+
`defineGet` связывает app-level `useHttp` с endpoint-фабриками:
|
|
179
224
|
|
|
180
225
|
```ts
|
|
181
|
-
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### With params
|
|
185
|
-
|
|
186
|
-
Example from `domains/feed/use-case.ts`:
|
|
226
|
+
const useHttp = createUseHttp({ ... })
|
|
187
227
|
|
|
188
|
-
|
|
189
|
-
home: createGet<{
|
|
190
|
-
category?: 'fresh' | 'popular' | 'recommended' | 'updated'
|
|
191
|
-
limit: number
|
|
192
|
-
offset: number
|
|
193
|
-
}>()('feed/home')
|
|
228
|
+
export const createGet = defineGet(useHttp)
|
|
194
229
|
```
|
|
195
230
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Example from `domains/content/use-case.ts`:
|
|
231
|
+
Дальше в домене можно описывать запросы коротко и типизированно:
|
|
199
232
|
|
|
200
233
|
```ts
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}>()('content/analytics', {
|
|
205
|
-
ignore(res) {
|
|
206
|
-
if (res.data.kind === 'no_auth') {
|
|
207
|
-
return true
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return false
|
|
211
|
-
},
|
|
212
|
-
})
|
|
234
|
+
products: createGet<{
|
|
235
|
+
limit: number
|
|
236
|
+
}>()('/products')
|
|
213
237
|
```
|
|
214
238
|
|
|
215
|
-
|
|
239
|
+
`defineGet` поддерживает два слоя настроек:
|
|
216
240
|
|
|
217
|
-
|
|
241
|
+
1. Базовые настройки endpoint-а при объявлении:
|
|
218
242
|
|
|
219
243
|
```ts
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (!config.cached) {
|
|
227
|
-
userStore.setUser(data.user)
|
|
228
|
-
userStore.setRegion(data.region)
|
|
229
|
-
}
|
|
230
|
-
},
|
|
244
|
+
const products = createGet<{ limit: number }>()('/products', {
|
|
245
|
+
mapParams: (params) => ({
|
|
246
|
+
limit: params?.limit ?? 10,
|
|
247
|
+
}),
|
|
231
248
|
})
|
|
232
249
|
```
|
|
233
250
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
Example from `domains/storage/use-case.ts`:
|
|
251
|
+
2. Runtime-опции при вызове:
|
|
237
252
|
|
|
238
253
|
```ts
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
},
|
|
254
|
+
const productsHttp = await products({
|
|
255
|
+
initParams: { limit: 20 },
|
|
256
|
+
lazy: true,
|
|
257
|
+
server: false,
|
|
245
258
|
})
|
|
246
259
|
```
|
|
247
260
|
|
|
248
|
-
|
|
261
|
+
Если `effect`, `mapParams` или `isError` указаны и при объявлении, и при вызове, пакет объединяет их так:
|
|
249
262
|
|
|
250
|
-
|
|
263
|
+
- `effect` вызывает оба обработчика
|
|
264
|
+
- `isError` из runtime имеет приоритет
|
|
265
|
+
- `mapParams` из runtime имеет приоритет
|
|
251
266
|
|
|
252
|
-
|
|
267
|
+
## createUseCase
|
|
253
268
|
|
|
254
|
-
|
|
255
|
-
const searchFastHttp = await app.$di.search.fast({
|
|
256
|
-
lazy: true,
|
|
257
|
-
server: false,
|
|
258
|
-
})
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Trigger fetch manually
|
|
269
|
+
В playground доменный модуль собирается через `createUseCase`:
|
|
262
270
|
|
|
263
271
|
```ts
|
|
264
|
-
|
|
265
|
-
text: searchQuery.value,
|
|
266
|
-
})
|
|
267
|
-
```
|
|
272
|
+
import { createUseCase } from '@brickflow/http'
|
|
268
273
|
|
|
269
|
-
|
|
274
|
+
import { createGet } from '~/core/http'
|
|
270
275
|
|
|
271
|
-
|
|
272
|
-
|
|
276
|
+
export default createUseCase()(({ httpClient }) => {
|
|
277
|
+
return {
|
|
278
|
+
async apiError() {
|
|
279
|
+
const { data } = await httpClient.post('/api/http-error-demo')
|
|
280
|
+
return data
|
|
281
|
+
},
|
|
273
282
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
283
|
+
products: createGet<{
|
|
284
|
+
limit: number
|
|
285
|
+
}>()('/products'),
|
|
286
|
+
}
|
|
287
|
+
})
|
|
278
288
|
```
|
|
279
289
|
|
|
280
|
-
|
|
290
|
+
Это даёт простой контракт для DI: use-case получает `httpClient`, а внутри может смешивать:
|
|
281
291
|
|
|
282
|
-
|
|
292
|
+
- прямые mutation/one-shot запросы через `httpClient`
|
|
293
|
+
- stateful GET-запросы через `createGet`
|
|
283
294
|
|
|
284
|
-
|
|
285
|
-
:loading="myContentHttp.pending || myContentHttp.pendingCache"
|
|
286
|
-
```
|
|
295
|
+
## Использование в компоненте
|
|
287
296
|
|
|
288
|
-
|
|
297
|
+
Актуальный пример из `apps/playground/pages/index.vue`:
|
|
289
298
|
|
|
290
299
|
```vue
|
|
291
|
-
|
|
300
|
+
<script lang="ts" setup>
|
|
301
|
+
const app = useNuxtApp()
|
|
302
|
+
const httpProducts = await app.$di.product.products({
|
|
303
|
+
lazy: true,
|
|
304
|
+
})
|
|
305
|
+
</script>
|
|
306
|
+
|
|
307
|
+
<template>
|
|
308
|
+
<button
|
|
309
|
+
type="button"
|
|
310
|
+
@click="httpProducts.fetch()"
|
|
311
|
+
>
|
|
312
|
+
{{ httpProducts.hasFirstData }} Action
|
|
313
|
+
</button>
|
|
314
|
+
|
|
315
|
+
{{ httpProducts.data }}
|
|
316
|
+
</template>
|
|
292
317
|
```
|
|
293
318
|
|
|
294
|
-
|
|
319
|
+
Типичный сценарий:
|
|
295
320
|
|
|
296
|
-
|
|
321
|
+
1. Создать request-state через `await endpoint({ ...options })`
|
|
322
|
+
2. Если `lazy: true`, вызвать `fetch()` вручную
|
|
323
|
+
3. Читать `data`, `error`, `pending`, `pendingCache`
|
|
297
324
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
- it is called for success
|
|
301
|
-
- it is called for error
|
|
302
|
-
- you can branch on `data.status`
|
|
303
|
-
- `config.cached` tells whether the payload came from cache
|
|
304
|
-
- `config.params` contains mapped params used for the request
|
|
305
|
-
|
|
306
|
-
Example from `pages/profile/[username].vue`:
|
|
325
|
+
Пример с параметрами:
|
|
307
326
|
|
|
308
327
|
```ts
|
|
309
|
-
const
|
|
310
|
-
effect(data) {
|
|
311
|
-
if (data.status === 'error' && data.kind === 'not_found') {
|
|
312
|
-
showError(createError({
|
|
313
|
-
fatal: true,
|
|
314
|
-
statusCode: 404,
|
|
315
|
-
statusMessage: 'Page Not Found',
|
|
316
|
-
}))
|
|
317
|
-
}
|
|
318
|
-
},
|
|
328
|
+
const productsHttp = await app.$di.product.products({
|
|
319
329
|
initParams: {
|
|
320
|
-
|
|
330
|
+
limit: 10,
|
|
321
331
|
},
|
|
322
|
-
|
|
332
|
+
lazy: true,
|
|
323
333
|
})
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## 9. `data` and `error`
|
|
327
334
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
- `data`: success payload only
|
|
331
|
-
- `error`: configured project error payload only
|
|
332
|
-
|
|
333
|
-
Internally the split is done by runtime error predicate:
|
|
334
|
-
|
|
335
|
-
- global `isError` from `createUseHttp(...)`
|
|
336
|
-
- or per-request `isError`
|
|
337
|
-
- fallback: `payload.status === 'error'`
|
|
338
|
-
|
|
339
|
-
Important:
|
|
340
|
-
|
|
341
|
-
- runtime error predicate does not reconfigure TypeScript types
|
|
342
|
-
- TypeScript error type comes from `declare module '@brickflow/http'`
|
|
343
|
-
|
|
344
|
-
## 10. Supported request options
|
|
345
|
-
|
|
346
|
-
For `useHttp` / `createGet`:
|
|
347
|
-
|
|
348
|
-
- `url`
|
|
349
|
-
- `initParams`
|
|
350
|
-
- `mapParams`
|
|
351
|
-
- `effect`
|
|
352
|
-
- `ignore`
|
|
353
|
-
- `isError`
|
|
354
|
-
- `lazy`
|
|
355
|
-
- `server`
|
|
356
|
-
|
|
357
|
-
Example with `initParams`:
|
|
358
|
-
|
|
359
|
-
```ts
|
|
360
|
-
const myContentHttp = await app.$di.content.my({
|
|
361
|
-
initParams: {
|
|
362
|
-
limit: props.limit,
|
|
363
|
-
offset: 0,
|
|
364
|
-
order: selectedOrder.value,
|
|
365
|
-
orderColumn: 'date',
|
|
366
|
-
type: selectedType.value,
|
|
367
|
-
},
|
|
335
|
+
await productsHttp.fetch({
|
|
336
|
+
limit: 30,
|
|
368
337
|
})
|
|
369
338
|
```
|
|
370
339
|
|
|
371
|
-
|
|
340
|
+
## Cache и синхронизация между вкладками
|
|
372
341
|
|
|
373
|
-
|
|
374
|
-
await myContentHttp.fetch({
|
|
375
|
-
limit: props.limit,
|
|
376
|
-
offset,
|
|
377
|
-
order: selectedOrder.value,
|
|
378
|
-
orderColumn: 'date',
|
|
379
|
-
type: selectedType.value,
|
|
380
|
-
})
|
|
381
|
-
```
|
|
342
|
+
Если передан `getCache`, `createUseHttp`:
|
|
382
343
|
|
|
383
|
-
|
|
344
|
+
- сначала пробует отдать cache
|
|
345
|
+
- вызывает `effect(..., { cached: true })` для cache-ответа
|
|
346
|
+
- затем делает сетевой запрос
|
|
347
|
+
- сохраняет успешный ответ в cache
|
|
348
|
+
- при изменении хеша удаляет связанные cache-ключи через `deleteKeysWithPart`
|
|
384
349
|
|
|
385
|
-
|
|
350
|
+
Дополнительно пакет использует `BroadcastChannel` и синхронизирует свежие успешные GET-ответы между вкладками.
|
|
386
351
|
|
|
387
|
-
|
|
388
|
-
- calls `effect(..., { cached: true })` for cached payload
|
|
389
|
-
- then performs fresh request
|
|
390
|
-
- stores success response in cache
|
|
391
|
-
- invalidates related keys when response hash changes
|
|
352
|
+
По умолчанию:
|
|
392
353
|
|
|
393
|
-
|
|
354
|
+
- `ttl = 7 дней`
|
|
355
|
+
- `channelName = 'http-tab-sync'`
|
|
394
356
|
|
|
395
|
-
##
|
|
357
|
+
## SSR
|
|
396
358
|
|
|
397
|
-
|
|
359
|
+
Если вызвать endpoint с `server: true`, на сервере пакет использует `useLazyAsyncData`, сохраняет результат в `useState` и переиспользует его на клиенте после гидрации.
|
|
360
|
+
|
|
361
|
+
Это полезно для страниц, где нужен первый ответ уже в SSR, но тот же контракт `data / pending / fetch` должен остаться и на клиенте.
|
|
362
|
+
|
|
363
|
+
## Вспомогательные функции
|
|
398
364
|
|
|
399
365
|
```ts
|
|
400
366
|
import { createURL, hashData } from '@brickflow/http'
|
|
401
367
|
```
|
|
402
368
|
|
|
403
|
-
`createURL(url, params)`
|
|
404
|
-
|
|
405
|
-
`hashData(data)` computes response hash for cache invalidation.
|
|
369
|
+
- `createURL(url, params)` строит query string
|
|
370
|
+
- `hashData(data)` считает SHA-1 через `crypto.subtle`, а без него использует fallback-хеш
|
|
406
371
|
|
|
407
|
-
##
|
|
372
|
+
## Когда что использовать
|
|
408
373
|
|
|
409
|
-
|
|
374
|
+
`createHttp`:
|
|
410
375
|
|
|
411
|
-
-
|
|
412
|
-
-
|
|
376
|
+
- POST/мутации
|
|
377
|
+
- one-shot запросы
|
|
378
|
+
- когда не нужен reactive state
|
|
413
379
|
|
|
414
|
-
|
|
380
|
+
`createUseHttp` + `defineGet`:
|
|
415
381
|
|
|
416
|
-
-
|
|
417
|
-
-
|
|
418
|
-
-
|
|
419
|
-
-
|
|
382
|
+
- SSR-friendly GET
|
|
383
|
+
- cache и `pendingCache`
|
|
384
|
+
- повторное использование endpoint-описаний
|
|
385
|
+
- ручной `fetch()`
|
|
420
386
|
|
|
421
|
-
|
|
387
|
+
`createUseCase`:
|
|
422
388
|
|
|
423
|
-
-
|
|
424
|
-
-
|
|
425
|
-
- you want all consumers to infer the same API schema
|
|
389
|
+
- доменные модули с DI
|
|
390
|
+
- единый контракт для доступа к `httpClient`
|