klaim 1.2.33 → 1.4.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 +39 -0
- package/bun.lockb +0 -0
- package/deno.json +38 -38
- package/dist/klaim.cjs +1 -1
- package/dist/klaim.es.js +160 -73
- package/dist/klaim.umd.js +1 -1
- package/package.json +6 -3
- package/src/core/Api.ts +24 -3
- package/src/core/Cache.ts +72 -0
- package/src/core/Klaim.ts +23 -5
- package/src/core/Route.ts +41 -9
- package/src/tools/fetchWithCache.ts +30 -0
- package/src/tools/hashStr.ts +28 -0
- package/tests/01.api.test.ts +60 -0
- package/tests/02.route.test.ts +186 -0
- package/tests/03.hook.test.ts +34 -0
- package/tests/04.klaim.test.ts +96 -0
- package/tests/05.cache.test.ts +46 -0
- package/vite.config.ts +23 -14
- package/src/tools/slugify.ts +0 -17
- package/src/tools/token.ts +0 -14
- package/tests/arguments.test.ts +0 -89
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ experience.
|
|
|
15
15
|
- [Request Handling](#request-handling)
|
|
16
16
|
- [Middleware Usage](#middleware-usage)
|
|
17
17
|
- [Hook Subscription](#hook-subscription)
|
|
18
|
+
- [Caching Requests](#caching-requests)
|
|
18
19
|
- [Links](#-links)
|
|
19
20
|
- [Contributing](#-contributing)
|
|
20
21
|
- [License](#-license)
|
|
@@ -27,6 +28,7 @@ experience.
|
|
|
27
28
|
- **Lightweight**: Minimal footprint for fast load times and minimal performance impact.
|
|
28
29
|
- **Middleware Support**: Easily add middleware to modify requests and responses (`before` and `after`).
|
|
29
30
|
- **Hook System**: Subscribe to hooks to monitor and react to specific events.
|
|
31
|
+
- **Caching**: Enable caching on requests to reduce network load and improve performance.
|
|
30
32
|
- **TypeScript Support**: Fully typed for enhanced code quality and developer experience.
|
|
31
33
|
|
|
32
34
|
## 📥 Installation
|
|
@@ -140,6 +142,43 @@ Hook.subscribe("hello.getFirstTodo", ({url}) => {
|
|
|
140
142
|
});
|
|
141
143
|
```
|
|
142
144
|
|
|
145
|
+
### Caching Requests
|
|
146
|
+
|
|
147
|
+
Enable caching on requests to reduce network load and improve performance. By default, the cache duration is 20 seconds,
|
|
148
|
+
but you can specify a custom duration in seconds.
|
|
149
|
+
|
|
150
|
+
#### Caching Individual Routes
|
|
151
|
+
|
|
152
|
+
You can enable caching on individual routes:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
156
|
+
// Get a list of todos with default cache duration (20 seconds)
|
|
157
|
+
Route.get<Todo[]>("listTodos", "todos").withCache();
|
|
158
|
+
|
|
159
|
+
// Get a specific todo by id with custom cache duration (300 seconds)
|
|
160
|
+
Route.get<Todo>("getTodo", "todos/[id]").withCache(300);
|
|
161
|
+
|
|
162
|
+
// Add a new todo (no cache)
|
|
163
|
+
Route.post<Todo>("addTodo", "todos");
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Now, when making requests, the caching feature will be applied.
|
|
168
|
+
|
|
169
|
+
#### Caching the Entire API
|
|
170
|
+
|
|
171
|
+
You can also enable caching for all routes defined within an API:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
Api.create("hello", "https://jsonplaceholder.typicode.com/", () => {
|
|
175
|
+
// Define routes for the API
|
|
176
|
+
Route.get<Todo[]>("listTodos", "todos");
|
|
177
|
+
Route.get<Todo>("getTodo", "todos/[id]");
|
|
178
|
+
Route.post<Todo>("addTodo", "todos");
|
|
179
|
+
}).withCache(); // Enable default cache duration (20 seconds) for all routes
|
|
180
|
+
```
|
|
181
|
+
|
|
143
182
|
## 🔗 Links
|
|
144
183
|
|
|
145
184
|
- [NPM](https://www.npmjs.com/package/klaim)
|
package/bun.lockb
CHANGED
|
Binary file
|
package/deno.json
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@antharuu/klaim",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Klaim is a lightweight TypeScript library designed to manage APIs and record requests, optimized for an optimal user experience.",
|
|
5
|
-
"repository": {
|
|
6
|
-
"type": "git",
|
|
7
|
-
"url": "https://github.com/antharuu/klaim.git"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"typescript",
|
|
11
|
-
"api",
|
|
12
|
-
"request",
|
|
13
|
-
"user experience",
|
|
14
|
-
"optimization",
|
|
15
|
-
"lightweight"
|
|
16
|
-
],
|
|
17
|
-
"author": "antharuu",
|
|
18
|
-
"license": "MIT",
|
|
19
|
-
"bugs": {
|
|
20
|
-
"url": "https://github.com/antharuu/klaim/issues"
|
|
21
|
-
},
|
|
22
|
-
"homepage": "https://github.com/antharuu/klaim#readme",
|
|
23
|
-
"main": "dist/klaim.cjs.js",
|
|
24
|
-
"module": "dist/klaim.es.js",
|
|
25
|
-
"types": "dist/index.d.ts",
|
|
26
|
-
"exports": "./mod.ts",
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "vite build",
|
|
29
|
-
"dev": "vite build --watch",
|
|
30
|
-
"test": "vitest",
|
|
31
|
-
"lint": "eslint src",
|
|
32
|
-
"lint:fix": "eslint src **/*.ts --fix",
|
|
33
|
-
"release": "dotenv release-it --"
|
|
34
|
-
},
|
|
35
|
-
"peerDependencies": {
|
|
36
|
-
"typescript": "^5.5.3"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@antharuu/klaim",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Klaim is a lightweight TypeScript library designed to manage APIs and record requests, optimized for an optimal user experience.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/antharuu/klaim.git"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"typescript",
|
|
11
|
+
"api",
|
|
12
|
+
"request",
|
|
13
|
+
"user experience",
|
|
14
|
+
"optimization",
|
|
15
|
+
"lightweight"
|
|
16
|
+
],
|
|
17
|
+
"author": "antharuu",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/antharuu/klaim/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/antharuu/klaim#readme",
|
|
23
|
+
"main": "dist/klaim.cjs.js",
|
|
24
|
+
"module": "dist/klaim.es.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"exports": "./mod.ts",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "vite build",
|
|
29
|
+
"dev": "vite build --watch",
|
|
30
|
+
"test": "vitest",
|
|
31
|
+
"lint": "eslint src",
|
|
32
|
+
"lint:fix": "eslint src **/*.ts --fix",
|
|
33
|
+
"release": "dotenv release-it --"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5.5.3"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/klaim.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var O=Object.defineProperty;var _=(n,t,e)=>t in n?O(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var i=(n,t,e)=>_(n,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function m(n){return n.trim().replace(/^\/|\/$/g,"")}function A(n){return n.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_","")).replace(/(^\w)/,t=>t.toLowerCase())}const u=class u{constructor(){i(this,"cache");this.cache=new Map}static get i(){return u._instance||(u._instance=new u),u._instance}set(t,e,r){const a=Date.now()+r;this.cache.set(t,{data:e,expiry:a})}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}};i(u,"_instance");let l=u;function k(n){let r=2166136261;for(let s=0;s<n.length;s++)r^=n.charCodeAt(s),r*=16777619;let a=(r>>>0).toString(16).padStart(8,"0");for(;a.length<32;)r^=a.charCodeAt(a.length%a.length),r*=16777619,a+=(r>>>0).toString(16).padStart(8,"0");return a.substring(0,32)}async function I(n,t,e){const r=`${n.toString()}${JSON.stringify(t)}`,a=k(r);if(l.i.has(a))return l.i.get(a);const c=await(await fetch(n,t)).json();return l.i.set(a,c,e),c}class g{static subscribe(t,e){this._callbacks.set(t,e)}static run(t){const e=this._callbacks.get(t);e&&e()}}i(g,"_callbacks",new Map);var b=(n=>(n.GET="GET",n.POST="POST",n.PUT="PUT",n.DELETE="DELETE",n.PATCH="PATCH",n.OPTIONS="OPTIONS",n))(b||{});class d{constructor(t,e,r,a="GET"){i(this,"api","undefined");i(this,"name");i(this,"url");i(this,"method");i(this,"headers");i(this,"arguments",new Set);i(this,"callbacks",{before:null,after:null});this.name=A(t),this.name!==t&&console.warn(`Route name "${t}" has been camelCased to "${this.name}"`),this.url=m(e),this.headers=r||{},this.method=a,this.detectArguments()}static createRoute(t,e,r,a){const s=new d(t,e,r,a);return h.i.registerRoute(s),s}static get(t,e,r={}){return this.createRoute(t,e,r,"GET")}static post(t,e,r){return this.createRoute(t,e,r,"POST")}static put(t,e,r){return this.createRoute(t,e,r,"PUT")}static delete(t,e,r){return this.createRoute(t,e,r,"DELETE")}static patch(t,e,r){return this.createRoute(t,e,r,"PATCH")}static options(t,e,r){return this.createRoute(t,e,r,"OPTIONS")}before(t){return this.callbacks.before=t,this}after(t){return this.callbacks.after=t,this}detectArguments(){const t=this.url.match(/\[([^\]]+)]/g);t&&t.forEach(e=>{const r=e.replace(/\[|]/g,"");this.arguments.add(r)})}withCache(t=20){return this.cache=t,this}}const f={};async function U(n,t,e={},r={}){let a=D(`${n.url}/${t.url}`,t,e),s={};r&&t.method!==b.GET&&(s.body=JSON.stringify(r)),s.headers={"Content-Type":"application/json",...n.headers,...t.headers},s.method=t.method;const{beforeRoute:c,beforeApi:T,beforeUrl:E,beforeConfig:C}=H({route:t,api:n,url:a,config:s});a=E,s=C,n=h.updateApi(T),t=h.updateRoute(c);const P=n.cache||t.cache;let p;P?p=await I(a,s,n.cache):p=await(await fetch(a,s)).json();const{afterRoute:S,afterApi:$,afterData:y}=N({route:t,api:n,response:p,data:p});return h.updateApi($),h.updateRoute(S),g.run(`${n.name}.${t.name}`),y}function D(n,t,e){let r=n;return t.arguments.forEach(a=>{if(e[a]===void 0)throw new Error(`Argument ${a} is missing`);r=r.replace(`[${a}]`,e[a])}),r}function H({route:n,api:t,url:e,config:r}){var s,c;const a=(c=(s=n.callbacks).before)==null?void 0:c.call(s,{route:n,api:t,url:e,config:r});return{beforeRoute:(a==null?void 0:a.route)||n,beforeApi:(a==null?void 0:a.api)||t,beforeUrl:(a==null?void 0:a.url)||e,beforeConfig:(a==null?void 0:a.config)||r}}function N({route:n,api:t,response:e,data:r}){var s,c;const a=(c=(s=n.callbacks).after)==null?void 0:c.call(s,{route:n,api:t,response:e,data:r});return{afterRoute:(a==null?void 0:a.route)||n,afterApi:(a==null?void 0:a.api)||t,afterResponse:(a==null?void 0:a.response)||e,afterData:(a==null?void 0:a.data)||r}}const o=class o{constructor(){i(this,"_apis",new Map);i(this,"_currentApi",null)}static get i(){return o._instance||(o._instance=new o),o._instance}registerApi(t){this._apis.set(t.name,t),f[t.name]={}}setCurrent(t){const e=this._apis.get(t);if(!e)throw new Error(`API ${t} not found`);this._currentApi=e}clearCurrent(){this._currentApi=null}registerRoute(t){if(!this._currentApi)throw new Error("No current API set, use Route only inside Api.create callback");t.api=this._currentApi.name,this._currentApi.routes.set(t.name,t),this.addToKlaimRoute(t.api,t)}getApi(t){return this._apis.get(t)}getRoute(t,e){const r=this._apis.get(t);if(!r)throw new Error(`API ${t} not found`);return r.routes.get(e)}static updateApi(t){return o.i._apis.has(t.name)||o.i.registerApi(t),o.i._apis.set(t.name,t),t}static updateRoute(t){const e=o.i._apis.get(t.api);if(!e)throw new Error(`API ${t.api} not found`);return e.routes.set(t.name,t),t}addToKlaimRoute(t,e){f[t][e.name]=async(r={},a={})=>{const s=o.i._apis.get(t);if(!s)throw new Error(`API ${e.api} not found`);return U(s,e,r,a)}}};i(o,"_instance");let h=o;class w{constructor(t,e,r={}){i(this,"name");i(this,"url");i(this,"headers");i(this,"routes",new Map);i(this,"cache",!1);this.name=t,this.url=m(e),this.headers=r}static create(t,e,r,a={}){const s=A(t);s!==t&&console.warn(`API name "${t}" has been camelCased to "${s}"`);const c=new w(s,e,a);return h.i.registerApi(c),h.i.setCurrent(s),r(),h.i.clearCurrent(),c}withCache(t=20){return this.cache=t,this}}exports.Api=w;exports.Hook=g;exports.Klaim=f;exports.Registry=h;exports.Route=d;
|
package/dist/klaim.es.js
CHANGED
|
@@ -1,13 +1,78 @@
|
|
|
1
|
-
var
|
|
2
|
-
var _ = (
|
|
3
|
-
var i = (
|
|
4
|
-
function
|
|
5
|
-
return
|
|
1
|
+
var y = Object.defineProperty;
|
|
2
|
+
var _ = (r, t, e) => t in r ? y(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
+
var i = (r, t, e) => _(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
function d(r) {
|
|
5
|
+
return r.trim().replace(/^\/|\/$/g, "");
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
return
|
|
7
|
+
function g(r) {
|
|
8
|
+
return r.replace(/([-_][a-z])/gi, (t) => t.toUpperCase().replace("-", "").replace("_", "")).replace(/(^\w)/, (t) => t.toLowerCase());
|
|
9
9
|
}
|
|
10
|
-
class
|
|
10
|
+
const u = class u {
|
|
11
|
+
/**
|
|
12
|
+
* Private constructor to prevent instantiation from outside the class.
|
|
13
|
+
*/
|
|
14
|
+
constructor() {
|
|
15
|
+
i(this, "cache");
|
|
16
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Provides access to the singleton instance of the class.
|
|
20
|
+
*
|
|
21
|
+
* @returns The singleton instance of Cache.
|
|
22
|
+
*/
|
|
23
|
+
static get i() {
|
|
24
|
+
return u._instance || (u._instance = new u()), u._instance;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sets the cache for a specific key.
|
|
28
|
+
*
|
|
29
|
+
* @param key The key to cache the value under.
|
|
30
|
+
* @param value The value to be cached.
|
|
31
|
+
* @param ttl Time to live in milliseconds.
|
|
32
|
+
*/
|
|
33
|
+
set(t, e, n) {
|
|
34
|
+
const a = Date.now() + n;
|
|
35
|
+
this.cache.set(t, { data: e, expiry: a });
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks if the cache has a valid entry for the given key.
|
|
39
|
+
*
|
|
40
|
+
* @param key The key to check in the cache.
|
|
41
|
+
* @returns True if a valid cache entry exists, otherwise false.
|
|
42
|
+
*/
|
|
43
|
+
has(t) {
|
|
44
|
+
const e = this.cache.get(t);
|
|
45
|
+
return e ? Date.now() > e.expiry ? (this.cache.delete(t), !1) : !0 : !1;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Gets the cached value for the given key.
|
|
49
|
+
*
|
|
50
|
+
* @param key The key to retrieve from the cache.
|
|
51
|
+
* @returns The cached value or null if not found or expired.
|
|
52
|
+
*/
|
|
53
|
+
get(t) {
|
|
54
|
+
return this.has(t) ? this.cache.get(t).data : null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
i(u, "_instance");
|
|
58
|
+
let l = u;
|
|
59
|
+
function k(r) {
|
|
60
|
+
let n = 2166136261;
|
|
61
|
+
for (let s = 0; s < r.length; s++)
|
|
62
|
+
n ^= r.charCodeAt(s), n *= 16777619;
|
|
63
|
+
let a = (n >>> 0).toString(16).padStart(8, "0");
|
|
64
|
+
for (; a.length < 32; )
|
|
65
|
+
n ^= a.charCodeAt(a.length % a.length), n *= 16777619, a += (n >>> 0).toString(16).padStart(8, "0");
|
|
66
|
+
return a.substring(0, 32);
|
|
67
|
+
}
|
|
68
|
+
async function I(r, t, e) {
|
|
69
|
+
const n = `${r.toString()}${JSON.stringify(t)}`, a = k(n);
|
|
70
|
+
if (l.i.has(a))
|
|
71
|
+
return l.i.get(a);
|
|
72
|
+
const c = await (await fetch(r, t)).json();
|
|
73
|
+
return l.i.set(a, c, e), c;
|
|
74
|
+
}
|
|
75
|
+
class w {
|
|
11
76
|
/**
|
|
12
77
|
* Subscribes to the hook
|
|
13
78
|
*
|
|
@@ -27,8 +92,8 @@ class m {
|
|
|
27
92
|
e && e();
|
|
28
93
|
}
|
|
29
94
|
}
|
|
30
|
-
i(
|
|
31
|
-
var
|
|
95
|
+
i(w, "_callbacks", /* @__PURE__ */ new Map());
|
|
96
|
+
var m = /* @__PURE__ */ ((r) => (r.GET = "GET", r.POST = "POST", r.PUT = "PUT", r.DELETE = "DELETE", r.PATCH = "PATCH", r.OPTIONS = "OPTIONS", r))(m || {});
|
|
32
97
|
class A {
|
|
33
98
|
/**
|
|
34
99
|
* Constructor
|
|
@@ -38,7 +103,7 @@ class A {
|
|
|
38
103
|
* @param headers - The headers to be sent with the request
|
|
39
104
|
* @param method - The HTTP method of the route
|
|
40
105
|
*/
|
|
41
|
-
constructor(t, e, n,
|
|
106
|
+
constructor(t, e, n, a = "GET") {
|
|
42
107
|
i(this, "api", "undefined");
|
|
43
108
|
i(this, "name");
|
|
44
109
|
i(this, "url");
|
|
@@ -55,7 +120,7 @@ class A {
|
|
|
55
120
|
*/
|
|
56
121
|
after: null
|
|
57
122
|
});
|
|
58
|
-
this.name =
|
|
123
|
+
this.name = g(t), this.name !== t && console.warn(`Route name "${t}" has been camelCased to "${this.name}"`), this.url = d(e), this.headers = n || {}, this.method = a, this.detectArguments();
|
|
59
124
|
}
|
|
60
125
|
/**
|
|
61
126
|
* Creates a new route
|
|
@@ -66,9 +131,9 @@ class A {
|
|
|
66
131
|
* @param method - The HTTP method of the route
|
|
67
132
|
* @returns The new route
|
|
68
133
|
*/
|
|
69
|
-
static createRoute(t, e, n,
|
|
70
|
-
const s = new A(t, e, n,
|
|
71
|
-
return
|
|
134
|
+
static createRoute(t, e, n, a) {
|
|
135
|
+
const s = new A(t, e, n, a);
|
|
136
|
+
return h.i.registerRoute(s), s;
|
|
72
137
|
}
|
|
73
138
|
/**
|
|
74
139
|
* Creates a new route with the GET method
|
|
@@ -200,58 +265,70 @@ class A {
|
|
|
200
265
|
this.arguments.add(n);
|
|
201
266
|
});
|
|
202
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Enables caching for the Route
|
|
270
|
+
*
|
|
271
|
+
* @param duration - The duration to cache the response for seconds (default: 20)
|
|
272
|
+
* @returns The Route
|
|
273
|
+
*/
|
|
274
|
+
withCache(t = 20) {
|
|
275
|
+
return this.cache = t, this;
|
|
276
|
+
}
|
|
203
277
|
}
|
|
204
|
-
const
|
|
205
|
-
async function
|
|
206
|
-
let
|
|
207
|
-
n && t.method !==
|
|
278
|
+
const f = {};
|
|
279
|
+
async function U(r, t, e = {}, n = {}) {
|
|
280
|
+
let a = D(`${r.url}/${t.url}`, t, e), s = {};
|
|
281
|
+
n && t.method !== m.GET && (s.body = JSON.stringify(n)), s.headers = {
|
|
208
282
|
"Content-Type": "application/json",
|
|
209
|
-
...
|
|
283
|
+
...r.headers,
|
|
210
284
|
...t.headers
|
|
211
285
|
}, s.method = t.method;
|
|
212
286
|
const {
|
|
213
|
-
beforeRoute:
|
|
214
|
-
beforeApi:
|
|
215
|
-
beforeUrl:
|
|
216
|
-
beforeConfig:
|
|
217
|
-
} =
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
287
|
+
beforeRoute: c,
|
|
288
|
+
beforeApi: T,
|
|
289
|
+
beforeUrl: b,
|
|
290
|
+
beforeConfig: C
|
|
291
|
+
} = H({ route: t, api: r, url: a, config: s });
|
|
292
|
+
a = b, s = C, r = h.updateApi(T), t = h.updateRoute(c);
|
|
293
|
+
const P = r.cache || t.cache;
|
|
294
|
+
let p;
|
|
295
|
+
P ? p = await I(a, s, r.cache) : p = await (await fetch(a, s)).json();
|
|
296
|
+
const {
|
|
297
|
+
afterRoute: $,
|
|
298
|
+
afterApi: S,
|
|
299
|
+
afterData: O
|
|
300
|
+
} = N({ route: t, api: r, response: p, data: p });
|
|
301
|
+
return h.updateApi(S), h.updateRoute($), w.run(`${r.name}.${t.name}`), O;
|
|
225
302
|
}
|
|
226
|
-
function
|
|
227
|
-
let n =
|
|
228
|
-
return t.arguments.forEach((
|
|
229
|
-
if (e[
|
|
230
|
-
throw new Error(`Argument ${
|
|
231
|
-
n = n.replace(`[${
|
|
303
|
+
function D(r, t, e) {
|
|
304
|
+
let n = r;
|
|
305
|
+
return t.arguments.forEach((a) => {
|
|
306
|
+
if (e[a] === void 0)
|
|
307
|
+
throw new Error(`Argument ${a} is missing`);
|
|
308
|
+
n = n.replace(`[${a}]`, e[a]);
|
|
232
309
|
}), n;
|
|
233
310
|
}
|
|
234
|
-
function
|
|
235
|
-
var s,
|
|
236
|
-
const
|
|
311
|
+
function H({ route: r, api: t, url: e, config: n }) {
|
|
312
|
+
var s, c;
|
|
313
|
+
const a = (c = (s = r.callbacks).before) == null ? void 0 : c.call(s, { route: r, api: t, url: e, config: n });
|
|
237
314
|
return {
|
|
238
|
-
beforeRoute: (
|
|
239
|
-
beforeApi: (
|
|
240
|
-
beforeUrl: (
|
|
241
|
-
beforeConfig: (
|
|
315
|
+
beforeRoute: (a == null ? void 0 : a.route) || r,
|
|
316
|
+
beforeApi: (a == null ? void 0 : a.api) || t,
|
|
317
|
+
beforeUrl: (a == null ? void 0 : a.url) || e,
|
|
318
|
+
beforeConfig: (a == null ? void 0 : a.config) || n
|
|
242
319
|
};
|
|
243
320
|
}
|
|
244
|
-
function
|
|
245
|
-
var s,
|
|
246
|
-
const
|
|
321
|
+
function N({ route: r, api: t, response: e, data: n }) {
|
|
322
|
+
var s, c;
|
|
323
|
+
const a = (c = (s = r.callbacks).after) == null ? void 0 : c.call(s, { route: r, api: t, response: e, data: n });
|
|
247
324
|
return {
|
|
248
|
-
afterRoute: (
|
|
249
|
-
afterApi: (
|
|
250
|
-
afterResponse: (
|
|
251
|
-
afterData: (
|
|
325
|
+
afterRoute: (a == null ? void 0 : a.route) || r,
|
|
326
|
+
afterApi: (a == null ? void 0 : a.api) || t,
|
|
327
|
+
afterResponse: (a == null ? void 0 : a.response) || e,
|
|
328
|
+
afterData: (a == null ? void 0 : a.data) || n
|
|
252
329
|
};
|
|
253
330
|
}
|
|
254
|
-
const
|
|
331
|
+
const o = class o {
|
|
255
332
|
/**
|
|
256
333
|
* Constructor
|
|
257
334
|
*/
|
|
@@ -265,7 +342,7 @@ const c = class c {
|
|
|
265
342
|
* @returns The singleton instance
|
|
266
343
|
*/
|
|
267
344
|
static get i() {
|
|
268
|
-
return
|
|
345
|
+
return o._instance || (o._instance = new o()), o._instance;
|
|
269
346
|
}
|
|
270
347
|
/**
|
|
271
348
|
* Registers an API
|
|
@@ -273,7 +350,7 @@ const c = class c {
|
|
|
273
350
|
* @param api - The API to register
|
|
274
351
|
*/
|
|
275
352
|
registerApi(t) {
|
|
276
|
-
this._apis.set(t.name, t),
|
|
353
|
+
this._apis.set(t.name, t), f[t.name] = {};
|
|
277
354
|
}
|
|
278
355
|
/**
|
|
279
356
|
* Sets the current API
|
|
@@ -331,7 +408,7 @@ const c = class c {
|
|
|
331
408
|
* @returns The updated API
|
|
332
409
|
*/
|
|
333
410
|
static updateApi(t) {
|
|
334
|
-
return
|
|
411
|
+
return o.i._apis.has(t.name) || o.i.registerApi(t), o.i._apis.set(t.name, t), t;
|
|
335
412
|
}
|
|
336
413
|
/**
|
|
337
414
|
* Updates a route
|
|
@@ -340,7 +417,7 @@ const c = class c {
|
|
|
340
417
|
* @returns The updated route
|
|
341
418
|
*/
|
|
342
419
|
static updateRoute(t) {
|
|
343
|
-
const e =
|
|
420
|
+
const e = o.i._apis.get(t.api);
|
|
344
421
|
if (!e)
|
|
345
422
|
throw new Error(`API ${t.api} not found`);
|
|
346
423
|
return e.routes.set(t.name, t), t;
|
|
@@ -352,17 +429,17 @@ const c = class c {
|
|
|
352
429
|
* @param route - The route to add
|
|
353
430
|
*/
|
|
354
431
|
addToKlaimRoute(t, e) {
|
|
355
|
-
|
|
356
|
-
const s =
|
|
432
|
+
f[t][e.name] = async (n = {}, a = {}) => {
|
|
433
|
+
const s = o.i._apis.get(t);
|
|
357
434
|
if (!s)
|
|
358
435
|
throw new Error(`API ${e.api} not found`);
|
|
359
|
-
return
|
|
436
|
+
return U(s, e, n, a);
|
|
360
437
|
};
|
|
361
438
|
}
|
|
362
439
|
};
|
|
363
|
-
i(
|
|
364
|
-
let
|
|
365
|
-
class
|
|
440
|
+
i(o, "_instance");
|
|
441
|
+
let h = o;
|
|
442
|
+
class E {
|
|
366
443
|
/**
|
|
367
444
|
* Constructor
|
|
368
445
|
*
|
|
@@ -370,12 +447,13 @@ class w {
|
|
|
370
447
|
* @param url - The base URL of the API
|
|
371
448
|
* @param headers - The headers to be sent with each request
|
|
372
449
|
*/
|
|
373
|
-
constructor(t, e, n) {
|
|
450
|
+
constructor(t, e, n = {}) {
|
|
374
451
|
i(this, "name");
|
|
375
452
|
i(this, "url");
|
|
376
453
|
i(this, "headers");
|
|
377
454
|
i(this, "routes", /* @__PURE__ */ new Map());
|
|
378
|
-
this
|
|
455
|
+
i(this, "cache", !1);
|
|
456
|
+
this.name = t, this.url = d(e), this.headers = n;
|
|
379
457
|
}
|
|
380
458
|
/**
|
|
381
459
|
* Creates a new API
|
|
@@ -386,17 +464,26 @@ class w {
|
|
|
386
464
|
* @param headers - The headers to be sent with each request
|
|
387
465
|
* @returns The new API
|
|
388
466
|
*/
|
|
389
|
-
static create(t, e, n,
|
|
390
|
-
const s =
|
|
467
|
+
static create(t, e, n, a = {}) {
|
|
468
|
+
const s = g(t);
|
|
391
469
|
s !== t && console.warn(`API name "${t}" has been camelCased to "${s}"`);
|
|
392
|
-
const
|
|
393
|
-
return
|
|
470
|
+
const c = new E(s, e, a);
|
|
471
|
+
return h.i.registerApi(c), h.i.setCurrent(s), n(), h.i.clearCurrent(), c;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Enables caching for the API
|
|
475
|
+
*
|
|
476
|
+
* @param duration - The duration to cache the response for seconds (default: 20)
|
|
477
|
+
* @returns The API
|
|
478
|
+
*/
|
|
479
|
+
withCache(t = 20) {
|
|
480
|
+
return this.cache = t, this;
|
|
394
481
|
}
|
|
395
482
|
}
|
|
396
483
|
export {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
484
|
+
E as Api,
|
|
485
|
+
w as Hook,
|
|
486
|
+
f as Klaim,
|
|
487
|
+
h as Registry,
|
|
401
488
|
A as Route
|
|
402
489
|
};
|
package/dist/klaim.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(c,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(c=typeof globalThis<"u"?globalThis:c||self,o(c.klaim={}))})(this,function(c){"use strict";var j=Object.defineProperty;var N=(c,o,p)=>o in c?j(c,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):c[o]=p;var s=(c,o,p)=>N(c,typeof o!="symbol"?o+"":o,p);function o(r){return r.trim().replace(/^\/|\/$/g,"")}function p(r){return r.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_","")).replace(/(^\w)/,t=>t.toLowerCase())}const f=class f{constructor(){s(this,"cache");this.cache=new Map}static get i(){return f._instance||(f._instance=new f),f._instance}set(t,e,a){const n=Date.now()+a;this.cache.set(t,{data:e,expiry:n})}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}};s(f,"_instance");let d=f;function E(r){let a=2166136261;for(let i=0;i<r.length;i++)a^=r.charCodeAt(i),a*=16777619;let n=(a>>>0).toString(16).padStart(8,"0");for(;n.length<32;)a^=n.charCodeAt(n.length%n.length),a*=16777619,n+=(a>>>0).toString(16).padStart(8,"0");return n.substring(0,32)}async function C(r,t,e){const a=`${r.toString()}${JSON.stringify(t)}`,n=E(a);if(d.i.has(n))return d.i.get(n);const h=await(await fetch(r,t)).json();return d.i.set(n,h,e),h}class m{static subscribe(t,e){this._callbacks.set(t,e)}static run(t){const e=this._callbacks.get(t);e&&e()}}s(m,"_callbacks",new Map);var b=(r=>(r.GET="GET",r.POST="POST",r.PUT="PUT",r.DELETE="DELETE",r.PATCH="PATCH",r.OPTIONS="OPTIONS",r))(b||{});class w{constructor(t,e,a,n="GET"){s(this,"api","undefined");s(this,"name");s(this,"url");s(this,"method");s(this,"headers");s(this,"arguments",new Set);s(this,"callbacks",{before:null,after:null});this.name=p(t),this.name!==t&&console.warn(`Route name "${t}" has been camelCased to "${this.name}"`),this.url=o(e),this.headers=a||{},this.method=n,this.detectArguments()}static createRoute(t,e,a,n){const i=new w(t,e,a,n);return l.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")}before(t){return this.callbacks.before=t,this}after(t){return this.callbacks.after=t,this}detectArguments(){const t=this.url.match(/\[([^\]]+)]/g);t&&t.forEach(e=>{const a=e.replace(/\[|]/g,"");this.arguments.add(a)})}withCache(t=20){return this.cache=t,this}}const A={};async function P(r,t,e={},a={}){let n=S(`${r.url}/${t.url}`,t,e),i={};a&&t.method!==b.GET&&(i.body=JSON.stringify(a)),i.headers={"Content-Type":"application/json",...r.headers,...t.headers},i.method=t.method;const{beforeRoute:h,beforeApi:O,beforeUrl:k,beforeConfig:_}=$({route:t,api:r,url:n,config:i});n=k,i=_,r=l.updateApi(O),t=l.updateRoute(h);const I=r.cache||t.cache;let g;I?g=await C(n,i,r.cache):g=await(await fetch(n,i)).json();const{afterRoute:U,afterApi:D,afterData:H}=y({route:t,api:r,response:g,data:g});return l.updateApi(D),l.updateRoute(U),m.run(`${r.name}.${t.name}`),H}function S(r,t,e){let a=r;return t.arguments.forEach(n=>{if(e[n]===void 0)throw new Error(`Argument ${n} is missing`);a=a.replace(`[${n}]`,e[n])}),a}function $({route:r,api:t,url:e,config:a}){var i,h;const n=(h=(i=r.callbacks).before)==null?void 0:h.call(i,{route:r,api:t,url:e,config:a});return{beforeRoute:(n==null?void 0:n.route)||r,beforeApi:(n==null?void 0:n.api)||t,beforeUrl:(n==null?void 0:n.url)||e,beforeConfig:(n==null?void 0:n.config)||a}}function y({route:r,api:t,response:e,data:a}){var i,h;const n=(h=(i=r.callbacks).after)==null?void 0:h.call(i,{route:r,api:t,response:e,data:a});return{afterRoute:(n==null?void 0:n.route)||r,afterApi:(n==null?void 0:n.api)||t,afterResponse:(n==null?void 0:n.response)||e,afterData:(n==null?void 0:n.data)||a}}const u=class u{constructor(){s(this,"_apis",new Map);s(this,"_currentApi",null)}static get i(){return u._instance||(u._instance=new u),u._instance}registerApi(t){this._apis.set(t.name,t),A[t.name]={}}setCurrent(t){const e=this._apis.get(t);if(!e)throw new Error(`API ${t} not found`);this._currentApi=e}clearCurrent(){this._currentApi=null}registerRoute(t){if(!this._currentApi)throw new Error("No current API set, use Route only inside Api.create callback");t.api=this._currentApi.name,this._currentApi.routes.set(t.name,t),this.addToKlaimRoute(t.api,t)}getApi(t){return this._apis.get(t)}getRoute(t,e){const a=this._apis.get(t);if(!a)throw new Error(`API ${t} not found`);return a.routes.get(e)}static updateApi(t){return u.i._apis.has(t.name)||u.i.registerApi(t),u.i._apis.set(t.name,t),t}static updateRoute(t){const e=u.i._apis.get(t.api);if(!e)throw new Error(`API ${t.api} not found`);return e.routes.set(t.name,t),t}addToKlaimRoute(t,e){A[t][e.name]=async(a={},n={})=>{const i=u.i._apis.get(t);if(!i)throw new Error(`API ${e.api} not found`);return P(i,e,a,n)}}};s(u,"_instance");let l=u;class T{constructor(t,e,a={}){s(this,"name");s(this,"url");s(this,"headers");s(this,"routes",new Map);s(this,"cache",!1);this.name=t,this.url=o(e),this.headers=a}static create(t,e,a,n={}){const i=p(t);i!==t&&console.warn(`API name "${t}" has been camelCased to "${i}"`);const h=new T(i,e,n);return l.i.registerApi(h),l.i.setCurrent(i),a(),l.i.clearCurrent(),h}withCache(t=20){return this.cache=t,this}}c.Api=T,c.Hook=m,c.Klaim=A,c.Registry=l,c.Route=w,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/antharuu/klaim#readme",
|
|
22
22
|
"type": "module",
|
|
23
|
-
"version": "1.
|
|
23
|
+
"version": "1.4.0",
|
|
24
24
|
"main": "dist/klaim.cjs.js",
|
|
25
25
|
"module": "dist/klaim.es.js",
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"build": "vite build",
|
|
29
29
|
"dev": "vite build --watch",
|
|
30
30
|
"test": "vitest",
|
|
31
|
+
"test:ui": "vitest --ui --coverage.enabled=true",
|
|
32
|
+
"test:cover": "vitest --coverage",
|
|
31
33
|
"lint": "eslint src",
|
|
32
34
|
"lint:fix": "eslint src **/*.ts --fix",
|
|
33
35
|
"release": "dotenv release-it -- patch --config release-it.config.cjs --ci",
|
|
@@ -41,14 +43,15 @@
|
|
|
41
43
|
"@types/eslint__js": "^8.42.3",
|
|
42
44
|
"@types/node": "^20.14.9",
|
|
43
45
|
"@vitest/coverage-v8": "^1.6.0",
|
|
46
|
+
"@vitest/ui": "^1.6.0",
|
|
44
47
|
"dotenv-cli": "^7.4.2",
|
|
45
48
|
"eslint": "^9.6.0",
|
|
46
49
|
"eslint-plugin-jsdoc": "^48.5.0",
|
|
47
|
-
"eslint-plugin-simple-import-sort": "^12.1.
|
|
50
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
48
51
|
"jsdom": "^24.1.0",
|
|
49
52
|
"release-it": "^17.4.1",
|
|
50
53
|
"typescript-eslint": "^7.15.0",
|
|
51
|
-
"vite": "^5.3.
|
|
54
|
+
"vite": "^5.3.3",
|
|
52
55
|
"vitest": "^1.6.0"
|
|
53
56
|
},
|
|
54
57
|
"peerDependencies": {
|
package/src/core/Api.ts
CHANGED
|
@@ -9,6 +9,9 @@ interface IApi {
|
|
|
9
9
|
url: string;
|
|
10
10
|
headers: IHeaders;
|
|
11
11
|
routes: Map<string, Route>;
|
|
12
|
+
cache: false | number;
|
|
13
|
+
|
|
14
|
+
withCache: (duration?: number) => this;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export type IApiCallback = () => void;
|
|
@@ -26,6 +29,8 @@ export class Api implements IApi {
|
|
|
26
29
|
|
|
27
30
|
public routes: Map<string, Route> = new Map<string, Route>();
|
|
28
31
|
|
|
32
|
+
public cache: false | number = false;
|
|
33
|
+
|
|
29
34
|
/**
|
|
30
35
|
* Constructor
|
|
31
36
|
*
|
|
@@ -33,10 +38,10 @@ export class Api implements IApi {
|
|
|
33
38
|
* @param url - The base URL of the API
|
|
34
39
|
* @param headers - The headers to be sent with each request
|
|
35
40
|
*/
|
|
36
|
-
private constructor (name: string, url: string, headers: IHeaders) {
|
|
41
|
+
private constructor (name: string, url: string, headers: IHeaders = {}) {
|
|
37
42
|
this.name = name;
|
|
38
43
|
this.url = cleanUrl(url);
|
|
39
|
-
this.headers = headers
|
|
44
|
+
this.headers = headers;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
/**
|
|
@@ -48,7 +53,12 @@ export class Api implements IApi {
|
|
|
48
53
|
* @param headers - The headers to be sent with each request
|
|
49
54
|
* @returns The new API
|
|
50
55
|
*/
|
|
51
|
-
public static create (
|
|
56
|
+
public static create (
|
|
57
|
+
name: string,
|
|
58
|
+
url: string,
|
|
59
|
+
callback: IApiCallback,
|
|
60
|
+
headers: IHeaders = {}
|
|
61
|
+
): Api {
|
|
52
62
|
const newName = toCamelCase(name);
|
|
53
63
|
if (newName !== name) {
|
|
54
64
|
console.warn(`API name "${name}" has been camelCased to "${newName}"`);
|
|
@@ -60,4 +70,15 @@ export class Api implements IApi {
|
|
|
60
70
|
Registry.i.clearCurrent();
|
|
61
71
|
return api;
|
|
62
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Enables caching for the API
|
|
76
|
+
*
|
|
77
|
+
* @param duration - The duration to cache the response for seconds (default: 20)
|
|
78
|
+
* @returns The API
|
|
79
|
+
*/
|
|
80
|
+
public withCache (duration = 20): this {
|
|
81
|
+
this.cache = duration;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
63
84
|
}
|