@molecule/app-http 1.0.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.
- package/LICENSE +115 -0
- package/dist/client.d.ts +99 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +9 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors.d.ts +27 -0
- package/dist/interceptors.d.ts.map +1 -0
- package/dist/interceptors.js +10 -0
- package/dist/interceptors.js.map +1 -0
- package/dist/provider.d.ts +20 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +298 -0
- package/dist/provider.js.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/unwrap.d.ts +66 -0
- package/dist/unwrap.d.ts.map +1 -0
- package/dist/unwrap.js +221 -0
- package/dist/unwrap.js.map +1 -0
- package/dist/utilities.d.ts +79 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +89 -0
- package/dist/utilities.js.map +1 -0
- package/package.json +52 -0
package/dist/unwrap.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response-envelope unwrappers.
|
|
3
|
+
*
|
|
4
|
+
* Apps and mock servers commonly return either a bare value (`T` or
|
|
5
|
+
* `T[]`) or an envelope `{ data: T }` / `{ data: T[] }`. These helpers
|
|
6
|
+
* normalize either shape into a predictable `T[]` or `T | null` for
|
|
7
|
+
* downstream code, eliminating the per-call `'data' in res ? ...` dance.
|
|
8
|
+
*
|
|
9
|
+
* Lifted out of the 59 apps that previously carried a local
|
|
10
|
+
* `lib/unwrap.ts` copy. The two functions cover the response shapes used
|
|
11
|
+
* by the molecule mock server and the typical resource routes — a bare
|
|
12
|
+
* value, a `{ data }` envelope, and (for lists) the RICH pagination
|
|
13
|
+
* envelope `{ data, total, limit, offset }` returned by the pre-built
|
|
14
|
+
* `@molecule/api-resource-*` list endpoints (comment, review,
|
|
15
|
+
* activity-feed, bookmark, …). Other bespoke envelopes are out of scope
|
|
16
|
+
* — handle those in the caller.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Normalize an unknown response body into a typed array.
|
|
22
|
+
*
|
|
23
|
+
* Accepts:
|
|
24
|
+
* - a bare array → returned as-is (cast)
|
|
25
|
+
* - `{ data: T[] }` envelope → the inner array
|
|
26
|
+
* - `HttpResponse<T[]>` (i.e. `{ data: T[], status, ... }`) → the inner array
|
|
27
|
+
* - `HttpResponse<{ data: T[] }>` (the response of an envelope-returning
|
|
28
|
+
* endpoint as it arrives from `@molecule/app-http`'s `HttpClient`) → the
|
|
29
|
+
* doubly-nested inner array
|
|
30
|
+
* - `HttpResponse<{ data: T[], total, limit, offset }>` (a RICH pagination
|
|
31
|
+
* envelope from a pre-built `@molecule/api-resource-*` list endpoint) → the
|
|
32
|
+
* inner `data` array (the numeric `total`/`limit`/`offset` make the shape
|
|
33
|
+
* unambiguous, so callers can pass the whole HttpResponse and still get the rows)
|
|
34
|
+
* - anything else → `[]`
|
|
35
|
+
*
|
|
36
|
+
* Callers commonly pass either the raw JSON body (e.g. from `fetch().then(r =>
|
|
37
|
+
* r.json())`) or the `HttpResponse` returned by `useHttpClient().get(...)`.
|
|
38
|
+
* Both shapes are handled here so pages don't have to remember to call
|
|
39
|
+
* `unwrapList(res.data)` vs `unwrapList(res)`.
|
|
40
|
+
*
|
|
41
|
+
* @param res - Raw response body OR an `HttpResponse` envelope from `@molecule/app-http`.
|
|
42
|
+
* @returns A typed array `T[]`; never `null`/`undefined`.
|
|
43
|
+
*/
|
|
44
|
+
export function unwrapList(res) {
|
|
45
|
+
if (Array.isArray(res))
|
|
46
|
+
return res;
|
|
47
|
+
if (res && typeof res === 'object' && 'data' in res) {
|
|
48
|
+
const inner = res.data;
|
|
49
|
+
if (Array.isArray(inner))
|
|
50
|
+
return inner;
|
|
51
|
+
// `HttpResponse<{ data: T[] }>` — caller passed the whole response object
|
|
52
|
+
// (typeof `{ data, status, statusText, headers, config }`) and the inner
|
|
53
|
+
// JSON body is itself an envelope `{ data: T[] }`. Peel both layers, but
|
|
54
|
+
// only when the outer shape is unambiguously an `HttpResponse` (i.e. has
|
|
55
|
+
// the `status: number` field), so we don't mis-peel application objects.
|
|
56
|
+
if (isHttpResponseLike(res) &&
|
|
57
|
+
inner &&
|
|
58
|
+
typeof inner === 'object' &&
|
|
59
|
+
// Only a PURE single-key `{ data: T[] }` envelope is a double-wrap to peel.
|
|
60
|
+
// A resource that legitimately carries its own `data` array field
|
|
61
|
+
// (`{ id, …, data: [...] }`) has >1 key — peeling it would return the
|
|
62
|
+
// resource's own array and drop the surrounding fields.
|
|
63
|
+
Object.keys(inner).length === 1 &&
|
|
64
|
+
'data' in inner &&
|
|
65
|
+
Array.isArray(inner.data)) {
|
|
66
|
+
return inner.data;
|
|
67
|
+
}
|
|
68
|
+
// `HttpResponse<PaginatedResult<T>>` — the pre-built `@molecule/api-resource-*`
|
|
69
|
+
// list endpoints return a RICH pagination envelope `{ data: T[], total, limit,
|
|
70
|
+
// offset }`. It has >1 key so the pure-`{ data }` branch above skips it, but the
|
|
71
|
+
// shape is unambiguous: a `data` array alongside numeric `total`, `limit`, AND
|
|
72
|
+
// `offset` is a pagination result, not an application object. Restricted to the
|
|
73
|
+
// HttpResponse case so a plain body is never second-guessed.
|
|
74
|
+
if (isHttpResponseLike(res) && isPaginationEnvelope(inner)) {
|
|
75
|
+
return inner.data;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Metadata keys a list endpoint may carry alongside its `data` array.
|
|
82
|
+
*
|
|
83
|
+
* Covers both conventions in this fleet: `PaginatedResult` from the pre-built
|
|
84
|
+
* `@molecule/api-resource-*` endpoints (`total`/`limit`/`offset`) and the
|
|
85
|
+
* `page`/`limit` form hand-written handlers use.
|
|
86
|
+
*/
|
|
87
|
+
const PAGINATION_KEYS = new Set([
|
|
88
|
+
'total',
|
|
89
|
+
'limit',
|
|
90
|
+
'offset',
|
|
91
|
+
'page',
|
|
92
|
+
'pageSize',
|
|
93
|
+
'per_page',
|
|
94
|
+
'perPage',
|
|
95
|
+
'count',
|
|
96
|
+
// An aggregate OVER the collection, in the same family as `total`/`count` —
|
|
97
|
+
// seven flagships' notification lists answer `{ data, unreadCount, total }`.
|
|
98
|
+
// The criterion for membership here is that the key describes the list rather
|
|
99
|
+
// than belonging to a resource: no notification row carries `unreadCount`, so
|
|
100
|
+
// this stays unambiguous. App-specific aggregates that don't meet that bar
|
|
101
|
+
// belong under `meta`.
|
|
102
|
+
'unreadCount',
|
|
103
|
+
'unread_count',
|
|
104
|
+
'hasMore',
|
|
105
|
+
'has_more',
|
|
106
|
+
'cursor',
|
|
107
|
+
'nextCursor',
|
|
108
|
+
'next_cursor',
|
|
109
|
+
'meta',
|
|
110
|
+
'links',
|
|
111
|
+
]);
|
|
112
|
+
/**
|
|
113
|
+
* Recognise a list envelope: a `data` array plus only pagination metadata.
|
|
114
|
+
*
|
|
115
|
+
* @param v - The inner JSON body of an `HttpResponse`.
|
|
116
|
+
* @returns True when `v` is a paginated list rather than an application object.
|
|
117
|
+
*/
|
|
118
|
+
function isPaginationEnvelope(v) {
|
|
119
|
+
if (!v || typeof v !== 'object')
|
|
120
|
+
return false;
|
|
121
|
+
if (!Array.isArray(v.data))
|
|
122
|
+
return false;
|
|
123
|
+
// Every OTHER key must be recognised pagination metadata. Requiring the exact
|
|
124
|
+
// trio total+limit+offset — as this did — missed `{ data, page, limit }`, the
|
|
125
|
+
// shape hand-written list handlers return, and unwrapList then answered `[]`
|
|
126
|
+
// for a response carrying 20 rows. That is the worst possible failure mode
|
|
127
|
+
// here: silent, and indistinguishable from an empty collection, so a
|
|
128
|
+
// storefront rendered zero products with no error anywhere.
|
|
129
|
+
//
|
|
130
|
+
// Membership is still what keeps this unambiguous. An application object that
|
|
131
|
+
// merely owns a `data` array carries domain keys (`id`, `name`, …), none of
|
|
132
|
+
// which are in this set, so it is never mistaken for an envelope.
|
|
133
|
+
return Object.keys(v).every((k) => k === 'data' || PAGINATION_KEYS.has(k));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Recognise an `HttpResponse` from `@molecule/app-http` without taking a
|
|
137
|
+
* runtime dependency on its types: an object with both `data` and a numeric
|
|
138
|
+
* `status` field. Plain API JSON bodies don't include `status`, so the check
|
|
139
|
+
* is unambiguous.
|
|
140
|
+
*/
|
|
141
|
+
function isHttpResponseLike(res) {
|
|
142
|
+
return (!!res &&
|
|
143
|
+
typeof res === 'object' &&
|
|
144
|
+
'data' in res &&
|
|
145
|
+
'status' in res &&
|
|
146
|
+
typeof res.status === 'number');
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Normalize an unknown response body into a single typed resource.
|
|
150
|
+
*
|
|
151
|
+
* Accepts:
|
|
152
|
+
* - a non-empty plain object → returned as-is (cast)
|
|
153
|
+
* - `{ data: T }` envelope → the inner value
|
|
154
|
+
* - `{ data: null }`, `{ data: undefined }`, `{ data: [] }`, or
|
|
155
|
+
* `{ data: {} }` (mock-server's no-match shape) → `null`
|
|
156
|
+
* - an empty object `{}` → `null`
|
|
157
|
+
* - arrays, primitives, `null`, `undefined` → `null`
|
|
158
|
+
*
|
|
159
|
+
* The "envelope contains an array → null" branch handles the case
|
|
160
|
+
* where the mock server returns `[]` for unmatched endpoints but the
|
|
161
|
+
* caller expects a single resource.
|
|
162
|
+
*
|
|
163
|
+
* @param res - Raw response body (e.g. `HttpResponse.data` from `@molecule/app-http`).
|
|
164
|
+
* @returns The typed resource `T`, or `null` when the response shape
|
|
165
|
+
* indicates "no resource" (including the various empty envelopes
|
|
166
|
+
* above).
|
|
167
|
+
*/
|
|
168
|
+
export function unwrapSingle(res) {
|
|
169
|
+
if (res && typeof res === 'object' && !Array.isArray(res)) {
|
|
170
|
+
// A resource may legitimately OWN a `data` field. grocery-delivery's
|
|
171
|
+
// GET /api/cart answers `{ data: [...], items: [...], subtotal, delivery_fee,
|
|
172
|
+
// … }` — a cart object that happens to carry its rows under `data` as well.
|
|
173
|
+
// Peeling that returned the inner array, which the "caller wants a single
|
|
174
|
+
// resource" branch below then turned into `null`, so the cart page rendered
|
|
175
|
+
// empty while the API held the item and 20 tests failed on the empty page
|
|
176
|
+
// instead of the cause. Only peel when the object is a PURE envelope: `data`
|
|
177
|
+
// plus recognised pagination metadata and nothing else. (isPaginationEnvelope
|
|
178
|
+
// covers the array case; the object case is guarded the same way here.)
|
|
179
|
+
const looksLikeResource = !isHttpResponseLike(res) &&
|
|
180
|
+
Object.keys(res).some((k) => k !== 'data' && !PAGINATION_KEYS.has(k));
|
|
181
|
+
if ('data' in res && !looksLikeResource) {
|
|
182
|
+
const inner = res.data;
|
|
183
|
+
if (inner === null || inner === undefined)
|
|
184
|
+
return null;
|
|
185
|
+
// Caller expects a single resource. If the envelope contains an array
|
|
186
|
+
// (mock-server returns [] for unmatched endpoints), treat as missing.
|
|
187
|
+
if (Array.isArray(inner))
|
|
188
|
+
return null;
|
|
189
|
+
if (typeof inner === 'object') {
|
|
190
|
+
const innerObj = inner;
|
|
191
|
+
// `HttpResponse<{ data: T }>` — caller passed the whole HttpResponse
|
|
192
|
+
// (recognisable by the `status: number` sibling on `res`) and the
|
|
193
|
+
// inner JSON body is itself an envelope `{ data: T }`. Peel the second
|
|
194
|
+
// layer. We restrict this to the HttpResponse case so plain payloads
|
|
195
|
+
// shaped `{ data: { data: ... } }` keep their existing semantics.
|
|
196
|
+
if (isHttpResponseLike(res) &&
|
|
197
|
+
// Only a PURE single-key `{ data: T }` envelope is a double-wrap to
|
|
198
|
+
// peel. A resource that legitimately carries its own `data` object
|
|
199
|
+
// field (`{ id, …, data: {…} }`) has >1 key — peeling it would drop
|
|
200
|
+
// id and the sibling fields, silently corrupting the resource.
|
|
201
|
+
Object.keys(innerObj).length === 1 &&
|
|
202
|
+
'data' in innerObj &&
|
|
203
|
+
innerObj.data !== null &&
|
|
204
|
+
innerObj.data !== undefined &&
|
|
205
|
+
typeof innerObj.data === 'object' &&
|
|
206
|
+
!Array.isArray(innerObj.data) &&
|
|
207
|
+
Object.keys(innerObj.data).length > 0) {
|
|
208
|
+
return innerObj.data;
|
|
209
|
+
}
|
|
210
|
+
if (Object.keys(innerObj).length === 0)
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return inner;
|
|
214
|
+
}
|
|
215
|
+
if (Object.keys(res).length === 0)
|
|
216
|
+
return null;
|
|
217
|
+
return res;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=unwrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unwrap.js","sourceRoot":"","sources":["../src/unwrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP bond accessor and convenience request functions.
|
|
3
|
+
*
|
|
4
|
+
* If no custom client is bonded, a fetch-based client is auto-created
|
|
5
|
+
* on first access. Provides `get`, `post`, `put`, `patch`, and `del`
|
|
6
|
+
* shorthand functions that delegate to the bonded client.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { HttpClient } from './client.js';
|
|
11
|
+
import type { HttpResponse, RequestConfig } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Registers an HTTP client as the active singleton.
|
|
14
|
+
*
|
|
15
|
+
* @param client - The HTTP client implementation to bond.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { setClient, createFetchClient } from '@molecule/app-http'
|
|
20
|
+
* setClient(createFetchClient({ baseURL: 'https://api.example.com' }))
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const setClient: (client: HttpClient) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the bonded HTTP client. If none is bonded, automatically
|
|
26
|
+
* creates a default fetch-based client.
|
|
27
|
+
*
|
|
28
|
+
* @returns The active HTTP client instance.
|
|
29
|
+
*/
|
|
30
|
+
export declare const getClient: () => HttpClient;
|
|
31
|
+
/**
|
|
32
|
+
* Makes a GET request using the bonded HTTP client.
|
|
33
|
+
*
|
|
34
|
+
* @param url - The request URL (relative to baseURL if configured).
|
|
35
|
+
* @param config - Optional request configuration (headers, params, timeout).
|
|
36
|
+
* @returns The HTTP response with typed data.
|
|
37
|
+
*/
|
|
38
|
+
export declare const get: <T = unknown>(url: string, config?: RequestConfig) => Promise<HttpResponse<T>>;
|
|
39
|
+
/**
|
|
40
|
+
* Makes a POST request using the bonded HTTP client.
|
|
41
|
+
*
|
|
42
|
+
* @param url - The request URL.
|
|
43
|
+
* @param data - The request body.
|
|
44
|
+
* @param config - Optional request configuration.
|
|
45
|
+
* @returns The HTTP response with typed data.
|
|
46
|
+
*/
|
|
47
|
+
export declare const post: <T = unknown>(url: string, data?: unknown, config?: RequestConfig) => Promise<HttpResponse<T>>;
|
|
48
|
+
/**
|
|
49
|
+
* Makes a PUT request using the bonded HTTP client.
|
|
50
|
+
*
|
|
51
|
+
* @param url - The request URL.
|
|
52
|
+
* @param data - The request body.
|
|
53
|
+
* @param config - Optional request configuration.
|
|
54
|
+
* @returns The HTTP response with typed data.
|
|
55
|
+
*/
|
|
56
|
+
export declare const put: <T = unknown>(url: string, data?: unknown, config?: RequestConfig) => Promise<HttpResponse<T>>;
|
|
57
|
+
/**
|
|
58
|
+
* Makes a PATCH request using the bonded HTTP client.
|
|
59
|
+
*
|
|
60
|
+
* @param url - The request URL.
|
|
61
|
+
* @param data - The request body (partial update).
|
|
62
|
+
* @param config - Optional request configuration.
|
|
63
|
+
* @returns The HTTP response with typed data.
|
|
64
|
+
*/
|
|
65
|
+
export declare const patch: <T = unknown>(url: string, data?: unknown, config?: RequestConfig) => Promise<HttpResponse<T>>;
|
|
66
|
+
/**
|
|
67
|
+
* Makes a DELETE request using the bonded HTTP client.
|
|
68
|
+
*
|
|
69
|
+
* @param url - The request URL.
|
|
70
|
+
* @param config - Optional request configuration.
|
|
71
|
+
* @returns The HTTP response with typed data.
|
|
72
|
+
*/
|
|
73
|
+
export declare const del: <T = unknown>(url: string, config?: RequestConfig) => Promise<HttpResponse<T>>;
|
|
74
|
+
/**
|
|
75
|
+
* Pre-created fetch client for environments where `fetch` is available.
|
|
76
|
+
* `null` in environments without a global `fetch`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const fetchClient: HttpClient | null;
|
|
79
|
+
//# sourceMappingURL=utilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAI7D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,UAAU,KAAG,IAE9C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,QAAO,UAM5B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,GAAG,GAAI,CAAC,GAAG,OAAO,EAAE,KAAK,MAAM,EAAE,SAAS,aAAa,KAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAC7D,CAAA;AAEjC;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,OAAO,EAC9B,KAAK,MAAM,EACX,OAAO,OAAO,EACd,SAAS,aAAa,KACrB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAA2C,CAAA;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,GAAI,CAAC,GAAG,OAAO,EAC7B,KAAK,MAAM,EACX,OAAO,OAAO,EACd,SAAS,aAAa,KACrB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAA0C,CAAA;AAEpE;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,GAAG,OAAO,EAC/B,KAAK,MAAM,EACX,OAAO,OAAO,EACd,SAAS,aAAa,KACrB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAA4C,CAAA;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,GAAG,GAAI,CAAC,GAAG,OAAO,EAAE,KAAK,MAAM,EAAE,SAAS,aAAa,KAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAC1D,CAAA;AAEpC;;;GAGG;AACH,eAAO,MAAM,WAAW,mBAA4D,CAAA"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP bond accessor and convenience request functions.
|
|
3
|
+
*
|
|
4
|
+
* If no custom client is bonded, a fetch-based client is auto-created
|
|
5
|
+
* on first access. Provides `get`, `post`, `put`, `patch`, and `del`
|
|
6
|
+
* shorthand functions that delegate to the bonded client.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { bond, get as bondGet } from '@molecule/app-bond';
|
|
11
|
+
import { createFetchClient } from './provider.js';
|
|
12
|
+
const BOND_TYPE = 'http-client';
|
|
13
|
+
/**
|
|
14
|
+
* Registers an HTTP client as the active singleton.
|
|
15
|
+
*
|
|
16
|
+
* @param client - The HTTP client implementation to bond.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { setClient, createFetchClient } from '@molecule/app-http'
|
|
21
|
+
* setClient(createFetchClient({ baseURL: 'https://api.example.com' }))
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const setClient = (client) => {
|
|
25
|
+
bond(BOND_TYPE, client);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the bonded HTTP client. If none is bonded, automatically
|
|
29
|
+
* creates a default fetch-based client.
|
|
30
|
+
*
|
|
31
|
+
* @returns The active HTTP client instance.
|
|
32
|
+
*/
|
|
33
|
+
export const getClient = () => {
|
|
34
|
+
const existing = bondGet(BOND_TYPE);
|
|
35
|
+
if (existing)
|
|
36
|
+
return existing;
|
|
37
|
+
const fallback = createFetchClient();
|
|
38
|
+
bond(BOND_TYPE, fallback);
|
|
39
|
+
return fallback;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Makes a GET request using the bonded HTTP client.
|
|
43
|
+
*
|
|
44
|
+
* @param url - The request URL (relative to baseURL if configured).
|
|
45
|
+
* @param config - Optional request configuration (headers, params, timeout).
|
|
46
|
+
* @returns The HTTP response with typed data.
|
|
47
|
+
*/
|
|
48
|
+
export const get = (url, config) => getClient().get(url, config);
|
|
49
|
+
/**
|
|
50
|
+
* Makes a POST request using the bonded HTTP client.
|
|
51
|
+
*
|
|
52
|
+
* @param url - The request URL.
|
|
53
|
+
* @param data - The request body.
|
|
54
|
+
* @param config - Optional request configuration.
|
|
55
|
+
* @returns The HTTP response with typed data.
|
|
56
|
+
*/
|
|
57
|
+
export const post = (url, data, config) => getClient().post(url, data, config);
|
|
58
|
+
/**
|
|
59
|
+
* Makes a PUT request using the bonded HTTP client.
|
|
60
|
+
*
|
|
61
|
+
* @param url - The request URL.
|
|
62
|
+
* @param data - The request body.
|
|
63
|
+
* @param config - Optional request configuration.
|
|
64
|
+
* @returns The HTTP response with typed data.
|
|
65
|
+
*/
|
|
66
|
+
export const put = (url, data, config) => getClient().put(url, data, config);
|
|
67
|
+
/**
|
|
68
|
+
* Makes a PATCH request using the bonded HTTP client.
|
|
69
|
+
*
|
|
70
|
+
* @param url - The request URL.
|
|
71
|
+
* @param data - The request body (partial update).
|
|
72
|
+
* @param config - Optional request configuration.
|
|
73
|
+
* @returns The HTTP response with typed data.
|
|
74
|
+
*/
|
|
75
|
+
export const patch = (url, data, config) => getClient().patch(url, data, config);
|
|
76
|
+
/**
|
|
77
|
+
* Makes a DELETE request using the bonded HTTP client.
|
|
78
|
+
*
|
|
79
|
+
* @param url - The request URL.
|
|
80
|
+
* @param config - Optional request configuration.
|
|
81
|
+
* @returns The HTTP response with typed data.
|
|
82
|
+
*/
|
|
83
|
+
export const del = (url, config) => getClient().delete(url, config);
|
|
84
|
+
/**
|
|
85
|
+
* Pre-created fetch client for environments where `fetch` is available.
|
|
86
|
+
* `null` in environments without a global `fetch`.
|
|
87
|
+
*/
|
|
88
|
+
export const fetchClient = typeof fetch !== 'undefined' ? createFetchClient() : null;
|
|
89
|
+
//# sourceMappingURL=utilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.js","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAGzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGjD,MAAM,SAAS,GAAG,aAAa,CAAA;AAE/B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAkB,EAAQ,EAAE;IACpD,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;AACzB,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAe,EAAE;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAa,SAAS,CAAC,CAAA;IAC/C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAA;IACpC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACzB,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAc,GAAW,EAAE,MAAsB,EAA4B,EAAE,CAChG,SAAS,EAAE,CAAC,GAAG,CAAI,GAAG,EAAE,MAAM,CAAC,CAAA;AAEjC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAClB,GAAW,EACX,IAAc,EACd,MAAsB,EACI,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,CAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CACjB,GAAW,EACX,IAAc,EACd,MAAsB,EACI,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,CAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AAEpE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,GAAW,EACX,IAAc,EACd,MAAsB,EACI,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,CAAI,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AAEtE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAc,GAAW,EAAE,MAAsB,EAA4B,EAAE,CAChG,SAAS,EAAE,CAAC,MAAM,CAAI,GAAG,EAAE,MAAM,CAAC,CAAA;AAEpC;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@molecule/app-http",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Client HTTP interface for molecule.dev",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"molecule",
|
|
24
|
+
"http",
|
|
25
|
+
"api-client",
|
|
26
|
+
"fetch"
|
|
27
|
+
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@molecule/app-bond": "^1.0.0",
|
|
31
|
+
"@molecule/app-i18n": "^1.0.0",
|
|
32
|
+
"@molecule/app-logger": "^1.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@molecule/app-bond": "1.0.0",
|
|
36
|
+
"@molecule/app-i18n": "1.0.0",
|
|
37
|
+
"@molecule/app-logger": "1.0.0",
|
|
38
|
+
"@types/node": "26.1.2",
|
|
39
|
+
"typescript": "6.0.3",
|
|
40
|
+
"vitest": "4.1.10"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/molecule-dev/molecule.git",
|
|
45
|
+
"directory": "packages/app/core/http"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/app/core/http",
|
|
48
|
+
"bugs": "https://github.com/molecule-dev/molecule/issues",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|