service-creator 0.1.3 → 0.3.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/README.md +106 -32
- package/dist/__tests__/create-service.test.d.ts +1 -0
- package/dist/create-service.d.ts +88 -3
- package/dist/fetch-types.d.ts +1 -0
- package/dist/fetchJSON.d.ts +1 -0
- package/dist/index.js +128 -107
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +2 -0
- package/dist/index.umd.cjs.map +1 -0
- package/package.json +44 -47
- package/dist/__tests__/url-helpers.test.js +0 -69
- package/dist/combinePathWithBase.js +0 -15
- package/dist/create-service.js +0 -227
- package/dist/fetch-types.js +0 -6
- package/dist/fetchJSON.js +0 -134
- package/dist/ls.js +0 -31
- package/dist/try-parse.js +0 -17
- package/dist/url-helpers.js +0 -74
- package/dist/withRetries.js +0 -115
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
[](https://www.npmjs.com/package/service-creator)
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://github.com/royriojas/create-service/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
|
|
1
7
|
# service-creator
|
|
2
8
|
|
|
3
|
-
A simple abstraction to create "services"
|
|
9
|
+
A simple abstraction to create "services" — plain objects with typed async methods that perform fetch calls, using convention over configuration.
|
|
4
10
|
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
@@ -10,9 +16,15 @@ npm install service-creator
|
|
|
10
16
|
|
|
11
17
|
## Usage
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
Define your service endpoints with `createEndpoint<TResponse, TArgs?>()` for full type safety:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createService, createEndpoint } from 'service-creator';
|
|
23
|
+
|
|
24
|
+
interface User {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
const fetcher = {
|
|
18
30
|
fetch: async (url, opts) => {
|
|
@@ -21,47 +33,109 @@ const fetcher = {
|
|
|
21
33
|
},
|
|
22
34
|
};
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
const userService = createService({
|
|
37
|
+
endpoints: {
|
|
38
|
+
// No args — just one generic for the response type
|
|
39
|
+
listUsers: createEndpoint<User[]>({
|
|
40
|
+
url: '/v1/users',
|
|
41
|
+
}),
|
|
42
|
+
|
|
43
|
+
// With args — response type first, then args type
|
|
44
|
+
getUser: createEndpoint<User, { id: string }>({
|
|
45
|
+
url: ({ id }) => `/v1/users/${id}`,
|
|
46
|
+
}),
|
|
47
|
+
|
|
48
|
+
// POST with body
|
|
49
|
+
createUser: createEndpoint<User, { name: string }>({
|
|
50
|
+
url: '/v1/users',
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: (args) => args, // args is typed as { name: string }
|
|
53
|
+
}),
|
|
54
|
+
|
|
55
|
+
// Response transformation
|
|
56
|
+
getUserName: createEndpoint<string, { id: string }>({
|
|
57
|
+
url: ({ id }) => `/v1/users/${id}`,
|
|
58
|
+
transform: (data) => data.name, // raw response → typed return value
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
basePath: 'https://api.example.com',
|
|
62
|
+
fetcher,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// All types are fully inferred:
|
|
66
|
+
const users = await userService.listUsers(); // User[]
|
|
67
|
+
const user = await userService.getUser({ id: '123' }); // User
|
|
68
|
+
const created = await userService.createUser({ name: 'Ali' });// User
|
|
69
|
+
const name = await userService.getUserName({ id: '123' }); // string
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `createEndpoint<TResponse, TArgs?, TError?>`
|
|
73
|
+
|
|
74
|
+
| Generic | Description | Default |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `TResponse` | Return type (`Promise<TResponse>`) | `any` |
|
|
77
|
+
| `TArgs` | Input parameter type. Omit for no-arg endpoints. | `void` |
|
|
78
|
+
| `TError` | Error type (available via `InferError`) | `Error` |
|
|
79
|
+
|
|
80
|
+
### Descriptor options
|
|
81
|
+
|
|
82
|
+
| Option | Type | Description |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `url` | `string \| (args: TArgs) => string` | Endpoint URL — static or dynamic |
|
|
85
|
+
| `method` | `HttpMethod` | HTTP method (`GET`, `POST`, `PUT`, `DELETE`, etc.) |
|
|
86
|
+
| `body` | `object \| (args: TArgs) => any` | Request body — static or computed from args |
|
|
87
|
+
| `headers` | `object \| (args: TArgs) => any` | Request headers — static or computed from args |
|
|
88
|
+
| `params` | `object \| (args: TArgs) => any` | Query string params — static or computed from args |
|
|
89
|
+
| `fetchOpts` | `FetchOptions` | Additional fetch options (credentials, mode, etc.) |
|
|
90
|
+
| `transform` | `(data: any) => TResponse` | Transform the raw response before returning |
|
|
91
|
+
|
|
92
|
+
### Custom error types
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface ApiError {
|
|
96
|
+
code: number;
|
|
97
|
+
message: string;
|
|
30
98
|
}
|
|
31
99
|
|
|
32
|
-
const
|
|
33
|
-
|
|
100
|
+
const service = createService({
|
|
101
|
+
endpoints: {
|
|
102
|
+
riskyCall: createEndpoint<Data, { id: string }, ApiError>({
|
|
103
|
+
url: ({ id }) => `/v1/data/${id}`,
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
fetcher,
|
|
107
|
+
});
|
|
108
|
+
```
|
|
34
109
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
110
|
+
## Alternative styles
|
|
111
|
+
|
|
112
|
+
Plain descriptors (types inferred from function signatures):
|
|
39
113
|
|
|
114
|
+
```ts
|
|
40
115
|
const service = createService({
|
|
41
116
|
endpoints: {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
body: ({ prompt }) => ({ prompt }),
|
|
46
|
-
headers: commonHeadersFn,
|
|
47
|
-
},
|
|
48
|
-
getDataById: {
|
|
49
|
-
method: 'GET',
|
|
50
|
-
headers: commonHeadersFn,
|
|
51
|
-
url: ({ id }) => `/v1/get-some-data-by-id/${id}`,
|
|
117
|
+
getUser: {
|
|
118
|
+
url: (id: string) => `/v1/users/${id}`,
|
|
119
|
+
transform: (data: any): User => data,
|
|
52
120
|
},
|
|
53
121
|
},
|
|
54
|
-
basePath: api,
|
|
55
122
|
fetcher,
|
|
56
123
|
});
|
|
124
|
+
```
|
|
57
125
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
console.log(data); // expected an array of SomeData
|
|
126
|
+
Legacy explicit interface:
|
|
61
127
|
|
|
62
|
-
|
|
128
|
+
```ts
|
|
129
|
+
interface MyService {
|
|
130
|
+
getUser: (id: string) => Promise<User>;
|
|
131
|
+
}
|
|
63
132
|
|
|
64
|
-
|
|
133
|
+
const service = createService<MyService>({
|
|
134
|
+
endpoints: {
|
|
135
|
+
getUser: { url: (id: string) => `/v1/users/${id}` },
|
|
136
|
+
},
|
|
137
|
+
fetcher,
|
|
138
|
+
});
|
|
65
139
|
```
|
|
66
140
|
|
|
67
141
|
## License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/create-service.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Fetcher, FetchOptions } from './fetch-types';
|
|
2
|
+
|
|
2
3
|
export interface Fn<T extends Array<any>> {
|
|
3
4
|
(...args: T): string;
|
|
4
5
|
}
|
|
@@ -7,18 +8,102 @@ export type StringOrFn<T extends ServiceFn> = string | Fn<Parameters<T>>;
|
|
|
7
8
|
export type ObjectOrFn<T extends ServiceFn> = object | Fn<Parameters<T>>;
|
|
8
9
|
export interface Descriptor<K extends ServiceFn> {
|
|
9
10
|
url: StringOrFn<K>;
|
|
10
|
-
method?:
|
|
11
|
+
method?: HttpMethod;
|
|
11
12
|
body?: ObjectOrFn<K>;
|
|
12
13
|
headers?: ObjectOrFn<K>;
|
|
13
14
|
params?: ObjectOrFn<K>;
|
|
14
15
|
fetchOpts?: FetchOptions;
|
|
16
|
+
transform?: (data: any) => any;
|
|
15
17
|
}
|
|
16
18
|
export type ServiceDescriptor<T> = {
|
|
17
19
|
[K in keyof T]: Descriptor<T[K] extends ServiceFn ? T[K] : never>;
|
|
18
20
|
};
|
|
19
21
|
export interface CreateServiceArgs<T> {
|
|
20
22
|
endpoints: ServiceDescriptor<T>;
|
|
21
|
-
basePath
|
|
23
|
+
basePath?: string;
|
|
22
24
|
fetcher: Fetcher;
|
|
23
25
|
}
|
|
24
|
-
export
|
|
26
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT';
|
|
27
|
+
export interface DescriptorBase {
|
|
28
|
+
url: string | ((...args: any[]) => string);
|
|
29
|
+
method?: HttpMethod;
|
|
30
|
+
body?: object | ((...args: any[]) => any);
|
|
31
|
+
headers?: object | ((...args: any[]) => any);
|
|
32
|
+
params?: object | ((...args: any[]) => any);
|
|
33
|
+
fetchOpts?: FetchOptions;
|
|
34
|
+
transform?: (data: any) => any;
|
|
35
|
+
}
|
|
36
|
+
/** A descriptor branded with explicit TResponse, TArgs, TError types */
|
|
37
|
+
export interface TypedEndpoint<TResponse = any, TArgs = void, TError = Error> extends DescriptorBase {
|
|
38
|
+
readonly [ENDPOINT_TYPES]: {
|
|
39
|
+
args: TArgs;
|
|
40
|
+
response: TResponse;
|
|
41
|
+
error: TError;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Configuration accepted by createEndpoint */
|
|
45
|
+
export interface EndpointConfig<TResponse, TArgs> {
|
|
46
|
+
url: string | ((args: TArgs) => string);
|
|
47
|
+
method?: HttpMethod;
|
|
48
|
+
body?: object | ((args: TArgs) => any);
|
|
49
|
+
headers?: object | ((args: TArgs) => any);
|
|
50
|
+
params?: object | ((args: TArgs) => any);
|
|
51
|
+
fetchOpts?: FetchOptions;
|
|
52
|
+
transform?: (data: any) => TResponse;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Define a typed endpoint with explicit response/input/error types.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // No args:
|
|
60
|
+
* createEndpoint<User[]>({ url: '/users' })
|
|
61
|
+
* // service method: () => Promise<User[]>
|
|
62
|
+
*
|
|
63
|
+
* // With args:
|
|
64
|
+
* createEndpoint<User, { id: string }>({
|
|
65
|
+
* url: ({ id }) => `/users/${id}`,
|
|
66
|
+
* })
|
|
67
|
+
* // service method: (args: { id: string }) => Promise<User>
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function createEndpoint<TResponse = any, TArgs = void, TError = Error>(config: EndpointConfig<TResponse, TArgs>): TypedEndpoint<TResponse, TArgs, TError>;
|
|
71
|
+
/** Infer the service method args from a descriptor */
|
|
72
|
+
export type InferArgs<D> = D extends {
|
|
73
|
+
readonly [ENDPOINT_TYPES]: {
|
|
74
|
+
args: infer A;
|
|
75
|
+
};
|
|
76
|
+
} ? [A] extends [void] ? [] : [args: A] : D extends {
|
|
77
|
+
url: (...args: infer P) => any;
|
|
78
|
+
} ? P : D extends {
|
|
79
|
+
body: (...args: infer P) => any;
|
|
80
|
+
} ? P : D extends {
|
|
81
|
+
params: (...args: infer P) => any;
|
|
82
|
+
} ? P : D extends {
|
|
83
|
+
headers: (...args: infer P) => any;
|
|
84
|
+
} ? P : any[];
|
|
85
|
+
/** Infer the service method return type from a descriptor */
|
|
86
|
+
export type InferReturn<D> = D extends {
|
|
87
|
+
readonly [ENDPOINT_TYPES]: {
|
|
88
|
+
response: infer R;
|
|
89
|
+
};
|
|
90
|
+
} ? R : D extends {
|
|
91
|
+
transform: (data: any) => infer R;
|
|
92
|
+
} ? R : any;
|
|
93
|
+
/** Infer the error type from a branded descriptor */
|
|
94
|
+
export type InferError<D> = D extends {
|
|
95
|
+
readonly [ENDPOINT_TYPES]: {
|
|
96
|
+
error: infer E;
|
|
97
|
+
};
|
|
98
|
+
} ? E : Error;
|
|
99
|
+
export type InferService<T extends Record<string, DescriptorBase>> = {
|
|
100
|
+
[K in keyof T]: (...args: InferArgs<T[K]>) => Promise<InferReturn<T[K]>>;
|
|
101
|
+
};
|
|
102
|
+
/** Create a service with types inferred from the endpoint descriptors */
|
|
103
|
+
export declare function createService<const T extends Record<string, DescriptorBase>>(args: {
|
|
104
|
+
endpoints: T;
|
|
105
|
+
basePath?: string;
|
|
106
|
+
fetcher: Fetcher;
|
|
107
|
+
}): InferService<T>;
|
|
108
|
+
/** @deprecated Use the inferred API (call without explicit generic) */
|
|
109
|
+
export declare function createService<T>(args: CreateServiceArgs<T>): T;
|
package/dist/fetch-types.d.ts
CHANGED
package/dist/fetchJSON.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,110 +1,131 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var _fetchTypes = require("./fetch-types");
|
|
21
|
-
|
|
22
|
-
Object.keys(_fetchTypes).forEach(function (key) {
|
|
23
|
-
if (key === "default" || key === "__esModule") return;
|
|
24
|
-
if (key in exports && exports[key] === _fetchTypes[key]) return;
|
|
25
|
-
Object.defineProperty(exports, key, {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
get: function get() {
|
|
28
|
-
return _fetchTypes[key];
|
|
29
|
-
}
|
|
1
|
+
var b = Object.defineProperty;
|
|
2
|
+
var R = (e, t, r) => t in e ? b(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
|
+
var y = (e, t, r) => (R(e, typeof t != "symbol" ? t + "" : t, r), r);
|
|
4
|
+
const P = (e, t) => (e = e.replace(/\/$/, ""), t = t.replace(/^\//, ""), `${e}/${t}`), O = (e, t) => {
|
|
5
|
+
const r = t ?? new URLSearchParams();
|
|
6
|
+
return Object.keys(e).forEach((a) => {
|
|
7
|
+
const n = e[a];
|
|
8
|
+
typeof n < "u" && (Array.isArray(n) ? n.forEach((s) => {
|
|
9
|
+
r.append(a, s);
|
|
10
|
+
}) : r.set(a, `${n}`));
|
|
11
|
+
}), r;
|
|
12
|
+
}, U = (e, t) => {
|
|
13
|
+
const [r, c] = e.split(/\?/), a = new URLSearchParams(c), n = O(t, a), s = [r], o = n.toString();
|
|
14
|
+
return o.length > 0 && s.push(`?${o}`), s.join("");
|
|
15
|
+
}, L = (e, t) => {
|
|
16
|
+
const [r, c] = e.split(/\?/), a = new URLSearchParams(c);
|
|
17
|
+
t.forEach((o) => {
|
|
18
|
+
a.delete(o);
|
|
30
19
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
20
|
+
const n = [r], s = a.toString();
|
|
21
|
+
return s.length > 0 && n.push(`?${s}`), n.join("");
|
|
22
|
+
}, v = (e = []) => e.length > 0 ? e[0] : void 0, d = async (e, ...t) => {
|
|
23
|
+
try {
|
|
24
|
+
return await e(...t);
|
|
25
|
+
} catch {
|
|
26
|
+
const c = e ? e.name : "anonymous";
|
|
27
|
+
console.warn(`[CreateService Error] ${c} failed with args:`, t);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function C(e) {
|
|
32
|
+
return e;
|
|
33
|
+
}
|
|
34
|
+
function D({ endpoints: e, basePath: t, fetcher: r }) {
|
|
35
|
+
return Object.keys(e).reduce((c, a) => (c[a] = async (...n) => {
|
|
36
|
+
const { url: s, body: o, headers: i, method: u, params: f, fetchOpts: l, transform: w } = e[a];
|
|
37
|
+
let h = typeof s == "function" ? await d(s, ...n) : s;
|
|
38
|
+
if (typeof h != "string")
|
|
39
|
+
throw console.error(
|
|
40
|
+
"[Create Service Error]: URL must be a string or a function returning a string",
|
|
41
|
+
h
|
|
42
|
+
), new Error(
|
|
43
|
+
`URL must be a string or a function returning a string for method ${a}.`
|
|
44
|
+
);
|
|
45
|
+
t && (h = P(t, h));
|
|
46
|
+
const S = typeof o == "function" ? await d(o, ...n) : u !== "GET" ? v(n) : void 0, g = typeof i == "function" ? await d(i, ...n) : i, m = typeof f == "function" ? await d(f, ...n) : f, E = { ...l || {}, body: S, headers: g, method: u };
|
|
47
|
+
m && (h = U(h, m));
|
|
48
|
+
const p = await r.fetch(h, E);
|
|
49
|
+
return w ? w(p) : p;
|
|
50
|
+
}, c), {});
|
|
51
|
+
}
|
|
52
|
+
class I extends Error {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(...arguments);
|
|
55
|
+
y(this, "response");
|
|
56
|
+
y(this, "data");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function J(e, t) {
|
|
60
|
+
const { serializeBody: r = !0, method: c, ...a } = t || {}, n = c ?? "GET", s = {
|
|
61
|
+
...a,
|
|
62
|
+
method: n,
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-type": "application/json",
|
|
65
|
+
...a.headers
|
|
66
|
+
},
|
|
67
|
+
body: n === "GET" ? void 0 : r ? JSON.stringify(t == null ? void 0 : t.body) : t == null ? void 0 : t.body
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
const o = await fetch(e, s);
|
|
71
|
+
if (o.status < 200 || o.status > 300) {
|
|
72
|
+
let u;
|
|
73
|
+
try {
|
|
74
|
+
u = await o.json();
|
|
75
|
+
} catch {
|
|
76
|
+
u = { error: "JSON_ERROR_NOT_RECEIVED" };
|
|
77
|
+
}
|
|
78
|
+
const f = new I(u.error);
|
|
79
|
+
throw f.response = o, f.data = u, f;
|
|
42
80
|
}
|
|
43
|
-
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
return await o.json();
|
|
82
|
+
} catch (o) {
|
|
83
|
+
throw console.error(">>> err", o), o;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const N = (e, { retries: t = 2, onTryError: r, onFail: c } = {}) => async (...n) => {
|
|
87
|
+
let s = 0;
|
|
88
|
+
for (; s < t; )
|
|
89
|
+
try {
|
|
90
|
+
return await e(...n);
|
|
91
|
+
} catch (o) {
|
|
92
|
+
if (s < t)
|
|
93
|
+
try {
|
|
94
|
+
await (r == null ? void 0 : r(o, n, s));
|
|
95
|
+
} catch (i) {
|
|
96
|
+
throw await (c == null ? void 0 : c(i, n)), i;
|
|
97
|
+
}
|
|
98
|
+
if (s += 1, s >= t)
|
|
99
|
+
throw await (c == null ? void 0 : c(o, n)), o;
|
|
55
100
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Object.keys(_tryParse).forEach(function (key) {
|
|
88
|
-
if (key === "default" || key === "__esModule") return;
|
|
89
|
-
if (key in exports && exports[key] === _tryParse[key]) return;
|
|
90
|
-
Object.defineProperty(exports, key, {
|
|
91
|
-
enumerable: true,
|
|
92
|
-
get: function get() {
|
|
93
|
-
return _tryParse[key];
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
var _ls = require("./ls");
|
|
99
|
-
|
|
100
|
-
Object.keys(_ls).forEach(function (key) {
|
|
101
|
-
if (key === "default" || key === "__esModule") return;
|
|
102
|
-
if (key in exports && exports[key] === _ls[key]) return;
|
|
103
|
-
Object.defineProperty(exports, key, {
|
|
104
|
-
enumerable: true,
|
|
105
|
-
get: function get() {
|
|
106
|
-
return _ls[key];
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTs7QUFDQTs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCAqIGZyb20gJy4vY3JlYXRlLXNlcnZpY2UnO1xuZXhwb3J0ICogZnJvbSAnLi9mZXRjaC10eXBlcyc7XG5leHBvcnQgKiBmcm9tICcuL2ZldGNoSlNPTic7XG5leHBvcnQgKiBmcm9tICcuL3dpdGhSZXRyaWVzJztcbmV4cG9ydCAqIGZyb20gJy4vY29tYmluZVBhdGhXaXRoQmFzZSc7XG5leHBvcnQgKiBmcm9tICcuL3VybC1oZWxwZXJzJztcbmV4cG9ydCAqIGZyb20gJy4vdHJ5LXBhcnNlJztcbmV4cG9ydCAqIGZyb20gJy4vbHMnO1xuIl19
|
|
101
|
+
}, $ = (e, t) => {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(e);
|
|
104
|
+
} catch {
|
|
105
|
+
return t;
|
|
106
|
+
}
|
|
107
|
+
}, A = (e, t) => {
|
|
108
|
+
const r = localStorage.getItem(e);
|
|
109
|
+
return $(r) ?? t;
|
|
110
|
+
}, x = (e, t) => {
|
|
111
|
+
const r = JSON.stringify(t);
|
|
112
|
+
localStorage.setItem(e, r);
|
|
113
|
+
}, G = (e) => {
|
|
114
|
+
localStorage.removeItem(e);
|
|
115
|
+
};
|
|
116
|
+
export {
|
|
117
|
+
I as ResponseError,
|
|
118
|
+
P as combinePathWithBase,
|
|
119
|
+
C as createEndpoint,
|
|
120
|
+
D as createService,
|
|
121
|
+
J as fetchJSON,
|
|
122
|
+
A as getItem,
|
|
123
|
+
O as paramsToURLSearchParams,
|
|
124
|
+
G as removeItem,
|
|
125
|
+
L as removeQueryParam,
|
|
126
|
+
x as setItem,
|
|
127
|
+
U as setQueryParams,
|
|
128
|
+
$ as tryParse,
|
|
129
|
+
N as withRetries
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/combinePathWithBase.ts","../src/url-helpers.ts","../src/create-service.ts","../src/fetchJSON.ts","../src/withRetries.ts","../src/try-parse.ts","../src/ls.ts"],"sourcesContent":["export const combinePathWithBase = (host: string, path: string) => {\n host = host.replace(/\\/$/, '');\n path = path.replace(/^\\//, '');\n return `${host}/${path}`;\n};\n","export const paramsToURLSearchParams = <T extends object>(\n params: T,\n urlParams?: URLSearchParams,\n) => {\n const urlSearchParams = urlParams ?? new URLSearchParams();\n\n const keys = Object.keys(params);\n keys.forEach((key) => {\n const val = params[key as keyof T];\n if (typeof val !== 'undefined') {\n if (Array.isArray(val)) {\n val.forEach((v) => {\n urlSearchParams.append(key, v);\n });\n } else {\n urlSearchParams.set(key, `${val}`);\n }\n }\n });\n\n return urlSearchParams;\n};\n\nexport const setQueryParams = <T extends object>(url: string, params: T) => {\n const [baseString, searchString] = url.split(/\\?/);\n\n const urlParams = new URLSearchParams(searchString);\n const mergeParams = paramsToURLSearchParams(params, urlParams);\n\n const parts = [baseString];\n\n const paramsToAdd = mergeParams.toString();\n\n if (paramsToAdd.length > 0) {\n parts.push(`?${paramsToAdd}`);\n }\n\n return parts.join('');\n};\n\nexport const removeQueryParam = (url: string, params: string[]) => {\n const [baseString, searchString] = url.split(/\\?/);\n\n const urlParams = new URLSearchParams(searchString);\n\n params.forEach((param) => {\n urlParams.delete(param);\n });\n\n const parts = [baseString];\n\n const paramsToAdd = urlParams.toString();\n\n if (paramsToAdd.length > 0) {\n parts.push(`?${paramsToAdd}`);\n }\n\n return parts.join('');\n};\n","import { combinePathWithBase } from './combinePathWithBase';\nimport { Fetcher, FetchOptions } from './fetch-types';\nimport { setQueryParams } from './url-helpers';\n\nconst getDefaultData = (args: any[] = []) => (args.length > 0 ? args[0] : undefined);\n\nconst tryCall = async (fn: Function, ...args: any[]) => {\n try {\n return await fn(...args);\n } catch (err) {\n const fnName = fn ? fn.name : 'anonymous';\n console.warn(`[CreateService Error] ${fnName} failed with args:`, args);\n return undefined;\n }\n};\n\n// ─── Legacy types (kept for backward compat with createService<T>) ───\n\nexport interface Fn<T extends Array<any>> {\n (...args: T): string;\n}\n\nexport type ServiceFn = (...args: any[]) => Promise<any>;\n\nexport type StringOrFn<T extends ServiceFn> = string | Fn<Parameters<T>>;\nexport type ObjectOrFn<T extends ServiceFn> = object | Fn<Parameters<T>>;\n\nexport interface Descriptor<K extends ServiceFn> {\n url: StringOrFn<K>;\n method?: HttpMethod;\n body?: ObjectOrFn<K>;\n headers?: ObjectOrFn<K>;\n params?: ObjectOrFn<K>;\n fetchOpts?: FetchOptions;\n transform?: (data: any) => any;\n}\n\nexport type ServiceDescriptor<T> = {\n [K in keyof T]: Descriptor<T[K] extends ServiceFn ? T[K] : never>;\n};\n\nexport interface CreateServiceArgs<T> {\n endpoints: ServiceDescriptor<T>;\n basePath?: string;\n fetcher: Fetcher;\n}\n\n// ─── New inferred API types ───\n\nexport type HttpMethod =\n | 'GET'\n | 'POST'\n | 'PUT'\n | 'DELETE'\n | 'PATCH'\n | 'HEAD'\n | 'OPTIONS'\n | 'TRACE'\n | 'CONNECT';\n\nexport interface DescriptorBase {\n url: string | ((...args: any[]) => string);\n method?: HttpMethod;\n body?: object | ((...args: any[]) => any);\n headers?: object | ((...args: any[]) => any);\n params?: object | ((...args: any[]) => any);\n fetchOpts?: FetchOptions;\n transform?: (data: any) => any;\n}\n\n// ─── createEndpoint helper ───\n\n/** @internal Type brand symbol — not present at runtime */\ndeclare const ENDPOINT_TYPES: unique symbol;\n\n/** A descriptor branded with explicit TResponse, TArgs, TError types */\nexport interface TypedEndpoint<\n TResponse = any,\n TArgs = void,\n TError = Error,\n> extends DescriptorBase {\n readonly [ENDPOINT_TYPES]: { args: TArgs; response: TResponse; error: TError };\n}\n\n/** Configuration accepted by createEndpoint */\nexport interface EndpointConfig<TResponse, TArgs> {\n url: string | ((args: TArgs) => string);\n method?: HttpMethod;\n body?: object | ((args: TArgs) => any);\n headers?: object | ((args: TArgs) => any);\n params?: object | ((args: TArgs) => any);\n fetchOpts?: FetchOptions;\n transform?: (data: any) => TResponse;\n}\n\n/**\n * Define a typed endpoint with explicit response/input/error types.\n *\n * @example\n * ```ts\n * // No args:\n * createEndpoint<User[]>({ url: '/users' })\n * // service method: () => Promise<User[]>\n *\n * // With args:\n * createEndpoint<User, { id: string }>({\n * url: ({ id }) => `/users/${id}`,\n * })\n * // service method: (args: { id: string }) => Promise<User>\n * ```\n */\nexport function createEndpoint<TResponse = any, TArgs = void, TError = Error>(\n config: EndpointConfig<TResponse, TArgs>,\n): TypedEndpoint<TResponse, TArgs, TError> {\n return config as any;\n}\n\n// ─── Type inference ───\n\n/** Infer the service method args from a descriptor */\nexport type InferArgs<D> =\n // 1. Branded endpoint — use the explicit TArgs\n D extends { readonly [ENDPOINT_TYPES]: { args: infer A } }\n ? [A] extends [void]\n ? []\n : [args: A]\n : // 2. Infer from descriptor functions (url → body → params → headers)\n D extends { url: (...args: infer P) => any }\n ? P\n : D extends { body: (...args: infer P) => any }\n ? P\n : D extends { params: (...args: infer P) => any }\n ? P\n : D extends { headers: (...args: infer P) => any }\n ? P\n : any[];\n\n/** Infer the service method return type from a descriptor */\nexport type InferReturn<D> =\n // 1. Branded endpoint — use the explicit TResponse\n D extends { readonly [ENDPOINT_TYPES]: { response: infer R } }\n ? R\n : // 2. Infer from transform return type\n D extends { transform: (data: any) => infer R }\n ? R\n : any;\n\n/** Infer the error type from a branded descriptor */\nexport type InferError<D> = D extends {\n readonly [ENDPOINT_TYPES]: { error: infer E };\n}\n ? E\n : Error;\n\nexport type InferService<T extends Record<string, DescriptorBase>> = {\n [K in keyof T]: (...args: InferArgs<T[K]>) => Promise<InferReturn<T[K]>>;\n};\n\n// ─── Overloads ───\n\n/** Create a service with types inferred from the endpoint descriptors */\nexport function createService<const T extends Record<string, DescriptorBase>>(args: {\n endpoints: T;\n basePath?: string;\n fetcher: Fetcher;\n}): InferService<T>;\n\n/** @deprecated Use the inferred API (call without explicit generic) */\nexport function createService<T>(args: CreateServiceArgs<T>): T;\n\n// ─── Implementation ───\n\nexport function createService({ endpoints, basePath, fetcher }: any): any {\n return Object.keys(endpoints).reduce((service: any, serviceName: string) => {\n service[serviceName] = async (...args: any[]) => {\n const { url, body, headers, method, params, fetchOpts, transform } = endpoints[serviceName];\n\n let urlToUse: string = typeof url === 'function' ? await tryCall(url, ...args) : url;\n if (typeof urlToUse !== 'string') {\n console.error(\n '[Create Service Error]: URL must be a string or a function returning a string',\n urlToUse,\n );\n throw new Error(\n `URL must be a string or a function returning a string for method ${serviceName}.`,\n );\n }\n\n // TODO: detect absolute urlToUse and prevent adding the base path\n if (basePath) {\n urlToUse = combinePathWithBase(basePath, urlToUse);\n }\n\n const getDefault = () => (method !== 'GET' ? getDefaultData(args) : undefined);\n\n // if data is a function we pass all the arguments to the function so it creates\n // the payload with all the provided arguments, expecting it to return a single object\n // if the data is not a function then we get the first argument passed to the function as the\n // data payload. If this is an object it will be serialized using json stringify before sending it\n const bodyToUse = typeof body === 'function' ? await tryCall(body, ...args) : getDefault();\n const headersToUse =\n typeof headers === 'function' ? await tryCall(headers, ...args) : headers;\n\n const paramsToUse = typeof params === 'function' ? await tryCall(params, ...args) : params;\n const opts = { ...(fetchOpts || {}), body: bodyToUse, headers: headersToUse, method };\n\n if (paramsToUse) {\n urlToUse = setQueryParams(urlToUse, paramsToUse);\n }\n\n const result = await fetcher.fetch(urlToUse, opts);\n return transform ? transform(result) : result;\n };\n\n return service;\n }, {} as any);\n}\n","import { FetchOptions, FetchURL, SerializeBodyProps } from './fetch-types';\n\nexport interface ErrorData {\n error: string;\n}\nexport class ResponseError extends Error {\n response?: Response;\n\n data?: ErrorData;\n}\n\nexport async function fetchJSON<T, K>(url: FetchURL, params?: SerializeBodyProps<K>): Promise<T> {\n const { serializeBody = true, method, ...rest } = params || {};\n const fetchMethod = method ?? 'GET';\n\n const props: FetchOptions = {\n ...rest,\n method: fetchMethod,\n headers: {\n 'Content-type': 'application/json',\n ...rest.headers,\n },\n body:\n fetchMethod === 'GET'\n ? undefined\n : ((serializeBody ? JSON.stringify(params?.body) : params?.body) as FetchOptions['body']),\n };\n\n try {\n const resp = await fetch(url, props);\n if (resp.status < 200 || resp.status > 300) {\n let data: ErrorData;\n try {\n data = await resp.json();\n } catch (parseError) {\n data = { error: 'JSON_ERROR_NOT_RECEIVED' };\n }\n const error = new ResponseError(data.error);\n error.response = resp;\n error.data = data;\n\n throw error;\n }\n const data = await resp.json();\n return data;\n } catch (err) {\n console.error('>>> err', err);\n throw err;\n }\n}\n\nexport type FetchFn = typeof fetchJSON;\n","export interface WithRetriesOpts<K> {\n retries?: number;\n onFail?: (error: Error, args: K) => Promise<void> | void;\n onTryError?: (error: Error, args: K, attemptIndex: number) => Promise<void> | void;\n}\n\nexport const withRetries = <K extends any[], T>(\n fn: (...args: K) => Promise<T>,\n { retries = 2, onTryError, onFail }: WithRetriesOpts<K> = {},\n) => {\n const retFn = async (...args: K) => {\n let attemptIndex = 0;\n\n while (attemptIndex < retries) {\n try {\n const ret = await fn(...args); // eslint-disable-line no-await-in-loop\n\n return ret as T;\n } catch (err) {\n if (attemptIndex < retries) {\n try {\n await onTryError?.(err as Error, args, attemptIndex); // eslint-disable-line no-await-in-loop\n } catch (error) {\n await onFail?.(error as Error, args); // eslint-disable-line no-await-in-loop\n throw error;\n }\n }\n attemptIndex += 1;\n if (attemptIndex >= retries) {\n await onFail?.(err as Error, args); // eslint-disable-line no-await-in-loop\n throw err;\n }\n }\n }\n };\n\n return retFn as (...args: K) => Promise<T>;\n};\n","export const tryParse = <T>(val: string | null, def?: T) => {\n try {\n return JSON.parse(val!) as T;\n } catch (err) {\n return def;\n }\n};\n","import { tryParse } from './try-parse';\n\nexport const getItem = <T>(key: string, defaultValue: T) => {\n const entity = localStorage.getItem(key);\n\n return tryParse(entity) ?? defaultValue;\n};\n\nexport const setItem = <T>(key: string, entity: T) => {\n const str = JSON.stringify(entity);\n\n localStorage.setItem(key, str);\n};\n\nexport const removeItem = (key: string) => {\n localStorage.removeItem(key);\n};\n"],"names":["combinePathWithBase","host","path","paramsToURLSearchParams","params","urlParams","urlSearchParams","key","val","v","setQueryParams","url","baseString","searchString","mergeParams","parts","paramsToAdd","removeQueryParam","param","getDefaultData","args","tryCall","fn","fnName","createEndpoint","config","createService","endpoints","basePath","fetcher","service","serviceName","body","headers","method","fetchOpts","transform","urlToUse","bodyToUse","headersToUse","paramsToUse","opts","result","ResponseError","__publicField","fetchJSON","serializeBody","rest","fetchMethod","props","resp","data","error","err","withRetries","retries","onTryError","onFail","attemptIndex","tryParse","def","getItem","defaultValue","entity","setItem","str","removeItem"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(r,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(r=typeof globalThis<"u"?globalThis:r||self,u(r.serviceCreator={}))})(this,function(r){"use strict";var A=Object.defineProperty;var Q=(r,u,f)=>u in r?A(r,u,{enumerable:!0,configurable:!0,writable:!0,value:f}):r[u]=f;var w=(r,u,f)=>(Q(r,typeof u!="symbol"?u+"":u,f),f);const u=(t,e)=>(t=t.replace(/\/$/,""),e=e.replace(/^\//,""),`${t}/${e}`),f=(t,e)=>{const o=e??new URLSearchParams;return Object.keys(t).forEach(i=>{const n=t[i];typeof n<"u"&&(Array.isArray(n)?n.forEach(s=>{o.append(i,s)}):o.set(i,`${n}`))}),o},S=(t,e)=>{const[o,c]=t.split(/\?/),i=new URLSearchParams(c),n=f(e,i),s=[o],a=n.toString();return a.length>0&&s.push(`?${a}`),s.join("")},v=(t,e)=>{const[o,c]=t.split(/\?/),i=new URLSearchParams(c);e.forEach(a=>{i.delete(a)});const n=[o],s=i.toString();return s.length>0&&n.push(`?${s}`),n.join("")},O=(t=[])=>t.length>0?t[0]:void 0,l=async(t,...e)=>{try{return await t(...e)}catch{const c=t?t.name:"anonymous";console.warn(`[CreateService Error] ${c} failed with args:`,e);return}};function I(t){return t}function T({endpoints:t,basePath:e,fetcher:o}){return Object.keys(t).reduce((c,i)=>(c[i]=async(...n)=>{const{url:s,body:a,headers:d,method:h,params:y,fetchOpts:P,transform:b}=t[i];let m=typeof s=="function"?await l(s,...n):s;if(typeof m!="string")throw console.error("[Create Service Error]: URL must be a string or a function returning a string",m),new Error(`URL must be a string or a function returning a string for method ${i}.`);e&&(m=u(e,m));const J=typeof a=="function"?await l(a,...n):h!=="GET"?O(n):void 0,N=typeof d=="function"?await l(d,...n):d,E=typeof y=="function"?await l(y,...n):y,D={...P||{},body:J,headers:N,method:h};E&&(m=S(m,E));const R=await o.fetch(m,D);return b?b(R):R},c),{})}class g extends Error{constructor(){super(...arguments);w(this,"response");w(this,"data")}}async function U(t,e){const{serializeBody:o=!0,method:c,...i}=e||{},n=c??"GET",s={...i,method:n,headers:{"Content-type":"application/json",...i.headers},body:n==="GET"?void 0:o?JSON.stringify(e==null?void 0:e.body):e==null?void 0:e.body};try{const a=await fetch(t,s);if(a.status<200||a.status>300){let h;try{h=await a.json()}catch{h={error:"JSON_ERROR_NOT_RECEIVED"}}const y=new g(h.error);throw y.response=a,y.data=h,y}return await a.json()}catch(a){throw console.error(">>> err",a),a}}const j=(t,{retries:e=2,onTryError:o,onFail:c}={})=>async(...n)=>{let s=0;for(;s<e;)try{return await t(...n)}catch(a){if(s<e)try{await(o==null?void 0:o(a,n,s))}catch(d){throw await(c==null?void 0:c(d,n)),d}if(s+=1,s>=e)throw await(c==null?void 0:c(a,n)),a}},p=(t,e)=>{try{return JSON.parse(t)}catch{return e}},$=(t,e)=>{const o=localStorage.getItem(t);return p(o)??e},L=(t,e)=>{const o=JSON.stringify(e);localStorage.setItem(t,o)},C=t=>{localStorage.removeItem(t)};r.ResponseError=g,r.combinePathWithBase=u,r.createEndpoint=I,r.createService=T,r.fetchJSON=U,r.getItem=$,r.paramsToURLSearchParams=f,r.removeItem=C,r.removeQueryParam=v,r.setItem=L,r.setQueryParams=S,r.tryParse=p,r.withRetries=j,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
|
|
2
|
+
//# sourceMappingURL=index.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.cjs","sources":["../src/combinePathWithBase.ts","../src/url-helpers.ts","../src/create-service.ts","../src/fetchJSON.ts","../src/withRetries.ts","../src/try-parse.ts","../src/ls.ts"],"sourcesContent":["export const combinePathWithBase = (host: string, path: string) => {\n host = host.replace(/\\/$/, '');\n path = path.replace(/^\\//, '');\n return `${host}/${path}`;\n};\n","export const paramsToURLSearchParams = <T extends object>(\n params: T,\n urlParams?: URLSearchParams,\n) => {\n const urlSearchParams = urlParams ?? new URLSearchParams();\n\n const keys = Object.keys(params);\n keys.forEach((key) => {\n const val = params[key as keyof T];\n if (typeof val !== 'undefined') {\n if (Array.isArray(val)) {\n val.forEach((v) => {\n urlSearchParams.append(key, v);\n });\n } else {\n urlSearchParams.set(key, `${val}`);\n }\n }\n });\n\n return urlSearchParams;\n};\n\nexport const setQueryParams = <T extends object>(url: string, params: T) => {\n const [baseString, searchString] = url.split(/\\?/);\n\n const urlParams = new URLSearchParams(searchString);\n const mergeParams = paramsToURLSearchParams(params, urlParams);\n\n const parts = [baseString];\n\n const paramsToAdd = mergeParams.toString();\n\n if (paramsToAdd.length > 0) {\n parts.push(`?${paramsToAdd}`);\n }\n\n return parts.join('');\n};\n\nexport const removeQueryParam = (url: string, params: string[]) => {\n const [baseString, searchString] = url.split(/\\?/);\n\n const urlParams = new URLSearchParams(searchString);\n\n params.forEach((param) => {\n urlParams.delete(param);\n });\n\n const parts = [baseString];\n\n const paramsToAdd = urlParams.toString();\n\n if (paramsToAdd.length > 0) {\n parts.push(`?${paramsToAdd}`);\n }\n\n return parts.join('');\n};\n","import { combinePathWithBase } from './combinePathWithBase';\nimport { Fetcher, FetchOptions } from './fetch-types';\nimport { setQueryParams } from './url-helpers';\n\nconst getDefaultData = (args: any[] = []) => (args.length > 0 ? args[0] : undefined);\n\nconst tryCall = async (fn: Function, ...args: any[]) => {\n try {\n return await fn(...args);\n } catch (err) {\n const fnName = fn ? fn.name : 'anonymous';\n console.warn(`[CreateService Error] ${fnName} failed with args:`, args);\n return undefined;\n }\n};\n\n// ─── Legacy types (kept for backward compat with createService<T>) ───\n\nexport interface Fn<T extends Array<any>> {\n (...args: T): string;\n}\n\nexport type ServiceFn = (...args: any[]) => Promise<any>;\n\nexport type StringOrFn<T extends ServiceFn> = string | Fn<Parameters<T>>;\nexport type ObjectOrFn<T extends ServiceFn> = object | Fn<Parameters<T>>;\n\nexport interface Descriptor<K extends ServiceFn> {\n url: StringOrFn<K>;\n method?: HttpMethod;\n body?: ObjectOrFn<K>;\n headers?: ObjectOrFn<K>;\n params?: ObjectOrFn<K>;\n fetchOpts?: FetchOptions;\n transform?: (data: any) => any;\n}\n\nexport type ServiceDescriptor<T> = {\n [K in keyof T]: Descriptor<T[K] extends ServiceFn ? T[K] : never>;\n};\n\nexport interface CreateServiceArgs<T> {\n endpoints: ServiceDescriptor<T>;\n basePath?: string;\n fetcher: Fetcher;\n}\n\n// ─── New inferred API types ───\n\nexport type HttpMethod =\n | 'GET'\n | 'POST'\n | 'PUT'\n | 'DELETE'\n | 'PATCH'\n | 'HEAD'\n | 'OPTIONS'\n | 'TRACE'\n | 'CONNECT';\n\nexport interface DescriptorBase {\n url: string | ((...args: any[]) => string);\n method?: HttpMethod;\n body?: object | ((...args: any[]) => any);\n headers?: object | ((...args: any[]) => any);\n params?: object | ((...args: any[]) => any);\n fetchOpts?: FetchOptions;\n transform?: (data: any) => any;\n}\n\n// ─── createEndpoint helper ───\n\n/** @internal Type brand symbol — not present at runtime */\ndeclare const ENDPOINT_TYPES: unique symbol;\n\n/** A descriptor branded with explicit TResponse, TArgs, TError types */\nexport interface TypedEndpoint<\n TResponse = any,\n TArgs = void,\n TError = Error,\n> extends DescriptorBase {\n readonly [ENDPOINT_TYPES]: { args: TArgs; response: TResponse; error: TError };\n}\n\n/** Configuration accepted by createEndpoint */\nexport interface EndpointConfig<TResponse, TArgs> {\n url: string | ((args: TArgs) => string);\n method?: HttpMethod;\n body?: object | ((args: TArgs) => any);\n headers?: object | ((args: TArgs) => any);\n params?: object | ((args: TArgs) => any);\n fetchOpts?: FetchOptions;\n transform?: (data: any) => TResponse;\n}\n\n/**\n * Define a typed endpoint with explicit response/input/error types.\n *\n * @example\n * ```ts\n * // No args:\n * createEndpoint<User[]>({ url: '/users' })\n * // service method: () => Promise<User[]>\n *\n * // With args:\n * createEndpoint<User, { id: string }>({\n * url: ({ id }) => `/users/${id}`,\n * })\n * // service method: (args: { id: string }) => Promise<User>\n * ```\n */\nexport function createEndpoint<TResponse = any, TArgs = void, TError = Error>(\n config: EndpointConfig<TResponse, TArgs>,\n): TypedEndpoint<TResponse, TArgs, TError> {\n return config as any;\n}\n\n// ─── Type inference ───\n\n/** Infer the service method args from a descriptor */\nexport type InferArgs<D> =\n // 1. Branded endpoint — use the explicit TArgs\n D extends { readonly [ENDPOINT_TYPES]: { args: infer A } }\n ? [A] extends [void]\n ? []\n : [args: A]\n : // 2. Infer from descriptor functions (url → body → params → headers)\n D extends { url: (...args: infer P) => any }\n ? P\n : D extends { body: (...args: infer P) => any }\n ? P\n : D extends { params: (...args: infer P) => any }\n ? P\n : D extends { headers: (...args: infer P) => any }\n ? P\n : any[];\n\n/** Infer the service method return type from a descriptor */\nexport type InferReturn<D> =\n // 1. Branded endpoint — use the explicit TResponse\n D extends { readonly [ENDPOINT_TYPES]: { response: infer R } }\n ? R\n : // 2. Infer from transform return type\n D extends { transform: (data: any) => infer R }\n ? R\n : any;\n\n/** Infer the error type from a branded descriptor */\nexport type InferError<D> = D extends {\n readonly [ENDPOINT_TYPES]: { error: infer E };\n}\n ? E\n : Error;\n\nexport type InferService<T extends Record<string, DescriptorBase>> = {\n [K in keyof T]: (...args: InferArgs<T[K]>) => Promise<InferReturn<T[K]>>;\n};\n\n// ─── Overloads ───\n\n/** Create a service with types inferred from the endpoint descriptors */\nexport function createService<const T extends Record<string, DescriptorBase>>(args: {\n endpoints: T;\n basePath?: string;\n fetcher: Fetcher;\n}): InferService<T>;\n\n/** @deprecated Use the inferred API (call without explicit generic) */\nexport function createService<T>(args: CreateServiceArgs<T>): T;\n\n// ─── Implementation ───\n\nexport function createService({ endpoints, basePath, fetcher }: any): any {\n return Object.keys(endpoints).reduce((service: any, serviceName: string) => {\n service[serviceName] = async (...args: any[]) => {\n const { url, body, headers, method, params, fetchOpts, transform } = endpoints[serviceName];\n\n let urlToUse: string = typeof url === 'function' ? await tryCall(url, ...args) : url;\n if (typeof urlToUse !== 'string') {\n console.error(\n '[Create Service Error]: URL must be a string or a function returning a string',\n urlToUse,\n );\n throw new Error(\n `URL must be a string or a function returning a string for method ${serviceName}.`,\n );\n }\n\n // TODO: detect absolute urlToUse and prevent adding the base path\n if (basePath) {\n urlToUse = combinePathWithBase(basePath, urlToUse);\n }\n\n const getDefault = () => (method !== 'GET' ? getDefaultData(args) : undefined);\n\n // if data is a function we pass all the arguments to the function so it creates\n // the payload with all the provided arguments, expecting it to return a single object\n // if the data is not a function then we get the first argument passed to the function as the\n // data payload. If this is an object it will be serialized using json stringify before sending it\n const bodyToUse = typeof body === 'function' ? await tryCall(body, ...args) : getDefault();\n const headersToUse =\n typeof headers === 'function' ? await tryCall(headers, ...args) : headers;\n\n const paramsToUse = typeof params === 'function' ? await tryCall(params, ...args) : params;\n const opts = { ...(fetchOpts || {}), body: bodyToUse, headers: headersToUse, method };\n\n if (paramsToUse) {\n urlToUse = setQueryParams(urlToUse, paramsToUse);\n }\n\n const result = await fetcher.fetch(urlToUse, opts);\n return transform ? transform(result) : result;\n };\n\n return service;\n }, {} as any);\n}\n","import { FetchOptions, FetchURL, SerializeBodyProps } from './fetch-types';\n\nexport interface ErrorData {\n error: string;\n}\nexport class ResponseError extends Error {\n response?: Response;\n\n data?: ErrorData;\n}\n\nexport async function fetchJSON<T, K>(url: FetchURL, params?: SerializeBodyProps<K>): Promise<T> {\n const { serializeBody = true, method, ...rest } = params || {};\n const fetchMethod = method ?? 'GET';\n\n const props: FetchOptions = {\n ...rest,\n method: fetchMethod,\n headers: {\n 'Content-type': 'application/json',\n ...rest.headers,\n },\n body:\n fetchMethod === 'GET'\n ? undefined\n : ((serializeBody ? JSON.stringify(params?.body) : params?.body) as FetchOptions['body']),\n };\n\n try {\n const resp = await fetch(url, props);\n if (resp.status < 200 || resp.status > 300) {\n let data: ErrorData;\n try {\n data = await resp.json();\n } catch (parseError) {\n data = { error: 'JSON_ERROR_NOT_RECEIVED' };\n }\n const error = new ResponseError(data.error);\n error.response = resp;\n error.data = data;\n\n throw error;\n }\n const data = await resp.json();\n return data;\n } catch (err) {\n console.error('>>> err', err);\n throw err;\n }\n}\n\nexport type FetchFn = typeof fetchJSON;\n","export interface WithRetriesOpts<K> {\n retries?: number;\n onFail?: (error: Error, args: K) => Promise<void> | void;\n onTryError?: (error: Error, args: K, attemptIndex: number) => Promise<void> | void;\n}\n\nexport const withRetries = <K extends any[], T>(\n fn: (...args: K) => Promise<T>,\n { retries = 2, onTryError, onFail }: WithRetriesOpts<K> = {},\n) => {\n const retFn = async (...args: K) => {\n let attemptIndex = 0;\n\n while (attemptIndex < retries) {\n try {\n const ret = await fn(...args); // eslint-disable-line no-await-in-loop\n\n return ret as T;\n } catch (err) {\n if (attemptIndex < retries) {\n try {\n await onTryError?.(err as Error, args, attemptIndex); // eslint-disable-line no-await-in-loop\n } catch (error) {\n await onFail?.(error as Error, args); // eslint-disable-line no-await-in-loop\n throw error;\n }\n }\n attemptIndex += 1;\n if (attemptIndex >= retries) {\n await onFail?.(err as Error, args); // eslint-disable-line no-await-in-loop\n throw err;\n }\n }\n }\n };\n\n return retFn as (...args: K) => Promise<T>;\n};\n","export const tryParse = <T>(val: string | null, def?: T) => {\n try {\n return JSON.parse(val!) as T;\n } catch (err) {\n return def;\n }\n};\n","import { tryParse } from './try-parse';\n\nexport const getItem = <T>(key: string, defaultValue: T) => {\n const entity = localStorage.getItem(key);\n\n return tryParse(entity) ?? defaultValue;\n};\n\nexport const setItem = <T>(key: string, entity: T) => {\n const str = JSON.stringify(entity);\n\n localStorage.setItem(key, str);\n};\n\nexport const removeItem = (key: string) => {\n localStorage.removeItem(key);\n};\n"],"names":["combinePathWithBase","host","path","paramsToURLSearchParams","params","urlParams","urlSearchParams","key","val","v","setQueryParams","url","baseString","searchString","mergeParams","parts","paramsToAdd","removeQueryParam","param","getDefaultData","args","tryCall","fn","fnName","createEndpoint","config","createService","endpoints","basePath","fetcher","service","serviceName","body","headers","method","fetchOpts","transform","urlToUse","bodyToUse","headersToUse","paramsToUse","opts","result","ResponseError","__publicField","fetchJSON","serializeBody","rest","fetchMethod","props","resp","data","error","err","withRetries","retries","onTryError","onFail","attemptIndex","tryParse","def","getItem","defaultValue","entity","setItem","str","removeItem"],"mappings":"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"}
|