@brickflow/http 0.0.14 → 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.
Files changed (46) hide show
  1. package/README.md +272 -288
  2. package/dist/create-get.d.ts +12 -0
  3. package/dist/create-get.d.ts.map +1 -0
  4. package/dist/http.d.ts +46 -0
  5. package/dist/http.d.ts.map +1 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.mjs +171 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/nuxt-DAmJOX58.js +233 -0
  11. package/dist/nuxt-DAmJOX58.js.map +1 -0
  12. package/dist/nuxt.d.ts +55 -0
  13. package/dist/nuxt.d.ts.map +1 -0
  14. package/dist/nuxt.mjs +2 -0
  15. package/dist/utils.d.ts +5 -0
  16. package/dist/utils.d.ts.map +1 -0
  17. package/package.json +30 -18
  18. package/src/app.d.ts +11 -0
  19. package/src/create-get.ts +43 -0
  20. package/src/http.ts +286 -0
  21. package/src/index.ts +3 -0
  22. package/src/nuxt.ts +355 -0
  23. package/src/utils.ts +50 -0
  24. package/dist/module.d.mts +0 -73
  25. package/dist/module.json +0 -12
  26. package/dist/module.mjs +0 -59
  27. package/dist/runtime/composables/useHttp.d.ts +0 -27
  28. package/dist/runtime/composables/useHttp.js +0 -197
  29. package/dist/runtime/http/client.d.ts +0 -3
  30. package/dist/runtime/http/client.js +0 -217
  31. package/dist/runtime/plugin.d.ts +0 -7
  32. package/dist/runtime/plugin.js +0 -56
  33. package/dist/runtime/types.d.ts +0 -21
  34. package/dist/runtime/utils/helpers.d.ts +0 -5
  35. package/dist/runtime/utils/helpers.js +0 -14
  36. package/dist/runtime/utils/index.d.ts +0 -6
  37. package/dist/runtime/utils/index.js +0 -5
  38. package/dist/runtime/utils/indexeddb.d.ts +0 -14
  39. package/dist/runtime/utils/indexeddb.js +0 -222
  40. package/dist/runtime/utils/middleware.d.ts +0 -8
  41. package/dist/runtime/utils/middleware.js +0 -20
  42. package/dist/runtime/utils/shared.d.ts +0 -83
  43. package/dist/runtime/utils/shared.js +0 -50
  44. package/dist/runtime/utils/typed.d.ts +0 -46
  45. package/dist/runtime/utils/typed.js +0 -9
  46. package/dist/types.d.mts +0 -11
package/package.json CHANGED
@@ -1,23 +1,33 @@
1
1
  {
2
2
  "name": "@brickflow/http",
3
- "version": "0.0.14",
4
- "type": "module",
5
- "description": "brickflow HTTP Nuxt module.",
3
+ "version": "0.0.16",
4
+ "description": "Minimal HTTP transport and Nuxt state helpers for brickflow.",
5
+ "sideEffects": false,
6
6
  "files": [
7
- "dist"
7
+ "dist",
8
+ "src"
8
9
  ],
9
- "main": "./dist/module.mjs",
10
- "types": "./dist/module.d.mts",
10
+ "main": "./dist/index.mjs",
11
+ "module": "./dist/index.mjs",
12
+ "types": "./src/index.ts",
11
13
  "exports": {
12
14
  ".": {
13
- "types": "./dist/module.d.mts",
14
- "import": "./dist/module.mjs"
15
+ "types": "./src/index.ts",
16
+ "development": "./src/index.ts",
17
+ "import": "./dist/index.mjs",
18
+ "default": "./dist/index.mjs"
19
+ },
20
+ "./nuxt": {
21
+ "types": "./src/nuxt.ts",
22
+ "development": "./src/nuxt.ts",
23
+ "import": "./dist/nuxt.mjs",
24
+ "default": "./dist/nuxt.mjs"
15
25
  }
16
26
  },
17
27
  "scripts": {
18
- "build": "NODE_OPTIONS='--conditions=development' nuxt-module-build build",
19
- "typecheck": "NODE_OPTIONS='--conditions=development' vue-tsc --project tsconfig.json --noEmit",
20
- "lint": "pnpm run --if-present typecheck && eslint -v && NODE_ENV=deploy eslint --cache .",
28
+ "build": "rolldown -c build.config.ts && tsc --project tsconfig.build.json",
29
+ "typecheck": "NODE_OPTIONS='--conditions=development' tsc --project tsconfig.json --noEmit",
30
+ "lint": "pnpm typecheck && eslint -v && NODE_ENV=deploy eslint --cache .",
21
31
  "lint:fix": "pnpm exec eslint . --fix",
22
32
  "format": "pnpm exec prettier . --ignore-path ../../.prettierignore --write",
23
33
  "format:check": "pnpm exec prettier . --ignore-path ../../.prettierignore --check"
@@ -25,15 +35,17 @@
25
35
  "publishConfig": {
26
36
  "access": "public"
27
37
  },
38
+ "dependencies": {
39
+ "@nuxt/kit": "4.4.7"
40
+ },
28
41
  "devDependencies": {
29
42
  "@brickflow/lint": "workspace:*",
30
43
  "@brickflow/prettier": "workspace:*",
31
- "@nuxt/kit": "4.4.6",
32
- "@nuxt/module-builder": "1.0.2",
33
- "nuxt": "4.4.6",
34
- "vue": "3.5.35",
35
- "vue-tsc": "3.3.2"
44
+ "rolldown": "1.1.0",
45
+ "vue-tsc": "3.3.4"
36
46
  },
37
- "author": "",
38
- "license": "ISC"
47
+ "peerDependencies": {
48
+ "nuxt": ">=3",
49
+ "vue": ">=3"
50
+ }
39
51
  }
package/src/app.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ declare module '#app' {
2
+ export * from 'nuxt/app'
3
+ }
4
+
5
+ interface ImportMeta {
6
+ browser: boolean
7
+ client: boolean
8
+ dev: boolean
9
+ server: boolean
10
+ test: boolean
11
+ }
@@ -0,0 +1,43 @@
1
+ import type { HttpClient, HttpKey, HttpParam } from './http'
2
+ import type { UseHttpFn, UseHttpOptions, UseHttpResult } from './nuxt'
3
+
4
+ export type CreateGetOptions<T extends HttpKey, P extends HttpParam> = Omit<UseHttpOptions<T, P>, 'url'>
5
+
6
+ export type CreateGetPayload<T extends HttpKey, P extends HttpParam> = Pick<
7
+ UseHttpOptions<T, P>,
8
+ 'effect' | 'isError' | 'mapParams'
9
+ >
10
+
11
+ export type CreateGetResult<T extends HttpKey, P extends HttpParam> = UseHttpResult<T, P>
12
+
13
+ export function createUseCase<D>() {
14
+ return <T>(
15
+ callback: (payload: D & { httpClient: HttpClient }) => T,
16
+ ): ((payload: D & { httpClient: HttpClient }) => T) => callback
17
+ }
18
+
19
+ export function defineGet(useHttp: UseHttpFn) {
20
+ return function withParams<P extends HttpParam>() {
21
+ return function withUrl<T extends HttpKey>(
22
+ url: T,
23
+ payload?: CreateGetPayload<T, P>,
24
+ ): (options?: CreateGetOptions<T, P>) => Promise<CreateGetResult<T, P>> {
25
+ return async function request(options: CreateGetOptions<T, P> = {}): Promise<CreateGetResult<T, P>> {
26
+ const result = await useHttp({
27
+ ...options,
28
+ effect: (...args) => {
29
+ const [data, config] = args
30
+
31
+ payload?.effect?.(data, config)
32
+ options?.effect?.(data, config)
33
+ },
34
+ isError: options.isError ?? payload?.isError,
35
+ mapParams: options.mapParams ?? payload?.mapParams,
36
+ url,
37
+ })
38
+
39
+ return result
40
+ }
41
+ }
42
+ }
43
+ }
package/src/http.ts ADDED
@@ -0,0 +1,286 @@
1
+ export interface CreateHttpOptions {
2
+ arrayMode?: HttpArrayMode
3
+ baseURL: string
4
+ fetch?: typeof fetch
5
+ headers?: (() => HttpHeaders) | HttpHeaders
6
+ onResponse?: (response: HttpAnyResponse) => Promise<void> | void
7
+ timeout?: number
8
+ }
9
+
10
+ export type GetConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey> & {
11
+ retry?: {
12
+ delay?: number
13
+ retries?: number
14
+ }
15
+ }
16
+
17
+ export type HttpAnyResponse = HttpResponse<HttpResponseData<HttpKey>, HttpKey, HttpConfigFull<HttpKey>>
18
+
19
+ export type HttpArrayMode = 'json' | 'repeat'
20
+
21
+ export type HttpClient = {
22
+ get: <T extends HttpKey, TConfig extends GetConfig<T> = GetConfig<T>>(
23
+ url: T,
24
+ config?: TConfig,
25
+ ) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>
26
+ post: <T extends HttpKey, TConfig extends PostConfig<T> = PostConfig<T>>(
27
+ url: T,
28
+ data?: FormData | Record<string, unknown>,
29
+ config?: TConfig,
30
+ ) => Promise<HttpResponse<HttpResponseData<T>, T, TConfig>>
31
+ }
32
+
33
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
34
+ export interface HttpConfig<TKey extends HttpKey = HttpKey> {}
35
+ export interface HttpEndpoint {}
36
+ export type HttpErrorGuard<TData = unknown, TError extends TData = TData> = (payload: TData) => payload is TError
37
+
38
+ export type HttpKey = Extract<keyof HttpSchema, string>
39
+ export type HttpParam = Record<string, boolean | number | string | string[] | undefined>
40
+
41
+ export interface HttpResponse<
42
+ T = unknown,
43
+ TKey extends HttpKey = HttpKey,
44
+ TConfig extends HttpConfigFull<TKey> = HttpConfigFull<TKey>,
45
+ > {
46
+ config: HttpResponseConfig<TKey, TConfig>
47
+ data: T
48
+ status: number
49
+ }
50
+ export type HttpResponseConfig<
51
+ TKey extends HttpKey = HttpKey,
52
+ TConfig extends HttpConfig<TKey> = HttpConfig<TKey>,
53
+ > = Omit<TConfig, 'signal'> & { url: string }
54
+
55
+ export type HttpResponseData<TKey extends HttpKey = HttpKey> = HttpSchema[TKey]
56
+
57
+ export type PostConfig<TKey extends HttpKey = HttpKey> = HttpConfigFull<TKey>
58
+
59
+ type HttpConfigFull<TKey extends HttpKey = HttpKey> = HttpConfig<TKey> & {
60
+ params?: HttpParam
61
+ signal?: AbortSignal | AbortSignal[]
62
+ }
63
+
64
+ type HttpHeaders = Record<string, string | undefined>
65
+
66
+ type HttpSchema = keyof HttpEndpoint extends never ? Record<string, unknown> : HttpEndpoint
67
+
68
+ export function createHttp(options: CreateHttpOptions): HttpClient {
69
+ const arrayMode = options.arrayMode ?? 'json'
70
+ const clientFetch = options.fetch ?? fetch
71
+ const timeout = options.timeout ?? 80000
72
+
73
+ return {
74
+ get<T extends HttpKey, TConfig extends GetConfig<T> = GetConfig<T>>(url: T, config?: TConfig) {
75
+ const requestConfig = (config ?? {}) as TConfig
76
+ const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`
77
+ const { delay = 300, retries = 3 } = requestConfig.retry ?? {}
78
+
79
+ const attemptRequest = async (attempt: number): Promise<HttpResponse<HttpResponseData<T>, T, TConfig>> => {
80
+ try {
81
+ const result = await clientFetch(requestUrl, {
82
+ credentials: 'include',
83
+ headers: resolveHeaders(options.headers),
84
+ method: 'GET',
85
+ signal: createSignal(requestConfig.signal, timeout),
86
+ })
87
+
88
+ const parsedResult = await result.json()
89
+ const response: HttpResponse<HttpResponseData<T>, T, TConfig> = {
90
+ config: createResponseConfig<T, TConfig>(joinUrl(options.baseURL, url), requestConfig),
91
+ data: parsedResult,
92
+ status: result.status,
93
+ }
94
+
95
+ if (!result.ok && isRetryableStatus(result.status) && attempt < retries) {
96
+ await wait(getRetryDelay(attempt, delay))
97
+ return attemptRequest(attempt + 1)
98
+ }
99
+
100
+ await options.onResponse?.(response as HttpAnyResponse)
101
+ return response
102
+ } catch (err) {
103
+ if (isAbortError(err) || attempt >= retries) {
104
+ throw err
105
+ }
106
+
107
+ await wait(getRetryDelay(attempt, delay))
108
+ return attemptRequest(attempt + 1)
109
+ }
110
+ }
111
+
112
+ return attemptRequest(0)
113
+ },
114
+ async post<T extends HttpKey, TConfig extends PostConfig<T> = PostConfig<T>>(
115
+ url: T,
116
+ data?: FormData | Record<string, unknown>,
117
+ config?: TConfig,
118
+ ) {
119
+ const requestConfig = (config ?? {}) as TConfig
120
+ const isForm = isFormData(data)
121
+ const requestUrl = `${joinUrl(options.baseURL, url)}${toQueryString(requestConfig.params, arrayMode)}`
122
+ const response = await clientFetch(requestUrl, {
123
+ body: isForm || data === undefined ? data : JSON.stringify(data),
124
+ credentials: 'include',
125
+ headers: resolveHeaders(
126
+ options.headers,
127
+ data !== undefined && !isForm ? { 'Content-Type': 'application/json' } : undefined,
128
+ ),
129
+ method: 'POST',
130
+ signal: createSignal(requestConfig.signal, timeout),
131
+ })
132
+
133
+ let parsedResult: Awaited<ReturnType<typeof response.json>> = {}
134
+ try {
135
+ parsedResult = await response.json()
136
+ } catch {
137
+ parsedResult = {}
138
+ }
139
+
140
+ const parsedResponse: HttpResponse<HttpResponseData<T>, T, TConfig> = {
141
+ config: createResponseConfig<T, TConfig>(joinUrl(options.baseURL, url), requestConfig),
142
+ data: parsedResult,
143
+ status: response.status,
144
+ }
145
+
146
+ await options.onResponse?.(parsedResponse as HttpAnyResponse)
147
+ return parsedResponse
148
+ },
149
+ }
150
+ }
151
+
152
+ function createAnySignal(signals: AbortSignal[]): AbortSignal {
153
+ if (typeof AbortSignal?.any === 'function') {
154
+ return AbortSignal.any(signals)
155
+ }
156
+
157
+ const controller = new AbortController()
158
+
159
+ signals.filter(Boolean).forEach((signal) => {
160
+ signal.addEventListener('abort', () => controller.abort(), { once: true })
161
+ })
162
+
163
+ return controller.signal
164
+ }
165
+
166
+ function createResponseConfig<TKey extends HttpKey, TConfig extends HttpConfigFull<TKey>>(
167
+ url: string,
168
+ config: TConfig,
169
+ ): HttpResponseConfig<TKey, TConfig> {
170
+ const responseConfig = { ...config } as Record<string, unknown>
171
+ delete responseConfig.signal
172
+
173
+ return {
174
+ ...responseConfig,
175
+ url,
176
+ } as HttpResponseConfig<TKey, TConfig>
177
+ }
178
+
179
+ function createSignal(signal: AbortSignal | AbortSignal[] | undefined, timeout: number): AbortSignal {
180
+ const timeoutSignal = createTimeoutSignal(timeout)
181
+
182
+ if (Array.isArray(signal)) {
183
+ return createAnySignal([...signal, timeoutSignal])
184
+ }
185
+
186
+ if (signal) {
187
+ return createAnySignal([signal, timeoutSignal])
188
+ }
189
+
190
+ return timeoutSignal
191
+ }
192
+
193
+ function createTimeoutSignal(ms: number): AbortSignal {
194
+ if (typeof AbortSignal?.timeout === 'function') {
195
+ return AbortSignal.timeout(ms)
196
+ }
197
+
198
+ const controller = new AbortController()
199
+ const timeoutId = setTimeout(() => controller.abort(), ms)
200
+
201
+ controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), {
202
+ once: true,
203
+ })
204
+
205
+ return controller.signal
206
+ }
207
+
208
+ function getRetryDelay(attempt: number, delay: number): number {
209
+ return delay * 2 ** attempt
210
+ }
211
+
212
+ function isAbortError(err: unknown): boolean {
213
+ return err instanceof DOMException && err.name === 'AbortError'
214
+ }
215
+
216
+ function isFormData(value: FormData | Record<string, unknown> | undefined): value is FormData {
217
+ return typeof FormData !== 'undefined' && value instanceof FormData
218
+ }
219
+
220
+ function isRetryableStatus(status: number): boolean {
221
+ return status >= 500 || status === 429
222
+ }
223
+
224
+ function joinUrl(baseURL: string, url: string): string {
225
+ const normalizedBase = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL
226
+ const normalizedUrl = url.startsWith('/') ? url.slice(1) : url
227
+
228
+ return `${normalizedBase}/${normalizedUrl}`
229
+ }
230
+
231
+ function resolveHeaders(
232
+ value: CreateHttpOptions['headers'],
233
+ extraHeaders?: Record<string, string>,
234
+ ): Record<string, string> {
235
+ const headers = typeof value === 'function' ? value() : value
236
+
237
+ return Object.entries({
238
+ ...headers,
239
+ ...extraHeaders,
240
+ }).reduce<Record<string, string>>((acc, [key, headerValue]) => {
241
+ if (typeof headerValue === 'string') {
242
+ acc[key] = headerValue
243
+ }
244
+
245
+ return acc
246
+ }, {})
247
+ }
248
+
249
+ function serializeQueryValue(
250
+ urlParams: URLSearchParams,
251
+ key: string,
252
+ value: HttpParam[string],
253
+ arrayMode: HttpArrayMode,
254
+ ): void {
255
+ if (value === undefined) {
256
+ return
257
+ }
258
+
259
+ if (Array.isArray(value)) {
260
+ if (arrayMode === 'json') {
261
+ urlParams.append(key, JSON.stringify(value))
262
+ return
263
+ }
264
+
265
+ value.forEach((item) => {
266
+ urlParams.append(`${key}[]`, item)
267
+ })
268
+ return
269
+ }
270
+
271
+ urlParams.append(key, String(value))
272
+ }
273
+
274
+ function toQueryString(params?: HttpParam, arrayMode: HttpArrayMode = 'json'): string {
275
+ const urlParams = new URLSearchParams()
276
+
277
+ Object.entries(params ?? {}).forEach(([key, value]) => {
278
+ serializeQueryValue(urlParams, key, value, arrayMode)
279
+ })
280
+
281
+ return urlParams.size > 0 ? `?${urlParams}` : ''
282
+ }
283
+
284
+ function wait(ms: number): Promise<void> {
285
+ return new Promise((resolve) => setTimeout(resolve, ms))
286
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './create-get'
2
+ export * from './http'
3
+ export * from './nuxt'