@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/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
+ }