klaim 1.7.51 → 1.8.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/.github/workflows/update-minor.yml +55 -0
- package/README.md +159 -133
- package/deno.json +1 -1
- package/dist/klaim.cjs +1 -1
- package/dist/klaim.es.js +191 -170
- package/dist/klaim.umd.js +1 -1
- package/package.json +1 -1
- package/src/core/Element.ts +221 -195
- package/src/core/Klaim.ts +178 -145
- package/src/core/Registry.ts +3 -4
- package/tests/09.pagination.test.ts +52 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Manual Minor Update & Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
update-and-release-minor:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write
|
|
11
|
+
contents: write
|
|
12
|
+
packages: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: '20'
|
|
23
|
+
registry-url: 'https://registry.npmjs.org'
|
|
24
|
+
|
|
25
|
+
- name: Configure NPM Authentication
|
|
26
|
+
run: |
|
|
27
|
+
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
|
|
28
|
+
npm whoami
|
|
29
|
+
env:
|
|
30
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
31
|
+
|
|
32
|
+
- name: Configure Git
|
|
33
|
+
run: |
|
|
34
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
35
|
+
git config --global user.name "GitHub Actions Bot"
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: npm install
|
|
39
|
+
|
|
40
|
+
- name: Update ALL dependencies
|
|
41
|
+
run: |
|
|
42
|
+
npx npm-check-updates -u
|
|
43
|
+
npm install
|
|
44
|
+
|
|
45
|
+
- name: Test and build
|
|
46
|
+
run: |
|
|
47
|
+
npm run test
|
|
48
|
+
npm run build
|
|
49
|
+
|
|
50
|
+
- name: Release Minor
|
|
51
|
+
run: npm run release:minor
|
|
52
|
+
env:
|
|
53
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
54
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
55
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
CHANGED
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
- **Retry Mechanism**: Automatically retry failed requests to enhance reliability.
|
|
38
38
|
- **TypeScript Support**: Fully typed for enhanced code quality and developer experience.
|
|
39
39
|
- **Response Validation**: Validate responses using schemas for increased reliability and consistency.
|
|
40
|
+
- **Pagination**: Handle paginated requests easily with support for both page and offset based pagination.
|
|
40
41
|
|
|
41
42
|
## ⌛ Next features
|
|
42
43
|
|
|
43
|
-
- Pagination (Version: 1.8)
|
|
44
44
|
- Rate Limiting (Version: 1.9)
|
|
45
45
|
- Login (Version: 1.10)
|
|
46
46
|
- Time Out (Version: 1.11)
|
|
@@ -73,55 +73,57 @@ import {Api, Route} from 'klaim';
|
|
|
73
73
|
|
|
74
74
|
// Your simple Todo type
|
|
75
75
|
type Todo = {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
userId: number;
|
|
77
|
+
id: number;
|
|
78
|
+
title: string;
|
|
79
|
+
completed: boolean;
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
// Create a new API with the name "hello" and the base URL "https://jsonplaceholder.typicode.com/"
|
|
83
83
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
// Define routes for the API
|
|
85
|
+
Route.get<Todo[]>("listTodos", "todos");
|
|
86
|
+
Route.get<Todo>("getTodo", "todos/[id]");
|
|
87
|
+
Route.post<Todo>("addTodo", "todos");
|
|
88
88
|
});
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
### Route Definition
|
|
92
92
|
|
|
93
|
-
Routes represent endpoints in your API and can be defined with different HTTP methods. Routes can include parameters and
|
|
93
|
+
Routes represent endpoints in your API and can be defined with different HTTP methods. Routes can include parameters and
|
|
94
|
+
custom configurations:
|
|
94
95
|
|
|
95
96
|
```typescript
|
|
96
97
|
Api.create("api", "https://api.example.com", () => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
98
|
+
// Basic GET route
|
|
99
|
+
Route.get("listUsers", "/users");
|
|
100
|
+
|
|
101
|
+
// GET route with URL parameter
|
|
102
|
+
Route.get("getUser", "/users/[id]");
|
|
103
|
+
|
|
104
|
+
// POST route with custom headers and body
|
|
105
|
+
Route.post("createUser", "/users", {
|
|
106
|
+
"Content-Type": "application/json"
|
|
107
|
+
}, {userId: 1, name: "John Doe"});
|
|
108
|
+
|
|
109
|
+
// PUT route with parameter
|
|
110
|
+
Route.put("updateUser", "/users/[id]");
|
|
111
|
+
|
|
112
|
+
// DELETE route
|
|
113
|
+
Route.delete("deleteUser", "/users/[id]");
|
|
114
|
+
|
|
115
|
+
// PATCH route
|
|
116
|
+
Route.patch("updateUserStatus", "/users/[id]/status");
|
|
117
|
+
|
|
118
|
+
// OPTIONS route
|
|
119
|
+
Route.options("userOptions", "/users");
|
|
119
120
|
});
|
|
120
121
|
```
|
|
121
122
|
|
|
122
123
|
### Groups
|
|
123
124
|
|
|
124
|
-
Klaim provides powerful grouping capabilities for both APIs and routes. Groups can be used to organize related elements,
|
|
125
|
+
Klaim provides powerful grouping capabilities for both APIs and routes. Groups can be used to organize related elements,
|
|
126
|
+
share configuration, and maintain a clean structure in your application.
|
|
125
127
|
|
|
126
128
|
#### API Groups
|
|
127
129
|
|
|
@@ -132,21 +134,21 @@ import {Group, Api, Route} from 'klaim';
|
|
|
132
134
|
|
|
133
135
|
// Create a group for user-related services
|
|
134
136
|
Group.create("userServices", () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
137
|
+
// Authentication API
|
|
138
|
+
Api.create("auth", "https://auth.example.com", () => {
|
|
139
|
+
Route.post("login", "/login");
|
|
140
|
+
Route.post("register", "/register");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// User Management API
|
|
144
|
+
Api.create("users", "https://users.example.com", () => {
|
|
145
|
+
Route.get("list", "/users");
|
|
146
|
+
Route.get("getOne", "/users/[id]");
|
|
147
|
+
});
|
|
146
148
|
}).withRetry(3); // Apply retry mechanism to all APIs in the group
|
|
147
149
|
|
|
148
150
|
// Access grouped APIs
|
|
149
|
-
await Klaim.userServices.auth.login({}, {
|
|
151
|
+
await Klaim.userServices.auth.login({}, {username: "user", password: "pass"});
|
|
150
152
|
await Klaim.userServices.users.list();
|
|
151
153
|
```
|
|
152
154
|
|
|
@@ -156,23 +158,23 @@ Organize routes within an API into logical groups:
|
|
|
156
158
|
|
|
157
159
|
```typescript
|
|
158
160
|
Api.create("hello", "https://api.example.com/", () => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
// Group user-related routes
|
|
162
|
+
Group.create("users", () => {
|
|
163
|
+
Route.get<User[]>("list", "/users");
|
|
164
|
+
Route.get<User>("getOne", "/users/[id]");
|
|
165
|
+
Route.post<User>("create", "/users");
|
|
166
|
+
}).withCache(60); // Cache all user routes for 60 seconds
|
|
167
|
+
|
|
168
|
+
// Group product-related routes
|
|
169
|
+
Group.create("products", () => {
|
|
170
|
+
Route.get("list", "/products");
|
|
171
|
+
Route.get("getOne", "/products/[id]");
|
|
172
|
+
});
|
|
171
173
|
});
|
|
172
174
|
|
|
173
175
|
// Use grouped routes
|
|
174
176
|
const users = await Klaim.hello.users.list();
|
|
175
|
-
const product = await Klaim.hello.products.getOne({
|
|
177
|
+
const product = await Klaim.hello.products.getOne({id: 1});
|
|
176
178
|
```
|
|
177
179
|
|
|
178
180
|
#### Nested Groups
|
|
@@ -181,32 +183,32 @@ Create complex hierarchies with nested groups:
|
|
|
181
183
|
|
|
182
184
|
```typescript
|
|
183
185
|
Group.create("services", () => {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
186
|
+
// Internal services group
|
|
187
|
+
Group.create("internal", () => {
|
|
188
|
+
Api.create("logs", "https://logs.internal.example.com", () => {
|
|
189
|
+
Route.post("write", "/logs");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
Api.create("metrics", "https://metrics.internal.example.com", () => {
|
|
193
|
+
Route.post("track", "/metrics");
|
|
194
|
+
});
|
|
195
|
+
}).withRetry(5); // More retries for internal services
|
|
196
|
+
|
|
197
|
+
// External services group
|
|
198
|
+
Group.create("external", () => {
|
|
199
|
+
Api.create("weather", "https://api.weather.com", () => {
|
|
200
|
+
Route.get("forecast", "/forecast/[city]");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
Api.create("geocoding", "https://api.geocoding.com", () => {
|
|
204
|
+
Route.get("search", "/search/[query]");
|
|
205
|
+
});
|
|
206
|
+
}).withCache(300); // Cache external services longer
|
|
205
207
|
});
|
|
206
208
|
|
|
207
209
|
// Access nested groups
|
|
208
|
-
await Klaim.services.internal.logs.write({}, {
|
|
209
|
-
await Klaim.services.external.weather.forecast({
|
|
210
|
+
await Klaim.services.internal.logs.write({}, {message: "Log entry"});
|
|
211
|
+
await Klaim.services.external.weather.forecast({city: "Paris"});
|
|
210
212
|
```
|
|
211
213
|
|
|
212
214
|
#### Group Configuration
|
|
@@ -215,22 +217,22 @@ Groups can share configuration among all their members:
|
|
|
215
217
|
|
|
216
218
|
```typescript
|
|
217
219
|
Group.create("apis", () => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
Api.create("service1", "https://api1.example.com", () => {
|
|
221
|
+
Route.get("test", "/test");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
Api.create("service2", "https://api2.example.com", () => {
|
|
225
|
+
Route.get("test", "/test");
|
|
226
|
+
});
|
|
225
227
|
})
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
228
|
+
.withCache(60) // Enable caching for all APIs
|
|
229
|
+
.withRetry(3) // Enable retries for all APIs
|
|
230
|
+
.before(({config}) => { // Add authentication for all APIs
|
|
231
|
+
config.headers.Authorization = `Bearer ${getToken()}`;
|
|
232
|
+
})
|
|
233
|
+
.after(({data}) => { // Process all responses
|
|
234
|
+
logResponse(data);
|
|
235
|
+
});
|
|
234
236
|
```
|
|
235
237
|
|
|
236
238
|
### Request Handling
|
|
@@ -258,16 +260,16 @@ and `after` middleware to process responses:
|
|
|
258
260
|
|
|
259
261
|
```typescript
|
|
260
262
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
263
|
+
// With before middleware
|
|
264
|
+
Route.get<Todo>("getRandomTodo", "todos")
|
|
265
|
+
.before(({url}) => {
|
|
266
|
+
const random = Math.floor(Math.random() * 10) + 1;
|
|
267
|
+
return {url: `${url}/${random}`};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// With after middleware
|
|
271
|
+
Route.get<Todo>("getFirstTodo", "todos")
|
|
272
|
+
.after(({data: [first]}) => ({data: first}));
|
|
271
273
|
});
|
|
272
274
|
```
|
|
273
275
|
|
|
@@ -281,7 +283,7 @@ import {Hook} from 'klaim';
|
|
|
281
283
|
|
|
282
284
|
// Subscribe to the "hello.getFirstTodo" hook
|
|
283
285
|
Hook.subscribe("hello.getFirstTodo", ({url}) => {
|
|
284
|
-
|
|
286
|
+
console.log(`Requesting ${url}`);
|
|
285
287
|
});
|
|
286
288
|
```
|
|
287
289
|
|
|
@@ -296,14 +298,14 @@ You can enable caching on individual routes:
|
|
|
296
298
|
|
|
297
299
|
```typescript
|
|
298
300
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
+
// Get a list of todos with default cache duration (20 seconds)
|
|
302
|
+
Route.get<Todo[]>("listTodos", "todos").withCache();
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
+
// Get a specific todo by id with custom cache duration (300 seconds)
|
|
305
|
+
Route.get<Todo>("getTodo", "todos/[id]").withCache(300);
|
|
304
306
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
+
// Add a new todo (no cache)
|
|
308
|
+
Route.post<Todo>("addTodo", "todos");
|
|
307
309
|
});
|
|
308
310
|
```
|
|
309
311
|
|
|
@@ -315,10 +317,10 @@ You can also enable caching for all routes defined within an API:
|
|
|
315
317
|
|
|
316
318
|
```typescript
|
|
317
319
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
// Define routes for the API
|
|
321
|
+
Route.get<Todo[]>("listTodos", "todos");
|
|
322
|
+
Route.get<Todo>("getTodo", "todos/[id]");
|
|
323
|
+
Route.post<Todo>("addTodo", "todos");
|
|
322
324
|
}).withCache(); // Enable default cache duration (20 seconds) for all routes
|
|
323
325
|
```
|
|
324
326
|
|
|
@@ -333,14 +335,14 @@ Enable retry on individual routes:
|
|
|
333
335
|
|
|
334
336
|
```typescript
|
|
335
337
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
// Get a list of todos with retry mechanism (default: 2)
|
|
339
|
+
Route.get<Todo[]>("listTodos", "todos").withRetry();
|
|
338
340
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
// Get a specific todo by id with retry mechanism (specified to 5)
|
|
342
|
+
Route.get<Todo>("getTodo", "todos/[id]").withRetry(5);
|
|
341
343
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
+
// Add a new todo (no retry)
|
|
345
|
+
Route.post<Todo>("addTodo", "todos");
|
|
344
346
|
});
|
|
345
347
|
```
|
|
346
348
|
|
|
@@ -350,10 +352,10 @@ Enable retry for all routes defined within an API:
|
|
|
350
352
|
|
|
351
353
|
```typescript
|
|
352
354
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
// Define routes for the API
|
|
356
|
+
Route.get<Todo[]>("listTodos", "todos");
|
|
357
|
+
Route.get<Todo>("getTodo", "todos/[id]");
|
|
358
|
+
Route.post<Todo>("addTodo", "todos");
|
|
357
359
|
}).withRetry();
|
|
358
360
|
```
|
|
359
361
|
|
|
@@ -376,15 +378,15 @@ import * as yup from 'yup';
|
|
|
376
378
|
|
|
377
379
|
// Define the schema using Yup
|
|
378
380
|
const todoSchema = yup.object().shape({
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
userId: yup.number().required(),
|
|
382
|
+
id: yup.number().min(1).max(10).required(),
|
|
383
|
+
title: yup.string().required(),
|
|
384
|
+
completed: yup.boolean().required()
|
|
383
385
|
});
|
|
384
386
|
|
|
385
387
|
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
+
// Get a specific todo by id with validation
|
|
389
|
+
Route.get<Todo>("getTodo", "todos/[id]").validate(todoSchema);
|
|
388
390
|
});
|
|
389
391
|
|
|
390
392
|
// This request will fail because the id is out of range
|
|
@@ -394,6 +396,30 @@ const todoFail = await Klaim.hello.getTodo<Todo>({id: 15});
|
|
|
394
396
|
const todo = await Klaim.hello.getTodo<Todo>({id: 1});
|
|
395
397
|
```
|
|
396
398
|
|
|
399
|
+
### Pagination
|
|
400
|
+
|
|
401
|
+
Configure pagination for routes that require it:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Basic usage with custom limit and offset parameter
|
|
405
|
+
Api.create("api", "https://api.example.com", () => {
|
|
406
|
+
Route.get("list", "/items").withPagination({
|
|
407
|
+
limit: 20, // Items per page
|
|
408
|
+
page: 1, // Default page number
|
|
409
|
+
pageParam: "offset", // Parameter name for page/offset or any other custom parameter
|
|
410
|
+
limitParam: "limit" // Parameter name for limit
|
|
411
|
+
}); // All options are optional
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Using paginated endpoints
|
|
415
|
+
const page1 = await Klaim.api.list(); // First page
|
|
416
|
+
const page2 = await Klaim.api.list(2); // Second page
|
|
417
|
+
const customPage = await Klaim.api.list(5); // Fifth page
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
⚠️ **Note**: The pagination feature simplifies your pagination parameters, but your API/backend needs to respond to these
|
|
421
|
+
parameters. Klaim does not handle the pagination logic, only the parameters management.
|
|
422
|
+
|
|
397
423
|
## 🔗 Links
|
|
398
424
|
|
|
399
425
|
- [NPM](https://www.npmjs.com/package/klaim)
|
package/deno.json
CHANGED
package/dist/klaim.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var O=Object.defineProperty;var U=(n,t,e)=>t in n?O(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var o=(n,t,e)=>U(n,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function E(n){return n.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_","")).replace(/(^\w)/,t=>t.toLowerCase())}function D(n){return n.trim().replace(/^\/|\/$/g,"")}const j={page:1,pageParam:"page",limit:10,limitParam:"limit"};class ${constructor(t,e,a,r={}){o(this,"type");o(this,"name");o(this,"url");o(this,"headers");o(this,"parent");o(this,"method");o(this,"arguments",new Set);o(this,"schema");o(this,"pagination");o(this,"callbacks",{before:null,after:null,call:null});o(this,"cache",!1);o(this,"retry",!1);o(this,"withCache",(t=20)=>(this.cache=t,this));o(this,"withRetry",(t=2)=>(this.retry=t,this));this.type=t,this.name=E(e),this.name!==e&&console.warn(`Name "${e}" has been camelCased to "${this.name}"`),this.url=D(a),this.headers=r||{}}before(t){return this.callbacks.before=t,this}after(t){return this.callbacks.after=t,this}onCall(t){return this.callbacks.call=t,this}withPagination(t={}){return this.pagination={...j,...t},this}}const p=class p{constructor(){o(this,"cache");this.cache=new Map}static get i(){return p._instance||(p._instance=new p),p._instance}set(t,e,a=0){const r=Date.now()+a;this.cache.set(t,{data:e,expiry:r})}has(t){const e=this.cache.get(t);return e?Date.now()>e.expiry?(this.cache.delete(t),!1):!0:!1}get(t){return this.has(t)?this.cache.get(t).data:null}};o(p,"_instance");let d=p;function x(n){let a=2166136261;for(let i=0;i<n.length;i++)a^=n.charCodeAt(i),a*=16777619;let r=(a>>>0).toString(16).padStart(8,"0");for(;r.length<32;)a^=r.charCodeAt(r.length%r.length),a*=16777619,r+=(a>>>0).toString(16).padStart(8,"0");return r.substring(0,32)}async function H(n,t,e){const a=`${n.toString()}${JSON.stringify(t)}`,r=x(a);if(d.i.has(r))return d.i.get(r);const c=await(await fetch(n,t)).json();return d.i.set(r,c,e),c}class A{static subscribe(t,e){this._callbacks.set(t,e)}static run(t){const e=this._callbacks.get(t);e&&e()}}o(A,"_callbacks",new Map);const C={};function I(n,t){return async(...e)=>{if(t.pagination){const[i=0,c={},l={}]=e;return F(n,t,i,c,l)}const[a={},r={}]=e;return F(n,t,void 0,a,r)}}async function F(n,t,e,a={},r={}){const i=n.split(".");let c;for(let g=0;g<i.length;g++){const y=i[g];if(c=s.i.getApi(y),c)break}if(!t||!c||t.type!=="route"||c.type!=="api")throw new Error(`Invalid path: ${n}.${t.name}`);let l=W(`${c.url}/${t.url}`,t,a);if(t.pagination&&typeof e<"u"){const{pageParam:g="page",limit:y=10,limitParam:K="limit"}=t.pagination,b=new URLSearchParams;b.append(g,String(e)),b.append(K,String(y));const G=l.includes("?")?"&":"?";l=`${l}${G}${b.toString()}`}let u={};r&&t.method!=="GET"&&(u.body=JSON.stringify(r)),u.headers={"Content-Type":"application/json",...c.headers,...t.headers},u.method=t.method;const{beforeRoute:f,beforeApi:P,beforeUrl:m,beforeConfig:T}=J({route:t,api:c,url:l,config:u});l=m,u=T,s.updateElement(P),s.updateElement(f);let w=await M(c,t,l,u);t.schema&&"validate"in t.schema&&(w=await t.schema.validate(w));const{afterRoute:N,afterApi:R,afterData:v}=z({route:t,api:c,response:w,data:w});return s.updateElement(R),s.updateElement(N),A.run(`${c.name}.${t.name}`),v}async function L(n,t,e,a){return n?await H(t,e,a.cache):await(await fetch(t,e)).json()}async function M(n,t,e,a){var f,P;const r=n.cache||t.cache,i=t.retry||n.retry||0;let c,l=!1,u=0;for(;u<=i&&!l;)try{(f=t.callbacks)!=null&&f.call?t.callbacks.call({}):(P=n.callbacks)!=null&&P.call&&n.callbacks.call({}),c=await L(!!r,e,a,n),l=!0}catch(m){if(u++,u>i)throw m.message=`Failed to fetch ${e} after ${i} attempts`,m}return c}function W(n,t,e){let a=n;return t.arguments.forEach(r=>{if(e[r]===void 0)throw new Error(`Argument ${r} is missing`);a=a.replace(`[${r}]`,e[r])}),a}function J({route:n,api:t,url:e,config:a}){var i,c;const r=(c=(i=n.callbacks).before)==null?void 0:c.call(i,{route:n,api:t,url:e,config:a});return{beforeRoute:(r==null?void 0:r.route)||n,beforeApi:(r==null?void 0:r.api)||t,beforeUrl:(r==null?void 0:r.url)||e,beforeConfig:(r==null?void 0:r.config)||a}}function z({route:n,api:t,response:e,data:a}){var i,c;const r=(c=(i=n.callbacks).after)==null?void 0:c.call(i,{route:n,api:t,response:e,data:a});return{afterRoute:(r==null?void 0:r.route)||n,afterApi:(r==null?void 0:r.api)||t,afterResponse:(r==null?void 0:r.response)||e,afterData:(r==null?void 0:r.data)||a}}const h=class h{constructor(){o(this,"_elements",new Map);o(this,"_currentParent",null)}static get i(){return h._instance||(h._instance=new h),h._instance}registerElement(t){const e=this._currentParent;e&&(t.parent=this.getFullPath(e));const a=this.getElementKey(t);if(this._elements.set(a,t),t.type==="api"||t.type==="group"){let r=C;if(e){const i=this.getFullPath(e).split(".");for(const c of i)r[c]||(r[c]={}),r=r[c]}r[t.name]||(r[t.name]={})}}getCurrentParent(){return this._currentParent}setCurrentParent(t){const e=this._elements.get(t);if(!e||e.type!=="api"&&e.type!=="group")throw new Error(`Element ${t} not found or not a valid parent type`);this._currentParent=e}clearCurrentParent(){this._currentParent=null}registerRoute(t){if(!this._currentParent)throw new Error("No current parent set, use Route only inside Api or Group create callback");t.parent=this.getFullPath(this._currentParent);const e=this.getElementKey(t);this._elements.set(e,t),this.addToKlaimRoute(t)}addToKlaimRoute(t){if(!t.parent)return;let e=C;const a=t.parent.split(".");for(const r of a)e[r]||(e[r]={}),e=e[r];e[t.name]=I(t.parent,t)}getElementKey(t){return t?t.parent?`${t.parent}.${t.name}`:t.name:""}getFullPath(t){if(!t)return"";if(!t.parent)return t.name;const e=[t.name];let a=t;for(;a.parent;){const r=this._elements.get(a.parent);if(!r)break;e.unshift(r.name),a=r}return e.join(".")}getRoute(t,e){return this._elements.get(`${t}.${e}`)}getChildren(t){const e=[];return this._elements.forEach(a=>{a.parent===t&&e.push(a)}),e}static updateElement(t){return h.i._elements.get(h.i.getElementKey(t))||t}getApi(t){const e=this._elements.get(t);if(!e){for(const[a,r]of this._elements.entries())if(r.type==="api"&&a.endsWith(`.${t}`))return r;return}return e.type==="api"?e:this.findApi(e)}findApi(t){if(!t||!t.parent)return;const e=t.parent.split(".");for(let a=e.length;a>=0;a--){const r=e.slice(0,a).join("."),i=this._elements.get(r);if((i==null?void 0:i.type)==="api")return i}}};o(h,"_instance");let s=h;class k extends ${static create(t,e,a,r={}){const i=E(t);i!==t&&console.warn(`API name "${t}" has been camelCased to "${i}"`);const c=new k(i,e,r),l=s.i.getCurrentParent();s.i.registerElement(c);const u=l?s.i.getFullPath(l):"",f=u?`${u}.${i}`:i;return s.i.setCurrentParent(f),a(),l?s.i.setCurrentParent(s.i.getFullPath(l)):s.i.clearCurrentParent(),c}constructor(t,e,a={}){super("api",t,e,a)}}class S extends ${static create(t,e){const a=E(t),r=s.i.getCurrentParent(),i=r?s.i.getFullPath(r):"",c=i?`${i}.${a}`:a,l=new S(a,"");a!==t&&console.warn(`Group name "${t}" has been camelCased to "${a}"`),s.i.registerElement(l);const u=s.i.getCurrentParent();return s.i.setCurrentParent(c),e(),u?s.i.setCurrentParent(s.i.getFullPath(u)):s.i.clearCurrentParent(),l}constructor(t,e,a={}){super("group",t,e,a)}withCache(t=20){return super.withCache(t),s.i.getChildren(s.i.getFullPath(this)).forEach(e=>{e.cache||(e.cache=t)}),this}withRetry(t=2){return super.withRetry(t),s.i.getChildren(s.i.getFullPath(this)).forEach(e=>{e.retry||(e.retry=t)}),this}before(t){return super.before(t),s.i.getChildren(s.i.getFullPath(this)).forEach(e=>{e.callbacks.before||(e.callbacks.before=t)}),this}after(t){return super.after(t),s.i.getChildren(s.i.getFullPath(this)).forEach(e=>{e.callbacks.after||(e.callbacks.after=t)}),this}onCall(t){return super.onCall(t),s.i.getChildren(s.i.getFullPath(this)).forEach(e=>{e.callbacks.call||(e.callbacks.call=t)}),this}}class _ extends ${constructor(t,e,a={},r="GET"){super("route",t,e,a),this.method=r,this.detectArguments()}static createRoute(t,e,a={},r){const i=new _(t,e,a,r);return s.i.registerRoute(i),i}static get(t,e,a={}){return this.createRoute(t,e,a,"GET")}static post(t,e,a={}){return this.createRoute(t,e,a,"POST")}static put(t,e,a={}){return this.createRoute(t,e,a,"PUT")}static delete(t,e,a={}){return this.createRoute(t,e,a,"DELETE")}static patch(t,e,a={}){return this.createRoute(t,e,a,"PATCH")}static options(t,e,a={}){return this.createRoute(t,e,a,"OPTIONS")}detectArguments(){const t=this.url.match(/\[([^\]]+)]/g);t&&t.forEach(e=>{const a=e.replace("[","").replace("]","");this.arguments.add(a)})}validate(t){return this.schema=t,this}}exports.Api=k;exports.Group=S;exports.Hook=A;exports.Klaim=C;exports.Registry=s;exports.Route=_;
|