klaim 1.2.33 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lockb +0 -0
- package/deno.json +1 -1
- package/dist/klaim.cjs +1 -1
- package/dist/klaim.es.js +2 -2
- package/dist/klaim.umd.js +1 -1
- package/package.json +6 -3
- package/src/core/Api.ts +8 -3
- 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/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/bun.lockb
CHANGED
|
Binary file
|
package/deno.json
CHANGED
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
|
|
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;
|
package/dist/klaim.es.js
CHANGED
|
@@ -370,12 +370,12 @@ class w {
|
|
|
370
370
|
* @param url - The base URL of the API
|
|
371
371
|
* @param headers - The headers to be sent with each request
|
|
372
372
|
*/
|
|
373
|
-
constructor(t, e, n) {
|
|
373
|
+
constructor(t, e, n = {}) {
|
|
374
374
|
i(this, "name");
|
|
375
375
|
i(this, "url");
|
|
376
376
|
i(this, "headers");
|
|
377
377
|
i(this, "routes", /* @__PURE__ */ new Map());
|
|
378
|
-
this.name = t, this.url = h(e), this.headers = n
|
|
378
|
+
this.name = t, this.url = h(e), this.headers = n;
|
|
379
379
|
}
|
|
380
380
|
/**
|
|
381
381
|
* Creates a new API
|
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
|
|
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"})});
|
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.3.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
|
@@ -33,10 +33,10 @@ export class Api implements IApi {
|
|
|
33
33
|
* @param url - The base URL of the API
|
|
34
34
|
* @param headers - The headers to be sent with each request
|
|
35
35
|
*/
|
|
36
|
-
private constructor (name: string, url: string, headers: IHeaders) {
|
|
36
|
+
private constructor (name: string, url: string, headers: IHeaders = {}) {
|
|
37
37
|
this.name = name;
|
|
38
38
|
this.url = cleanUrl(url);
|
|
39
|
-
this.headers = headers
|
|
39
|
+
this.headers = headers;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -48,7 +48,12 @@ export class Api implements IApi {
|
|
|
48
48
|
* @param headers - The headers to be sent with each request
|
|
49
49
|
* @returns The new API
|
|
50
50
|
*/
|
|
51
|
-
public static create (
|
|
51
|
+
public static create (
|
|
52
|
+
name: string,
|
|
53
|
+
url: string,
|
|
54
|
+
callback: IApiCallback,
|
|
55
|
+
headers: IHeaders = {}
|
|
56
|
+
): Api {
|
|
52
57
|
const newName = toCamelCase(name);
|
|
53
58
|
if (newName !== name) {
|
|
54
59
|
console.warn(`API name "${name}" has been camelCased to "${newName}"`);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Api, Klaim, Registry } from "../src/index.ts";
|
|
3
|
+
|
|
4
|
+
describe("Api", () => {
|
|
5
|
+
it("should create an API instance with correct properties", () => {
|
|
6
|
+
const name = "testApi";
|
|
7
|
+
const url = "https://jsonplaceholder.typicode.com";
|
|
8
|
+
|
|
9
|
+
const api = Api.create(name, url, () => {});
|
|
10
|
+
|
|
11
|
+
expect(Registry.i.getApi(name)).toBe(api);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should format the URL correctly", () => {
|
|
15
|
+
const name = "testApi";
|
|
16
|
+
const url = "https://jsonplaceholder.typicode.com/";
|
|
17
|
+
const waitingUrl = "https://jsonplaceholder.typicode.com";
|
|
18
|
+
|
|
19
|
+
const api = Api.create(name, url, () => {});
|
|
20
|
+
|
|
21
|
+
expect(api.url).toBe(waitingUrl);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should format the name to camelCase", () => {
|
|
25
|
+
const name = "test-api";
|
|
26
|
+
const waitingName = "testApi";
|
|
27
|
+
|
|
28
|
+
const api = Api.create(
|
|
29
|
+
name,
|
|
30
|
+
"https://jsonplaceholder.typicode.com",
|
|
31
|
+
() => {},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(api.name).toBe(waitingName);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should access the API instance from Klaim", () => {
|
|
38
|
+
const name = "testApi";
|
|
39
|
+
|
|
40
|
+
Api.create(name, "https://jsonplaceholder.typicode.com", () => {});
|
|
41
|
+
|
|
42
|
+
expect(Klaim[name]).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should set the headers correctly", () => {
|
|
46
|
+
const name = "testApi";
|
|
47
|
+
const headers = {
|
|
48
|
+
"Authorization": "Bearer token 123",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const api = Api.create(
|
|
52
|
+
name,
|
|
53
|
+
"https://jsonplaceholder.typicode.com",
|
|
54
|
+
() => {},
|
|
55
|
+
headers,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(api.headers).toBe(headers);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Api, Klaim, Registry, Route } from "../src/index.ts";
|
|
3
|
+
|
|
4
|
+
const apiName = "testApi";
|
|
5
|
+
const apiUrl = "https://jsonplaceholder.typicode.com";
|
|
6
|
+
|
|
7
|
+
await describe("Route", async () => {
|
|
8
|
+
it("should create an route instance with correct properties", () => {
|
|
9
|
+
const name = "testRoute";
|
|
10
|
+
const routePath = "/posts";
|
|
11
|
+
|
|
12
|
+
Api.create(apiName, apiUrl, () => {
|
|
13
|
+
Route.get(name, routePath);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should format the path correctly", () => {
|
|
20
|
+
const name = "testRoute";
|
|
21
|
+
const routePath = "/posts/";
|
|
22
|
+
const waitingPath = "posts";
|
|
23
|
+
|
|
24
|
+
Api.create(apiName, apiUrl, () => {
|
|
25
|
+
Route.get(name, routePath);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(Registry.i.getRoute(apiName, name).url).toBe(waitingPath);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should format the name to camelCase", () => {
|
|
32
|
+
const name = "test-route";
|
|
33
|
+
const waitingName = "testRoute";
|
|
34
|
+
|
|
35
|
+
Api.create(apiName, apiUrl, () => {
|
|
36
|
+
Route.get(name, "/posts");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(Registry.i.getRoute(apiName, waitingName)).toBeDefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should access the route instance from Klaim", () => {
|
|
43
|
+
const name = "testRoute";
|
|
44
|
+
|
|
45
|
+
Api.create(apiName, apiUrl, () => {
|
|
46
|
+
Route.get(name, "/posts");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(Klaim[apiName][name]).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should set the headers correctly", () => {
|
|
53
|
+
const name = "testRoute";
|
|
54
|
+
const routePath = "/posts";
|
|
55
|
+
const headers = {
|
|
56
|
+
"Authorization": "Bearer token 123",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
Api.create(apiName, apiUrl, () => {
|
|
60
|
+
Route.get(name, routePath, headers);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(Registry.i.getRoute(apiName, name).headers).toEqual(headers);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("Methods", () => {
|
|
67
|
+
it("should create a GET route instance with correct properties", () => {
|
|
68
|
+
const name = "testRoute";
|
|
69
|
+
const routePath = "/posts";
|
|
70
|
+
|
|
71
|
+
Api.create(apiName, apiUrl, () => {
|
|
72
|
+
Route.get(name, routePath);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should create a POST route instance with correct properties", () => {
|
|
79
|
+
const name = "testRoute";
|
|
80
|
+
const routePath = "/posts";
|
|
81
|
+
|
|
82
|
+
Api.create(apiName, apiUrl, () => {
|
|
83
|
+
Route.post(name, routePath);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should create a PUT route instance with correct properties", () => {
|
|
90
|
+
const name = "testRoute";
|
|
91
|
+
const routePath = "/posts";
|
|
92
|
+
|
|
93
|
+
Api.create(apiName, apiUrl, () => {
|
|
94
|
+
Route.put(name, routePath);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should create a DELETE route instance with correct properties", () => {
|
|
101
|
+
const name = "testRoute";
|
|
102
|
+
const routePath = "/posts";
|
|
103
|
+
|
|
104
|
+
Api.create(apiName, apiUrl, () => {
|
|
105
|
+
Route.delete(name, routePath);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should create a PATCH route instance with correct properties", () => {
|
|
112
|
+
const name = "testRoute";
|
|
113
|
+
const routePath = "/posts";
|
|
114
|
+
|
|
115
|
+
Api.create(apiName, apiUrl, () => {
|
|
116
|
+
Route.patch(name, routePath);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should create a OPTIONS route instance with correct properties", () => {
|
|
123
|
+
const name = "testRoute";
|
|
124
|
+
const routePath = "/posts";
|
|
125
|
+
|
|
126
|
+
Api.create(apiName, apiUrl, () => {
|
|
127
|
+
Route.options(name, routePath);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(Registry.i.getRoute(apiName, name)).toBeDefined();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("Arguments", () => {
|
|
135
|
+
it("should set the arguments correctly", () => {
|
|
136
|
+
const name = "testRoute";
|
|
137
|
+
const routePath = "/post/[id]";
|
|
138
|
+
const args = new Set(["id"]);
|
|
139
|
+
|
|
140
|
+
Api.create(apiName, apiUrl, () => {
|
|
141
|
+
Route.get(name, routePath, {});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(Registry.i.getRoute(apiName, name).arguments).toEqual(args);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should set the arguments correctly with multiple arguments", () => {
|
|
148
|
+
const name = "testRoute";
|
|
149
|
+
const routePath = "/post/[id]/[name]";
|
|
150
|
+
const args = new Set(["id", "name"]);
|
|
151
|
+
|
|
152
|
+
Api.create(apiName, apiUrl, () => {
|
|
153
|
+
Route.get(name, routePath, {});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(Registry.i.getRoute(apiName, name).arguments).toEqual(args);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await describe("Middlewares", async () => {
|
|
161
|
+
it("should set the before middleware correctly", () => {
|
|
162
|
+
const name = "testRoute";
|
|
163
|
+
const routePath = "/posts";
|
|
164
|
+
const before = () => {};
|
|
165
|
+
|
|
166
|
+
Api.create(apiName, apiUrl, () => {
|
|
167
|
+
Route.get(name, routePath, {}).before(before);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(Registry.i.getRoute(apiName, name).callbacks.before)
|
|
171
|
+
.toEqual(before);
|
|
172
|
+
});
|
|
173
|
+
it("should set the after middleware correctly", () => {
|
|
174
|
+
const name = "testRoute";
|
|
175
|
+
const routePath = "/posts";
|
|
176
|
+
const after = () => {};
|
|
177
|
+
|
|
178
|
+
Api.create(apiName, apiUrl, () => {
|
|
179
|
+
Route.get(name, routePath, {}).after(after);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(Registry.i.getRoute(apiName, name).callbacks.after)
|
|
183
|
+
.toEqual(after);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Hook } from "../src/index.ts";
|
|
3
|
+
|
|
4
|
+
await describe("Route", async () => {
|
|
5
|
+
it("should subscribe to a hook", () => {
|
|
6
|
+
let a = 1;
|
|
7
|
+
|
|
8
|
+
Hook.subscribe("testHook", () => {
|
|
9
|
+
a++;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
Hook.run("testHook");
|
|
13
|
+
expect(a).toBe(2);
|
|
14
|
+
|
|
15
|
+
Hook.run("testHook");
|
|
16
|
+
expect(a).toBe(3);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should not run a hook if it is not subscribed", () => {
|
|
20
|
+
const a = 1;
|
|
21
|
+
|
|
22
|
+
Hook.run("testHook");
|
|
23
|
+
expect(a).toBe(1);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should not subscribe to a hook if no callback is provided", () => {
|
|
27
|
+
const a = 1;
|
|
28
|
+
Hook.subscribe("testHook", undefined);
|
|
29
|
+
|
|
30
|
+
Hook.run("testHook");
|
|
31
|
+
|
|
32
|
+
expect(a).toBe(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Api, Klaim, Registry, Route } from "../src/index.ts";
|
|
3
|
+
|
|
4
|
+
const apiName = "testApi";
|
|
5
|
+
const apiUrl = "https://jsonplaceholder.typicode.com";
|
|
6
|
+
|
|
7
|
+
const routeName = "testRoute";
|
|
8
|
+
const routeUrl = "/todos/1";
|
|
9
|
+
const routeUrlWithArg = "/todos/[id]";
|
|
10
|
+
const res = {
|
|
11
|
+
userId: 1,
|
|
12
|
+
id: 1,
|
|
13
|
+
title: "delectus aut autem",
|
|
14
|
+
completed: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const res2 = {
|
|
18
|
+
userId: 1,
|
|
19
|
+
id: 2,
|
|
20
|
+
title: "quis ut nam facilis et officia qui",
|
|
21
|
+
completed: false,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
await describe("Route", async () => {
|
|
25
|
+
it("should call the API", async () => {
|
|
26
|
+
Api.create(apiName, apiUrl, () => {
|
|
27
|
+
Route.get(routeName, routeUrl);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(Klaim[apiName]).toBeDefined();
|
|
31
|
+
expect(Klaim[apiName][routeName]).toBeDefined();
|
|
32
|
+
expect(Klaim[apiName][routeName]).toBeInstanceOf(Function);
|
|
33
|
+
expect(Klaim[apiName][routeName]()).toBeInstanceOf(Promise);
|
|
34
|
+
expect(Klaim[apiName][routeName]()).resolves.toEqual(res);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should have a body if post but not if get", async () => {
|
|
38
|
+
const apiNameA = `${apiName}A`;
|
|
39
|
+
const routeNameA = `${routeName}A`;
|
|
40
|
+
|
|
41
|
+
const apiNameB = `${apiName}B`;
|
|
42
|
+
const routeNameB = `${routeName}B`;
|
|
43
|
+
|
|
44
|
+
Api.create(apiNameA, apiUrl, () => {
|
|
45
|
+
Route.get(routeNameA, routeUrl);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
Api.create(apiNameB, apiUrl, () => {
|
|
49
|
+
Route.post(routeNameB, routeUrl);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(Klaim[apiNameA][routeNameA]()).resolves.toEqual(res);
|
|
53
|
+
expect(Klaim[apiNameB][routeNameB]()).resolves.toEqual({});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should can have arguments", async () => {
|
|
57
|
+
Api.create(apiName, apiUrl, () => {
|
|
58
|
+
Route.get(routeName, routeUrlWithArg);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(Klaim[apiName][routeName]({ id: 1 })).resolves.toEqual(res);
|
|
62
|
+
expect(Klaim[apiName][routeName]({ id: 2 })).resolves.toEqual(res2);
|
|
63
|
+
// if pass no argument, it should fail
|
|
64
|
+
expect(Klaim[apiName][routeName]()).rejects.toThrow();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should call the before and after hooks", async () => {
|
|
68
|
+
let a = 0;
|
|
69
|
+
Api.create(apiName, apiUrl, () => {
|
|
70
|
+
Route.get(routeName, routeUrl)
|
|
71
|
+
.before(() => {
|
|
72
|
+
a = 1;
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(a).toEqual(0);
|
|
77
|
+
Klaim[apiName][routeName]();
|
|
78
|
+
expect(a).toEqual(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should call the after hook", async () => {
|
|
82
|
+
let a = 0;
|
|
83
|
+
Api.create(apiName, apiUrl, () => {
|
|
84
|
+
Route.get(routeName, routeUrl)
|
|
85
|
+
.after(() => {
|
|
86
|
+
a = 1;
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(a).toEqual(0);
|
|
91
|
+
Klaim[apiName][routeName]();
|
|
92
|
+
expect(a).toEqual(0);
|
|
93
|
+
await Klaim[apiName][routeName]();
|
|
94
|
+
expect(a).toEqual(1);
|
|
95
|
+
});
|
|
96
|
+
});
|
package/vite.config.ts
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
|
-
import {defineConfig} from
|
|
2
|
-
import {configDefaults} from
|
|
3
|
-
import * as path from
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { configDefaults } from "vitest/config";
|
|
3
|
+
import * as path from "path";
|
|
4
4
|
|
|
5
5
|
export default defineConfig({
|
|
6
6
|
build: {
|
|
7
7
|
lib: {
|
|
8
|
-
entry: path.resolve(__dirname,
|
|
9
|
-
name:
|
|
10
|
-
fileName: format => {
|
|
11
|
-
if (format ===
|
|
8
|
+
entry: path.resolve(__dirname, "src/index.ts"),
|
|
9
|
+
name: "klaim",
|
|
10
|
+
fileName: (format) => {
|
|
11
|
+
if (format === "cjs") return `klaim.${format}`;
|
|
12
12
|
return `klaim.${format}.js`;
|
|
13
13
|
},
|
|
14
|
-
formats: [
|
|
14
|
+
formats: ["es", "cjs", "umd"],
|
|
15
15
|
},
|
|
16
16
|
rollupOptions: {
|
|
17
17
|
external: [],
|
|
18
18
|
output: {
|
|
19
|
-
globals: {}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
19
|
+
globals: {},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
22
|
},
|
|
23
23
|
test: {
|
|
24
24
|
globals: true,
|
|
25
|
-
environment:
|
|
25
|
+
environment: "jsdom",
|
|
26
|
+
exclude: [
|
|
27
|
+
...configDefaults.exclude,
|
|
28
|
+
"tests/e2e/**",
|
|
29
|
+
],
|
|
26
30
|
coverage: {
|
|
27
|
-
provider:
|
|
31
|
+
provider: "v8",
|
|
32
|
+
exclude: [
|
|
33
|
+
"**/*.cjs",
|
|
34
|
+
"index.ts",
|
|
35
|
+
"mod.ts",
|
|
36
|
+
"src/core/Registry.ts",
|
|
37
|
+
],
|
|
28
38
|
},
|
|
29
|
-
exclude: [...configDefaults.exclude, 'tests/e2e/**'],
|
|
30
39
|
},
|
|
31
40
|
});
|
package/src/tools/slugify.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slugify a text
|
|
3
|
-
*
|
|
4
|
-
* @param text - The text to slugify
|
|
5
|
-
* @param splitCharacter - The character to use to split the text
|
|
6
|
-
* @returns The slugified text
|
|
7
|
-
*/
|
|
8
|
-
export default function (text: string, splitCharacter: string = "_"): string {
|
|
9
|
-
return text
|
|
10
|
-
.toString()
|
|
11
|
-
.normalize("NFD") // split an accented letter in the base letter and the acent
|
|
12
|
-
.replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
|
|
13
|
-
.toLowerCase()
|
|
14
|
-
.trim()
|
|
15
|
-
.replace(/[^a-z0-9 ]/g, "") // remove all chars not letters, numbers and spaces (to be replaced)
|
|
16
|
-
.replace(/\s+/g, splitCharacter); // separator
|
|
17
|
-
}
|
package/src/tools/token.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate a new token
|
|
3
|
-
*
|
|
4
|
-
* @param {number} length The length of the token
|
|
5
|
-
* @returns {string} The generated token
|
|
6
|
-
*/
|
|
7
|
-
export default function (length?: number): string {
|
|
8
|
-
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
9
|
-
let token = "";
|
|
10
|
-
for (let i = 0; i < (length || 32); i++) {
|
|
11
|
-
token += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
12
|
-
}
|
|
13
|
-
return token;
|
|
14
|
-
}
|
package/tests/arguments.test.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import {describe, expect, it, vi} from 'vitest';
|
|
2
|
-
import {Route, RouteArgumentType} from '../src/core/Route';
|
|
3
|
-
|
|
4
|
-
// Mocks
|
|
5
|
-
vi.mock('../tools/token', () => ({
|
|
6
|
-
default: vi.fn(() => 'mocked-token'),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
vi.mock('../tools/slugify', () => ({
|
|
10
|
-
default: vi.fn((str: string) => str.replace(/\s+/g, '-').toLowerCase()),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
describe('Route.detectArgumentsOfUrl', () => {
|
|
14
|
-
it('should detect arguments of type ANY', () => {
|
|
15
|
-
const route = new Route('/user/[id]');
|
|
16
|
-
const args = route._arguments;
|
|
17
|
-
|
|
18
|
-
expect(args.size).toBe(1);
|
|
19
|
-
expect(Array.from(args)).toEqual([
|
|
20
|
-
{name: 'id', type: RouteArgumentType.ANY, default: null, required: true},
|
|
21
|
-
]);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should detect multiple arguments', () => {
|
|
25
|
-
const route = new Route('/posts/[post]/comments/[comment]');
|
|
26
|
-
const args = route._arguments;
|
|
27
|
-
|
|
28
|
-
expect(args.size).toBe(2);
|
|
29
|
-
expect(Array.from(args)).toEqual([
|
|
30
|
-
{name: 'post', type: RouteArgumentType.ANY, default: null, required: true},
|
|
31
|
-
{name: 'comment', type: RouteArgumentType.ANY, default: null, required: true},
|
|
32
|
-
]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should detect arguments with specific types', () => {
|
|
36
|
-
const route = new Route('/user/[id:number]');
|
|
37
|
-
const args = route._arguments;
|
|
38
|
-
|
|
39
|
-
expect(args.size).toBe(1);
|
|
40
|
-
expect(Array.from(args)).toEqual([
|
|
41
|
-
{name: 'id', type: RouteArgumentType.NUMBER, default: null, required: true},
|
|
42
|
-
]);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should detect arguments with default values', () => {
|
|
46
|
-
const route = new Route('/user/[id:number=1]');
|
|
47
|
-
const args = route._arguments;
|
|
48
|
-
|
|
49
|
-
expect(args.size).toBe(1);
|
|
50
|
-
expect(Array.from(args)).toEqual([
|
|
51
|
-
{name: 'id', type: RouteArgumentType.NUMBER, default: 1, required: true},
|
|
52
|
-
]);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should detect optional arguments', () => {
|
|
56
|
-
const route = new Route('/user/[name?]');
|
|
57
|
-
const args = route._arguments;
|
|
58
|
-
|
|
59
|
-
expect(args.size).toBe(1);
|
|
60
|
-
expect(Array.from(args)).toEqual([
|
|
61
|
-
{name: 'name', type: RouteArgumentType.ANY, default: null, required: false},
|
|
62
|
-
]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should detect multiple arguments with different types', () => {
|
|
66
|
-
const route = new Route('/profile/[user:string]/settings/[setting:boolean]');
|
|
67
|
-
const args = route._arguments;
|
|
68
|
-
|
|
69
|
-
expect(args.size).toBe(2);
|
|
70
|
-
expect(Array.from(args)).toEqual([
|
|
71
|
-
{name: 'user', type: RouteArgumentType.STRING, default: null, required: true},
|
|
72
|
-
{name: 'setting', type: RouteArgumentType.BOOLEAN, default: null, required: true},
|
|
73
|
-
]);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should handle invalid JSON in default value gracefully', () => {
|
|
77
|
-
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {
|
|
78
|
-
});
|
|
79
|
-
const route = new Route('/user/[id:number=invalid]');
|
|
80
|
-
const args = route._arguments;
|
|
81
|
-
|
|
82
|
-
expect(args.size).toBe(1);
|
|
83
|
-
expect(Array.from(args)).toEqual([
|
|
84
|
-
{name: 'id', type: RouteArgumentType.NUMBER, default: 'invalid', required: true},
|
|
85
|
-
]);
|
|
86
|
-
expect(consoleSpy).toHaveBeenCalled();
|
|
87
|
-
consoleSpy.mockRestore();
|
|
88
|
-
});
|
|
89
|
-
});
|