@brickflow/http 0.0.13 → 0.0.14

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 ADDED
@@ -0,0 +1,406 @@
1
+ # `@brickflow/http`
2
+
3
+ Nuxt HTTP module and typed client with:
4
+
5
+ - `NuxtApp.$http`
6
+ - `useHttp()` auto-import
7
+ - request/response middleware
8
+ - retry and timeout support
9
+ - optional client cache via IndexedDB
10
+ - typed URL literals for `get`, `post`, and `useHttp`
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @brickflow/http
16
+ ```
17
+
18
+ ## Nuxt Setup
19
+
20
+ ```ts
21
+ export default defineNuxtConfig({
22
+ modules: ['@brickflow/http'],
23
+ })
24
+ ```
25
+
26
+ With options:
27
+
28
+ ```ts
29
+ export default defineNuxtConfig({
30
+ modules: ['@brickflow/http'],
31
+ brickflowHttp: {
32
+ baseURL: 'https://api.example.com',
33
+ cache: true,
34
+ cacheDbName: 'smart-cache-v2',
35
+ cacheStoreName: 'data',
36
+ cacheTtlMs: 1000 * 60 * 60 * 24 * 7,
37
+ clientEnvHeader: true,
38
+ defaultHeaders: {
39
+ 'X-App-Version': '1.0.0',
40
+ },
41
+ disableCacheInDev: true,
42
+ requestTimeoutMs: 80000,
43
+ retry: {
44
+ delay: 300,
45
+ retries: 3,
46
+ },
47
+ },
48
+ })
49
+ ```
50
+
51
+ ## Defaults
52
+
53
+ `brickflowHttp` defaults:
54
+
55
+ | Option | Default |
56
+ | ------------------- | ------------------ |
57
+ | `baseURL` | `''` |
58
+ | `cache` | `true` |
59
+ | `cacheDbName` | `'smart-cache-v2'` |
60
+ | `cacheStoreName` | `'data'` |
61
+ | `cacheTtlMs` | `604800000` |
62
+ | `clientEnvHeader` | `true` |
63
+ | `defaultHeaders` | `{}` |
64
+ | `disableCacheInDev` | `true` |
65
+ | `requestTimeoutMs` | `80000` |
66
+ | `retry.delay` | `300` |
67
+ | `retry.retries` | `3` |
68
+
69
+ Runtime behavior:
70
+
71
+ - `X-Requested-With: XMLHttpRequest` is added automatically
72
+ - in dev, `Client-Env: development` is added when `clientEnvHeader` is enabled
73
+ - `5xx` and `451` responses with `{ status: 'error' }` trigger Nuxt `showError()`
74
+
75
+ ## Basic Usage
76
+
77
+ Use `$http` in components, composables, or plugins:
78
+
79
+ ```ts
80
+ const { $http } = useNuxtApp()
81
+
82
+ const response = await $http.get<{ id: string; name: string }>('/user', {
83
+ params: {
84
+ id: '42',
85
+ },
86
+ })
87
+
88
+ if ('status' in response.data && response.data.status === 'error') {
89
+ console.error(response.data.message)
90
+ } else {
91
+ console.log(response.data.name)
92
+ }
93
+ ```
94
+
95
+ POST:
96
+
97
+ ```ts
98
+ const { $http } = useNuxtApp()
99
+
100
+ const response = await $http.post<{ ok: true }>(
101
+ '/posts',
102
+ {
103
+ title: 'Hello',
104
+ },
105
+ {
106
+ params: {
107
+ draft: true,
108
+ },
109
+ },
110
+ )
111
+ ```
112
+
113
+ ## `useHttp()`
114
+
115
+ `useHttp()` is auto-imported and returns:
116
+
117
+ - `data`
118
+ - `error`
119
+ - `pending`
120
+ - `pendingCache`
121
+ - `hasFirstData`
122
+ - `hasFreshData`
123
+ - `fetch()`
124
+
125
+ Basic example:
126
+
127
+ ```ts
128
+ const users = await useHttp<Array<{ id: string; name: string }>>({
129
+ server: true,
130
+ url: '/users',
131
+ })
132
+ ```
133
+
134
+ With params:
135
+
136
+ ```ts
137
+ const users = await useHttp<Array<{ id: string; name: string }>, { page: number }>({
138
+ initParams: {
139
+ page: 1,
140
+ },
141
+ url: '/users',
142
+ })
143
+
144
+ await users.fetch({
145
+ page: 2,
146
+ })
147
+ ```
148
+
149
+ With side effects:
150
+
151
+ ```ts
152
+ const profile = await useHttp<{ id: string; name: string }>({
153
+ effect(payload, { cached }) {
154
+ if (!cached) {
155
+ console.log('fresh profile payload', payload)
156
+ }
157
+ },
158
+ url: '/profile',
159
+ })
160
+ ```
161
+
162
+ ## Typed Routes
163
+
164
+ If you want `'/users'` and other URL literals to infer `params`, `data`, `error`, and `body` automatically, extend the global `BrickflowHttpRouteMap`.
165
+
166
+ Create a declaration file, for example `types/brickflow-http.d.ts`:
167
+
168
+ ```ts
169
+ import type { HttpErrorPayload } from '@brickflow/http'
170
+
171
+ declare global {
172
+ interface BrickflowHttpRouteMap {
173
+ '/api/user': {
174
+ data: { id: string; name: string }
175
+ error: HttpErrorPayload & { code?: 'NOT_FOUND' }
176
+ params: { id: string }
177
+ }
178
+ '/api/posts': {
179
+ data: Array<{ id: string; title: string }>
180
+ params: { page?: number }
181
+ }
182
+ '/api/posts/create': {
183
+ body: { title: string }
184
+ data: { id: string }
185
+ }
186
+ }
187
+ }
188
+
189
+ export {}
190
+ ```
191
+
192
+ After that, `$http` and `useHttp()` infer types from the URL literal automatically.
193
+
194
+ Typed `get`:
195
+
196
+ ```ts
197
+ const { $http } = useNuxtApp()
198
+
199
+ const response = await $http.get('/api/user', {
200
+ params: {
201
+ id: '42',
202
+ },
203
+ })
204
+ ```
205
+
206
+ Typed `post`:
207
+
208
+ ```ts
209
+ const { $http } = useNuxtApp()
210
+
211
+ await $http.post('/api/posts/create', {
212
+ title: 'New post',
213
+ })
214
+ ```
215
+
216
+ Typed `useHttp` without generics:
217
+
218
+ ```ts
219
+ const posts = await useHttp({
220
+ initParams: {
221
+ page: 1,
222
+ },
223
+ url: '/api/posts',
224
+ })
225
+
226
+ await posts.fetch({
227
+ page: 2,
228
+ })
229
+ ```
230
+
231
+ ## Strict Client
232
+
233
+ `$http` keeps a fallback overload for plain `string`, so unknown URLs are still allowed.
234
+
235
+ If you want to forbid unknown URLs completely:
236
+
237
+ ```ts
238
+ import { createStrictHttpClient } from '@brickflow/http'
239
+
240
+ const { $http } = useNuxtApp()
241
+ const strictHttp = createStrictHttpClient($http)
242
+
243
+ await strictHttp.get('/api/user', {
244
+ params: { id: '42' },
245
+ })
246
+
247
+ // TypeScript error
248
+ await strictHttp.get('/api/unknown')
249
+ ```
250
+
251
+ If you want typed overloads on a standalone client while keeping the plain `string` fallback:
252
+
253
+ ```ts
254
+ import { createHttpClient, createTypedHttpClient } from '@brickflow/http'
255
+
256
+ const http = createTypedHttpClient(
257
+ createHttpClient({
258
+ baseURL: 'https://api.example.com',
259
+ }),
260
+ )
261
+ ```
262
+
263
+ ## Dynamic Base URL
264
+
265
+ Standalone client supports a dynamic resolver:
266
+
267
+ ```ts
268
+ const tenantStore = useTenantStore()
269
+
270
+ const http = createHttpClient({
271
+ baseURL: () => tenantStore.apiBaseUrl,
272
+ })
273
+ ```
274
+
275
+ In Nuxt, the better option is request middleware, because it works for both `$http` and `useHttp()`:
276
+
277
+ ```ts
278
+ import { addHttpRequestMiddleware } from '@brickflow/http'
279
+
280
+ export default defineNuxtPlugin(() => {
281
+ const apiBaseUrl = useState('api-base-url', () => 'https://dummyjson.com')
282
+
283
+ addHttpRequestMiddleware((request) => {
284
+ if (request.url.startsWith('/products') || request.url.startsWith('/test')) {
285
+ request.baseURL = apiBaseUrl.value
286
+ }
287
+ })
288
+ })
289
+ ```
290
+
291
+ Then the same request code can stay relative:
292
+
293
+ ```ts
294
+ const products = await useHttp({
295
+ url: '/products',
296
+ })
297
+
298
+ const { $http } = useNuxtApp()
299
+ await $http.get('/test')
300
+ ```
301
+
302
+ The playground app contains a live example that switches the same typed requests between:
303
+
304
+ - direct `https://dummyjson.com`
305
+ - local proxy `/api/dummyjson`
306
+
307
+ See:
308
+
309
+ - [apps/playground/app/pages/index.vue](/Users/andrii/Lab/personal/brickme/apps/playground/app/pages/index.vue:1)
310
+ - [apps/playground/app/plugins/http-middleware.ts](/Users/andrii/Lab/personal/brickme/apps/playground/app/plugins/http-middleware.ts:1)
311
+
312
+ ## Global Middleware
313
+
314
+ Register middleware once and it will run for every request or response:
315
+
316
+ ```ts
317
+ import { addHttpRequestMiddleware, addHttpResponseMiddleware } from '@brickflow/http'
318
+
319
+ export default defineNuxtPlugin(() => {
320
+ addHttpRequestMiddleware((request) => {
321
+ request.headers.set('X-App-Version', '1.0.0')
322
+ })
323
+
324
+ addHttpResponseMiddleware((response, request) => {
325
+ if (response.status >= 500) {
326
+ console.error('HTTP error', request.url, response.status)
327
+ }
328
+ })
329
+ })
330
+ ```
331
+
332
+ Request middleware can mutate:
333
+
334
+ - `baseURL`
335
+ - `url`
336
+ - `method`
337
+ - `headers`
338
+ - `body`
339
+ - `params`
340
+ - `signal`
341
+ - `credentials`
342
+
343
+ Response middleware receives:
344
+
345
+ - `response`
346
+ - `request`
347
+
348
+ You can remove middleware later:
349
+
350
+ ```ts
351
+ import { addHttpRequestMiddleware, removeHttpRequestMiddleware } from '@brickflow/http'
352
+
353
+ const middleware = (request: Parameters<typeof addHttpRequestMiddleware>[0]) => {
354
+ request.headers.set('X-Debug', '1')
355
+ }
356
+
357
+ addHttpRequestMiddleware(middleware)
358
+ removeHttpRequestMiddleware(middleware)
359
+ ```
360
+
361
+ ## Standalone Client
362
+
363
+ You can use the client outside Nuxt injection:
364
+
365
+ ```ts
366
+ import { createHttpClient } from '@brickflow/http'
367
+
368
+ const http = createHttpClient({
369
+ baseURL: 'https://api.example.com',
370
+ requestTimeoutMs: 10000,
371
+ retry: {
372
+ delay: 250,
373
+ retries: 2,
374
+ },
375
+ })
376
+
377
+ const response = await http.get<{ ok: true }>('/health')
378
+ ```
379
+
380
+ With custom headers:
381
+
382
+ ```ts
383
+ const http = createHttpClient({
384
+ baseURL: async () => `https://${tenant.value}.api.example.com`,
385
+ createHeaders: async () => {
386
+ return {
387
+ Authorization: `Bearer ${token}`,
388
+ }
389
+ },
390
+ })
391
+ ```
392
+
393
+ ## Exports
394
+
395
+ Main exports:
396
+
397
+ - default Nuxt module
398
+ - `createHttpClient`
399
+ - `createTypedHttpClient`
400
+ - `createStrictHttpClient`
401
+ - `defineHttpRoutes`
402
+ - `addHttpRequestMiddleware`
403
+ - `addHttpResponseMiddleware`
404
+ - `removeHttpRequestMiddleware`
405
+ - `removeHttpResponseMiddleware`
406
+ - public TypeScript types for client, config, middleware, payloads, and typed route maps
@@ -0,0 +1,73 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { HttpRetryConfig } from '../dist/runtime/utils/shared.js';
3
+ export { CreateHttpClientOptions, GetConfig, HttpBaseURL, HttpBaseURLResolver, HttpClient, HttpConfig, HttpErrorPayload, HttpParam, HttpPayload, HttpRequestContext, HttpRequestMiddleware, HttpResponse, HttpResponseMiddleware, HttpRetryConfig, HttpRuntimeConfig, PostConfig } from '../dist/runtime/utils/shared.js';
4
+ export { createHttpClient } from '../dist/runtime/http/client.js';
5
+ export { addHttpRequestMiddleware, addHttpResponseMiddleware, removeHttpRequestMiddleware, removeHttpResponseMiddleware } from '../dist/runtime/utils/middleware.js';
6
+ export { HttpRouteBody, HttpRouteData, HttpRouteDefinition, HttpRouteError, HttpRouteMap, HttpRouteParams, ResolveHttpRoute, StrictTypedHttpClient, TypedGetConfig, TypedHttpClient, TypedHttpResponse, TypedPostConfig, createStrictHttpClient, createTypedHttpClient, defineHttpRoutes } from '../dist/runtime/utils/typed.js';
7
+
8
+ interface ModuleOptions {
9
+ /**
10
+ * Base URL used by the injected Nuxt HTTP client.
11
+ *
12
+ * @default ''
13
+ */
14
+ baseURL?: string;
15
+ /**
16
+ * Enables client-side IndexedDB caching in `useHttp()`.
17
+ *
18
+ * @default true
19
+ */
20
+ cache?: boolean;
21
+ /**
22
+ * IndexedDB database name used for cached responses.
23
+ *
24
+ * @default 'smart-cache-v2'
25
+ */
26
+ cacheDbName?: 'smart-cache-v2';
27
+ /**
28
+ * IndexedDB store name used for cached responses.
29
+ *
30
+ * @default 'data'
31
+ */
32
+ cacheStoreName?: string;
33
+ /**
34
+ * Cache TTL in milliseconds.
35
+ *
36
+ * @default 604800000
37
+ */
38
+ cacheTtlMs?: number;
39
+ /**
40
+ * Adds `Client-Env: development` in dev mode.
41
+ *
42
+ * @default true
43
+ */
44
+ clientEnvHeader?: boolean;
45
+ /**
46
+ * Default headers merged into every request.
47
+ *
48
+ * @default {}
49
+ */
50
+ defaultHeaders?: Record<string, string>;
51
+ /**
52
+ * Disables IndexedDB cache when `import.meta.dev` is enabled.
53
+ *
54
+ * @default true
55
+ */
56
+ disableCacheInDev?: boolean;
57
+ /**
58
+ * Request timeout in milliseconds.
59
+ *
60
+ * @default 80000
61
+ */
62
+ requestTimeoutMs?: number;
63
+ /**
64
+ * Retry policy for GET requests on retryable responses and network failures.
65
+ *
66
+ * @default { delay: 300, retries: 3 }
67
+ */
68
+ retry?: HttpRetryConfig;
69
+ }
70
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
71
+
72
+ export { _default as default };
73
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "compatibility": {
3
+ "nuxt": ">=4.0.0"
4
+ },
5
+ "configKey": "brickflowHttp",
6
+ "name": "@brickflow/http",
7
+ "version": "0.0.14",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "3.6.1"
11
+ }
12
+ }
@@ -0,0 +1,59 @@
1
+ import { defineNuxtModule, createResolver, addPlugin, addImportsDir } from '@nuxt/kit';
2
+ export { createHttpClient } from '../dist/runtime/http/client.js';
3
+ export { addHttpRequestMiddleware, addHttpResponseMiddleware, removeHttpRequestMiddleware, removeHttpResponseMiddleware } from '../dist/runtime/utils/middleware.js';
4
+ export { createStrictHttpClient, createTypedHttpClient, defineHttpRoutes } from '../dist/runtime/utils/typed.js';
5
+
6
+ const DAY = 1e3 * 60 * 60 * 24;
7
+ const defaultRuntimeConfig = {
8
+ baseURL: "",
9
+ cache: true,
10
+ cacheDbName: "smart-cache-v2",
11
+ cacheStoreName: "data",
12
+ cacheTtlMs: DAY * 7,
13
+ clientEnvHeader: true,
14
+ defaultHeaders: {},
15
+ disableCacheInDev: true,
16
+ requestTimeoutMs: 8e4,
17
+ retry: {
18
+ delay: 300,
19
+ retries: 3
20
+ }
21
+ };
22
+ const module$1 = defineNuxtModule({
23
+ defaults: defaultRuntimeConfig,
24
+ meta: {
25
+ compatibility: {
26
+ nuxt: ">=4.0.0"
27
+ },
28
+ configKey: "brickflowHttp",
29
+ name: "@brickflow/http"
30
+ },
31
+ setup(options, nuxt) {
32
+ const resolver = createResolver(import.meta.url);
33
+ const currentConfig = nuxt.options.runtimeConfig.public.brickflowHttp ?? {};
34
+ nuxt.options.runtimeConfig.public.brickflowHttp = {
35
+ ...defaultRuntimeConfig,
36
+ ...currentConfig,
37
+ ...options,
38
+ defaultHeaders: {
39
+ ...defaultRuntimeConfig.defaultHeaders,
40
+ ...currentConfig.defaultHeaders ?? {},
41
+ ...options.defaultHeaders ?? {}
42
+ },
43
+ retry: {
44
+ ...defaultRuntimeConfig.retry,
45
+ ...currentConfig.retry ?? {},
46
+ ...options.retry ?? {}
47
+ }
48
+ };
49
+ addPlugin(resolver.resolve("./runtime/plugin"));
50
+ addImportsDir(resolver.resolve("./runtime/composables"));
51
+ nuxt.hook("prepare:types", ({ references }) => {
52
+ references.push({
53
+ path: resolver.resolve("./runtime/types.d.ts")
54
+ });
55
+ });
56
+ }
57
+ });
58
+
59
+ export { module$1 as default };
@@ -0,0 +1,27 @@
1
+ import { type ShallowReactive } from 'vue';
2
+ import type { HttpRouteData, HttpRouteError, HttpRouteMap, HttpRouteParams, ResolveHttpRoute } from '../utils/typed.js';
3
+ import { type HttpErrorPayload, type HttpParam, type HttpPayload } from '../utils/shared.js';
4
+ type UseHttpState<TData, TError extends HttpErrorPayload, TParams extends HttpParam> = ShallowReactive<{
5
+ data: null | TData;
6
+ error: null | TError;
7
+ fetch: (params?: TParams, opt?: {
8
+ signal: AbortSignal;
9
+ }) => Promise<void>;
10
+ hasFirstData: boolean;
11
+ hasFreshData: boolean;
12
+ pending: boolean;
13
+ pendingCache: boolean;
14
+ }>;
15
+ export declare function useHttp<TUrl extends Extract<keyof HttpRouteMap, string>>(options: {
16
+ effect?: (payload: HttpPayload<HttpRouteData<ResolveHttpRoute<HttpRouteMap, TUrl>>, HttpRouteError<ResolveHttpRoute<HttpRouteMap, TUrl>>>, config: {
17
+ cached: boolean;
18
+ params: HttpRouteParams<ResolveHttpRoute<HttpRouteMap, TUrl>>;
19
+ }) => undefined | void;
20
+ initParams?: HttpRouteParams<ResolveHttpRoute<HttpRouteMap, TUrl>>;
21
+ lazy?: true;
22
+ mapParams?: <TMapped extends HttpRouteParams<ResolveHttpRoute<HttpRouteMap, TUrl>>>(params?: TMapped) => TMapped;
23
+ server?: boolean;
24
+ url: TUrl;
25
+ }): Promise<UseHttpState<HttpRouteData<ResolveHttpRoute<HttpRouteMap, TUrl>>, HttpRouteError<ResolveHttpRoute<HttpRouteMap, TUrl>>, HttpRouteParams<ResolveHttpRoute<HttpRouteMap, TUrl>>>>;
26
+ export {};
27
+ //# sourceMappingURL=useHttp.d.ts.map