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 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.2.33",
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 $=Object.defineProperty;var k=(a,t,e)=>t in a?$(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var i=(a,t,e)=>k(a,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function d(a){return a.trim().replace(/^\/|\/$/g,"")}function A(a){return a.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_","")).replace(/(^\w)/,t=>t.toLowerCase())}class p{static subscribe(t,e){this._callbacks.set(t,e)}static run(t){const e=this._callbacks.get(t);e&&e()}}i(p,"_callbacks",new Map);var g=(a=>(a.GET="GET",a.POST="POST",a.PUT="PUT",a.DELETE="DELETE",a.PATCH="PATCH",a.OPTIONS="OPTIONS",a))(g||{});class h{constructor(t,e,n,r="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=d(e),this.headers=n||{},this.method=r,this.detectArguments()}static createRoute(t,e,n,r){const s=new h(t,e,n,r);return o.i.registerRoute(s),s}static get(t,e,n={}){return this.createRoute(t,e,n,"GET")}static post(t,e,n){return this.createRoute(t,e,n,"POST")}static put(t,e,n){return this.createRoute(t,e,n,"PUT")}static delete(t,e,n){return this.createRoute(t,e,n,"DELETE")}static patch(t,e,n){return this.createRoute(t,e,n,"PATCH")}static options(t,e,n){return this.createRoute(t,e,n,"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 n=e.replace(/\[|]/g,"");this.arguments.add(n)})}}const l={};async function O(a,t,e={},n={}){let r=_(`${a.url}/${t.url}`,t,e),s={};n&&t.method!==g.GET&&(s.body=JSON.stringify(n)),s.headers={"Content-Type":"application/json",...a.headers,...t.headers},s.method=t.method;const{beforeRoute:u,beforeApi:w,beforeUrl:T,beforeConfig:b}=y({route:t,api:a,url:r,config:s});r=T,s=b,a=o.updateApi(w),t=o.updateRoute(u);const m=await fetch(r,s),{afterRoute:E,afterApi:P,afterData:C}=S({route:t,api:a,response:m,data:await m.json()});return o.updateApi(P),o.updateRoute(E),p.run(`${a.name}.${t.name}`),C}function _(a,t,e){let n=a;return t.arguments.forEach(r=>{if(e[r]===void 0)throw new Error(`Argument ${r} is missing`);n=n.replace(`[${r}]`,e[r])}),n}function y({route:a,api:t,url:e,config:n}){var s,u;const r=(u=(s=a.callbacks).before)==null?void 0:u.call(s,{route:a,api:t,url:e,config:n});return{beforeRoute:(r==null?void 0:r.route)||a,beforeApi:(r==null?void 0:r.api)||t,beforeUrl:(r==null?void 0:r.url)||e,beforeConfig:(r==null?void 0:r.config)||n}}function S({route:a,api:t,response:e,data:n}){var s,u;const r=(u=(s=a.callbacks).after)==null?void 0:u.call(s,{route:a,api:t,response:e,data:n});return{afterRoute:(r==null?void 0:r.route)||a,afterApi:(r==null?void 0:r.api)||t,afterResponse:(r==null?void 0:r.response)||e,afterData:(r==null?void 0:r.data)||n}}const c=class c{constructor(){i(this,"_apis",new Map);i(this,"_currentApi",null)}static get i(){return c._instance||(c._instance=new c),c._instance}registerApi(t){this._apis.set(t.name,t),l[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 n=this._apis.get(t);if(!n)throw new Error(`API ${t} not found`);return n.routes.get(e)}static updateApi(t){return c.i._apis.has(t.name)||c.i.registerApi(t),c.i._apis.set(t.name,t),t}static updateRoute(t){const e=c.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){l[t][e.name]=async(n={},r={})=>{const s=c.i._apis.get(t);if(!s)throw new Error(`API ${e.api} not found`);return O(s,e,n,r)}}};i(c,"_instance");let o=c;class f{constructor(t,e,n){i(this,"name");i(this,"url");i(this,"headers");i(this,"routes",new Map);this.name=t,this.url=d(e),this.headers=n||{}}static create(t,e,n,r={}){const s=A(t);s!==t&&console.warn(`API name "${t}" has been camelCased to "${s}"`);const u=new f(s,e,r);return o.i.registerApi(u),o.i.setCurrent(s),n(),o.i.clearCurrent(),u}}exports.Api=f;exports.Hook=p;exports.Klaim=l;exports.Registry=o;exports.Route=h;
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 $ = Object.defineProperty;
2
- var _ = (a, t, e) => t in a ? $(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
3
- var i = (a, t, e) => _(a, typeof t != "symbol" ? t + "" : t, e);
4
- function h(a) {
5
- return a.trim().replace(/^\/|\/$/g, "");
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 f(a) {
8
- return a.replace(/([-_][a-z])/gi, (t) => t.toUpperCase().replace("-", "").replace("_", "")).replace(/(^\w)/, (t) => t.toLowerCase());
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 m {
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(m, "_callbacks", /* @__PURE__ */ new Map());
31
- var d = /* @__PURE__ */ ((a) => (a.GET = "GET", a.POST = "POST", a.PUT = "PUT", a.DELETE = "DELETE", a.PATCH = "PATCH", a.OPTIONS = "OPTIONS", a))(d || {});
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, r = "GET") {
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 = f(t), this.name !== t && console.warn(`Route name "${t}" has been camelCased to "${this.name}"`), this.url = h(e), this.headers = n || {}, this.method = r, this.detectArguments();
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, r) {
70
- const s = new A(t, e, n, r);
71
- return u.i.registerRoute(s), s;
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 p = {};
205
- async function k(a, t, e = {}, n = {}) {
206
- let r = O(`${a.url}/${t.url}`, t, e), s = {};
207
- n && t.method !== d.GET && (s.body = JSON.stringify(n)), s.headers = {
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
- ...a.headers,
283
+ ...r.headers,
210
284
  ...t.headers
211
285
  }, s.method = t.method;
212
286
  const {
213
- beforeRoute: o,
214
- beforeApi: g,
215
- beforeUrl: T,
216
- beforeConfig: E
217
- } = I({ route: t, api: a, url: r, config: s });
218
- r = T, s = E, a = u.updateApi(g), t = u.updateRoute(o);
219
- const l = await fetch(r, s), {
220
- afterRoute: b,
221
- afterApi: P,
222
- afterData: C
223
- } = S({ route: t, api: a, response: l, data: await l.json() });
224
- return u.updateApi(P), u.updateRoute(b), m.run(`${a.name}.${t.name}`), C;
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 O(a, t, e) {
227
- let n = a;
228
- return t.arguments.forEach((r) => {
229
- if (e[r] === void 0)
230
- throw new Error(`Argument ${r} is missing`);
231
- n = n.replace(`[${r}]`, e[r]);
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 I({ route: a, api: t, url: e, config: n }) {
235
- var s, o;
236
- const r = (o = (s = a.callbacks).before) == null ? void 0 : o.call(s, { route: a, api: t, url: e, config: n });
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: (r == null ? void 0 : r.route) || a,
239
- beforeApi: (r == null ? void 0 : r.api) || t,
240
- beforeUrl: (r == null ? void 0 : r.url) || e,
241
- beforeConfig: (r == null ? void 0 : r.config) || n
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 S({ route: a, api: t, response: e, data: n }) {
245
- var s, o;
246
- const r = (o = (s = a.callbacks).after) == null ? void 0 : o.call(s, { route: a, api: t, response: e, data: n });
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: (r == null ? void 0 : r.route) || a,
249
- afterApi: (r == null ? void 0 : r.api) || t,
250
- afterResponse: (r == null ? void 0 : r.response) || e,
251
- afterData: (r == null ? void 0 : r.data) || n
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 c = class c {
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 c._instance || (c._instance = new c()), c._instance;
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), p[t.name] = {};
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 c.i._apis.has(t.name) || c.i.registerApi(t), c.i._apis.set(t.name, t), t;
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 = c.i._apis.get(t.api);
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
- p[t][e.name] = async (n = {}, r = {}) => {
356
- const s = c.i._apis.get(t);
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 k(s, e, n, r);
436
+ return U(s, e, n, a);
360
437
  };
361
438
  }
362
439
  };
363
- i(c, "_instance");
364
- let u = c;
365
- class w {
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.name = t, this.url = h(e), this.headers = n || {};
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, r = {}) {
390
- const s = f(t);
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 o = new w(s, e, r);
393
- return u.i.registerApi(o), u.i.setCurrent(s), n(), u.i.clearCurrent(), o;
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
- w as Api,
398
- m as Hook,
399
- p as Klaim,
400
- u as Registry,
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(s,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(s=typeof globalThis<"u"?globalThis:s||self,o(s.klaim={}))})(this,function(s){"use strict";var S=Object.defineProperty;var I=(s,o,h)=>o in s?S(s,o,{enumerable:!0,configurable:!0,writable:!0,value:h}):s[o]=h;var c=(s,o,h)=>I(s,typeof o!="symbol"?o+"":o,h);function o(i){return i.trim().replace(/^\/|\/$/g,"")}function h(i){return i.replace(/([-_][a-z])/gi,t=>t.toUpperCase().replace("-","").replace("_","")).replace(/(^\w)/,t=>t.toLowerCase())}class f{static subscribe(t,e){this._callbacks.set(t,e)}static run(t){const e=this._callbacks.get(t);e&&e()}}c(f,"_callbacks",new Map);var g=(i=>(i.GET="GET",i.POST="POST",i.PUT="PUT",i.DELETE="DELETE",i.PATCH="PATCH",i.OPTIONS="OPTIONS",i))(g||{});class d{constructor(t,e,r,n="GET"){c(this,"api","undefined");c(this,"name");c(this,"url");c(this,"method");c(this,"headers");c(this,"arguments",new Set);c(this,"callbacks",{before:null,after:null});this.name=h(t),this.name!==t&&console.warn(`Route name "${t}" has been camelCased to "${this.name}"`),this.url=o(e),this.headers=r||{},this.method=n,this.detectArguments()}static createRoute(t,e,r,n){const a=new d(t,e,r,n);return l.i.registerRoute(a),a}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)})}}const m={};async function T(i,t,e={},r={}){let n=b(`${i.url}/${t.url}`,t,e),a={};r&&t.method!==g.GET&&(a.body=JSON.stringify(r)),a.headers={"Content-Type":"application/json",...i.headers,...t.headers},a.method=t.method;const{beforeRoute:p,beforeApi:C,beforeUrl:$,beforeConfig:k}=E({route:t,api:i,url:n,config:a});n=$,a=k,i=l.updateApi(C),t=l.updateRoute(p);const w=await fetch(n,a),{afterRoute:y,afterApi:O,afterData:_}=P({route:t,api:i,response:w,data:await w.json()});return l.updateApi(O),l.updateRoute(y),f.run(`${i.name}.${t.name}`),_}function b(i,t,e){let r=i;return t.arguments.forEach(n=>{if(e[n]===void 0)throw new Error(`Argument ${n} is missing`);r=r.replace(`[${n}]`,e[n])}),r}function E({route:i,api:t,url:e,config:r}){var a,p;const n=(p=(a=i.callbacks).before)==null?void 0:p.call(a,{route:i,api:t,url:e,config:r});return{beforeRoute:(n==null?void 0:n.route)||i,beforeApi:(n==null?void 0:n.api)||t,beforeUrl:(n==null?void 0:n.url)||e,beforeConfig:(n==null?void 0:n.config)||r}}function P({route:i,api:t,response:e,data:r}){var a,p;const n=(p=(a=i.callbacks).after)==null?void 0:p.call(a,{route:i,api:t,response:e,data:r});return{afterRoute:(n==null?void 0:n.route)||i,afterApi:(n==null?void 0:n.api)||t,afterResponse:(n==null?void 0:n.response)||e,afterData:(n==null?void 0:n.data)||r}}const u=class u{constructor(){c(this,"_apis",new Map);c(this,"_currentApi",null)}static get i(){return u._instance||(u._instance=new u),u._instance}registerApi(t){this._apis.set(t.name,t),m[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 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){m[t][e.name]=async(r={},n={})=>{const a=u.i._apis.get(t);if(!a)throw new Error(`API ${e.api} not found`);return T(a,e,r,n)}}};c(u,"_instance");let l=u;class A{constructor(t,e,r){c(this,"name");c(this,"url");c(this,"headers");c(this,"routes",new Map);this.name=t,this.url=o(e),this.headers=r||{}}static create(t,e,r,n={}){const a=h(t);a!==t&&console.warn(`API name "${t}" has been camelCased to "${a}"`);const p=new A(a,e,n);return l.i.registerApi(p),l.i.setCurrent(a),r(),l.i.clearCurrent(),p}}s.Api=A,s.Hook=f,s.Klaim=m,s.Registry=l,s.Route=d,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
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.2.33",
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.0",
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.2",
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 (name: string, url: string, callback: IApiCallback, headers: IHeaders = {}): Api {
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
  }