extreme-router 1.0.2 → 1.1.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/CHANGELOG.MD CHANGED
@@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [1.1.0] - 2025-05-18
10
+ ### Added
11
+ - Introduced the `allowRegisterUpdateExisting` option to the router constructor. When enabled (`true`), invoking `router.register()` on an already registered path will return the existing store instance for that path instead of throwing an error, allowing the store's data to be updated. When disabled (`false`, default), attempting to register an existing path will throw an error as before.
12
+
13
+ ### Fixed
14
+ - Ensured that when `allowRegisterUpdateExisting` is `true`, `router.register()` consistently returns the original store instance for an existing path, rather than creating a new instance. This guarantees that updates are applied to the correct store object.
15
+
9
16
  ## [1.0.2] - 2025-05-14
10
17
 
11
18
  ### Changed
package/README.md CHANGED
@@ -55,7 +55,7 @@ Extreme Router is designed for speed and flexibility. It uses an optimized radix
55
55
  - Optional Prefix Group Parameters (`img(png|jpg|gif)?`)
56
56
  - **TypeScript Native:** Written entirely in TypeScript with excellent type support.
57
57
  - **Zero Dependencies:** Lightweight and dependency-free core.
58
- - **Compact Size:** The core library is lightweight: **12.87 KB minified** / **3.81 KB gzipped** (ESM) and **13.44 KB minified** / **4.06 KB gzipped** (CJS).
58
+ - **Compact Size:** The core library is lightweight: **13.03 KB minified** / **3.85 KB gzipped** (ESM) and **13.60 KB minified** / **4.10 KB gzipped** (CJS).
59
59
  - **Well-Tested:** Comprehensive test suite ensuring reliability with **100% code coverage**.
60
60
  - **Benchmarked:** Performance is continuously monitored.
61
61
 
@@ -332,6 +332,7 @@ console.log(match3);
332
332
  - **`new Extreme<T>(options?: Options<T>)`**: Creates a new router instance.
333
333
  - `options.storeFactory`: A function that returns a new store object for each registered route. Defaults to `() => Object.create(null)`.
334
334
  - `options.plugins`: An array of plugin functions (`Plugin[]`) to register automatically when the router is created. Defaults to `[]`. Plugins will be applied (and sorted by priority) before any manual `router.use()` calls.
335
+ - `options.allowRegisterUpdateExisting`: If set to `true`, calling `router.register()` for a path that is already registered will not throw an error; instead, it will return the existing store object for that path, allowing you to update or modify its data. If `false` (default), attempting to register an already registered path will throw an error. This option only affects exact path matches and does not merge or update routes with different parameterizations or plugin handling.
335
336
  - **`router.use(plugin: Plugin): this`**: Registers a plugin function and returns the router instance, allowing method chaining.
336
337
  - Example:
337
338
  ```typescript
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "files": {
3
3
  "index.cjs": {
4
- "minified": "13.44 KB",
5
- "gzipped": "4.06 KB",
6
- "minifiedBytes": 13763,
7
- "gzippedBytes": 4161
4
+ "minified": "13.60 KB",
5
+ "gzipped": "4.10 KB",
6
+ "minifiedBytes": 13927,
7
+ "gzippedBytes": 4200
8
8
  },
9
9
  "index.js": {
10
- "minified": "12.87 KB",
11
- "gzipped": "3.81 KB",
12
- "minifiedBytes": 13180,
13
- "gzippedBytes": 3901
10
+ "minified": "13.03 KB",
11
+ "gzipped": "3.85 KB",
12
+ "minifiedBytes": 13344,
13
+ "gzippedBytes": 3942
14
14
  }
15
15
  },
16
16
  "total": {
17
- "minified": 26943,
18
- "gzipped": 8062,
19
- "minifiedKB": "26.31 KB",
20
- "gzippedKB": "7.87 KB"
17
+ "minified": 27271,
18
+ "gzipped": 8142,
19
+ "minifiedKB": "26.63 KB",
20
+ "gzippedKB": "7.95 KB"
21
21
  }
22
22
  }
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var x=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var j=(d,t)=>{for(var e in t)x(d,e,{get:t[e],enumerable:!0})},v=(d,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of A(t))!$.call(d,r)&&r!==e&&x(d,r,{get:()=>t[r],enumerable:!(i=H(t,r))||i.enumerable});return d};var F=d=>v(x({},"__esModule",{value:!0}),d);var E={};j(E,{default:()=>G,extensionParam:()=>w,groupParam:()=>O,optionalParam:()=>R,optionalPrefixGroup:()=>D,param:()=>S,prefixGroup:()=>C,regexParam:()=>M,wildcard:()=>b});module.exports=F(E);var N=(s=>(s[s.StoreIsNotFunction=0]="StoreIsNotFunction",s[s.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",s[s.StoreUnexpected=2]="StoreUnexpected",s[s.PathAlreadyRegistered=3]="PathAlreadyRegistered",s[s.PathIsEmpty=4]="PathIsEmpty",s[s.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",s[s.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",s[s.PluginIsNotFunction=7]="PluginIsNotFunction",s[s.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",s[s.PluginsOptionNotArray=9]="PluginsOptionNotArray",s[s.PluginMissingId=10]="PluginMissingId",s[s.PluginMissingPriority=11]="PluginMissingPriority",s[s.PluginIdIsNotString=12]="PluginIdIsNotString",s[s.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",s[s.PluginMissingSyntax=14]="PluginMissingSyntax",s[s.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",s[s.PluginMissingHandler=16]="PluginMissingHandler",s[s.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",s[s.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",s[s.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",s[s.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",s[s.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",s[s.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",s[s.PluginUnexpected=23]="PluginUnexpected",s[s.PluginDoesNotExist=24]="PluginDoesNotExist",s[s.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",s[s.WildcardNotAtEnd=26]="WildcardNotAtEnd",s))(N||{});var f=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[]};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(e=>{let i=this.validatePlugin(e);this.plugins.push(i)}),this.plugins.sort((e,i)=>e.priority-i.priority)}validateOptions(t){let e={...this.defaultOptions,...t};if(!e.storeFactory)e.storeFactory=this.defaultOptions.storeFactory;else if(typeof e.storeFactory!="function")this.throwError(0,typeof e.storeFactory);else{let i;try{i=e.storeFactory()}catch(r){this.throwError(2,r instanceof Error?r.message:String(r))}(typeof i!="object"||Array.isArray(i))&&this.throwError(1,typeof i)}return Array.isArray(e.plugins)||this.throwError(9,typeof e.plugins),e}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,e){if(N[t]!==void 0){let r=this.errorTypes[t](e);throw new Error(r)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let e=t.split("/").filter(Boolean),i=e.map((o,a)=>this.matchers.paramOptionalSegment.test(o)?a:-1).filter(o=>o>=0);if(i.length===0)return["/"+e.join("/")];let r=new Set,n=1<<i.length;for(let o=0;o<n;o++){let a=e.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(e[c]))return!0;let g=i.indexOf(c);return!!(o&1<<g)}),l=a.length===0?"/":"/"+a.join("/");r.add(l)}return Array.from(r)}registerStaticPath(t,e){this.staticPathCache[t]&&this.throwError(3,t);let i=Object.create(e??this.options.storeFactory());return this.staticPathCache[t]=i,i}registerDynamicPath(t,e){let i=t.split("/").filter(Boolean),r=this.root,n=e??this.options.storeFactory(),o=i.length;for(let a=0;a<o;a++){let l=i[a];if(this.matchers.staticSegment.test(l))r=r.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==o-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,r.dynamicChildren??=[];let h=r.dynamicChildren,p=h.find(P=>P.pluginMeta?.id===g.id&&P.pluginMeta?.paramName===g.paramName);if(p)r=p;else{h.find(y=>y.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let m=this.createNode();m.pluginMeta=g,h.push(m),h.sort((y,I)=>y.pluginMeta.priority-I.pluginMeta.priority),r=m}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return r.store&&r.pluginMeta?.override!==!0&&this.throwError(3,t),r.store=n,r.registeredPath=t,n}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let e=this.root,i=t.split("/").filter(Boolean),{unregistered:r}=this.cleanupTraversal(e,i,0);return r}cleanupTraversal(t,e,i){let r=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,n=a=>r(a)&&!a.store;if(i===e.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:r(t),unregistered:!0}):{shouldDelete:r(t),unregistered:!1};let o=e[i];if(this.matchers.staticSegment.test(o)){let a=t.staticChildren[o];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,e,i+1);if(l)return delete t.staticChildren[o],{shouldDelete:n(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],h=g.pluginMeta,p=u?.handler(o);if(h?.id===u?.id&&h?.paramName===p?.paramName){let{shouldDelete:P,unregistered:m}=this.cleanupTraversal(g,e,i+1);if(P)return a.splice(c,1),{shouldDelete:n(t),unregistered:m};if(m)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let e;try{e=t()}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}(!e||typeof e!="object")&&this.throwError(8,typeof e),e.id||this.throwError(10),typeof e.id!="string"&&this.throwError(12,typeof e.id),this.plugins.some(o=>o.id===e.id)&&this.throwError(5,e.id),e.priority||this.throwError(11),typeof e.priority!="number"&&this.throwError(13,typeof e.priority),this.plugins.some(o=>o.priority===e.priority)&&this.throwError(6,String(e.priority)),e.syntax||this.throwError(14),typeof e.syntax!="string"&&this.throwError(15,typeof e.syntax),e.handler||this.throwError(16,e.id),typeof e.handler!="function"&&this.throwError(7,typeof e.handler);let i,r=e.syntax;try{i=e.handler(r)}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}i==null&&this.throwError(18,r),(!i||typeof i!="object")&&this.throwError(19,typeof i),i.match||this.throwError(20),typeof i.match!="function"&&this.throwError(21,typeof i.match);let n;try{n=i.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}return typeof n!="boolean"&&this.throwError(22,typeof n),e}use(t){let e=this.validatePlugin(t);return this.plugins.push(e),this.plugins.sort((i,r)=>i.priority-r.priority),this}inspect(){let t=[];for(let r in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,r)){let n=this.staticPathCache[r];t.push({path:r,type:"static",store:n})}let e=new Set,i=r=>{r.store&&r.registeredPath&&(e.has(r.registeredPath)||(t.push({path:r.registeredPath,type:"dynamic",store:r.store}),e.add(r.registeredPath)));for(let n in r.staticChildren)Object.prototype.hasOwnProperty.call(r.staticChildren,n)&&i(r.staticChildren[n]);if(r.dynamicChildren)for(let n of r.dynamicChildren)i(n)};return i(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),i=this.options.storeFactory();for(let r of e)this.matchers.staticPath.test(r)?this.registerStaticPath(r,i):this.registerDynamicPath(r,i);return i}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),i=!1;for(let r of e)this.matchers.staticPath.test(r)?i=this.unregisterStaticPath(r):i=this.unregisterDynamicPath(r);return i}return this.unregisterDynamicPath(t)}match(t){let e=this.staticPathCache[t];if(e)return e;let i=t.split("/").filter(Boolean),r=i.length,n=this.root,o=Object.create(null),a=0;for(;a<r;a++){let l=i[a],u=n.staticChildren[l];if(u){n=u;continue}let c=n.dynamicChildren;if(c){let g=!1;for(let h=0;h<c.length;h++){let p=c[h],P=p?.pluginMeta;if(P?.match({urlSegment:l,urlSegments:i,index:a,params:o})){if(P.wildcard&&p?.store!==void 0){let m=Object.create(p.store);return m.params=o,m}n=p,g=!0;break}}if(!g)return null}else return null}if(a===r&&n.store){let l=Object.create(n.store);return l.params=o,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(r);if(!n||!n.groups||!n.groups.paramName)return null;let o=n.groups.paramName;return{paramName:o,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:r=>{let n=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(r);if(!n)return null;let o=n.groups?.wildcardName??"*";return{paramName:o,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let h=l+1;h<g;h++)c+="/"+a[h];return u[o]=c||"",!0}}}});var M=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.regex)return null;let o=n.groups.paramName,a=new RegExp(`^${n.groups.regex}$`);return{paramName:o,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[o]=c[0],!0):!1}}}});var w=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.extension)return null;let o=n.groups.paramName,a=n.groups.extension;return{paramName:o,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[o]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.dynamicGroup)return null;let o=n.groups.paramName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:o,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[o]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:r=>{let n=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!n||!n.groups||!n.groups.staticName||!n.groups.dynamicGroup)return null;let o=n.groups.staticName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:r=>{let n=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(r);if(!n||!n.groups||!n.groups.staticName||!n.groups.dynamicGroup)return null;let o=n.groups.staticName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return l[o]=o,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(r);if(!n||!n.groups||!n.groups.paramName)return null;let o=n.groups.paramName;return{paramName:o,override:!0,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var G=f;0&&(module.exports={extensionParam,groupParam,optionalParam,optionalPrefixGroup,param,prefixGroup,regexParam,wildcard});
1
+ "use strict";var x=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var j=(d,t)=>{for(var e in t)x(d,e,{get:t[e],enumerable:!0})},v=(d,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of A(t))!$.call(d,r)&&r!==e&&x(d,r,{get:()=>t[r],enumerable:!(n=H(t,r))||n.enumerable});return d};var F=d=>v(x({},"__esModule",{value:!0}),d);var G={};j(G,{default:()=>E,extensionParam:()=>M,groupParam:()=>O,optionalParam:()=>D,optionalPrefixGroup:()=>R,param:()=>S,prefixGroup:()=>C,regexParam:()=>w,wildcard:()=>b});module.exports=F(G);var N=(o=>(o[o.StoreIsNotFunction=0]="StoreIsNotFunction",o[o.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",o[o.StoreUnexpected=2]="StoreUnexpected",o[o.PathAlreadyRegistered=3]="PathAlreadyRegistered",o[o.PathIsEmpty=4]="PathIsEmpty",o[o.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",o[o.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",o[o.PluginIsNotFunction=7]="PluginIsNotFunction",o[o.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",o[o.PluginsOptionNotArray=9]="PluginsOptionNotArray",o[o.PluginMissingId=10]="PluginMissingId",o[o.PluginMissingPriority=11]="PluginMissingPriority",o[o.PluginIdIsNotString=12]="PluginIdIsNotString",o[o.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",o[o.PluginMissingSyntax=14]="PluginMissingSyntax",o[o.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",o[o.PluginMissingHandler=16]="PluginMissingHandler",o[o.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",o[o.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",o[o.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",o[o.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",o[o.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",o[o.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",o[o.PluginUnexpected=23]="PluginUnexpected",o[o.PluginDoesNotExist=24]="PluginDoesNotExist",o[o.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",o[o.WildcardNotAtEnd=26]="WildcardNotAtEnd",o))(N||{});var f=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[],allowRegisterUpdateExisting:!1};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(e=>{let n=this.validatePlugin(e);this.plugins.push(n)}),this.plugins.sort((e,n)=>e.priority-n.priority)}validateOptions(t){let e={...this.defaultOptions,...t};if(!e.storeFactory)e.storeFactory=this.defaultOptions.storeFactory;else if(typeof e.storeFactory!="function")this.throwError(0,typeof e.storeFactory);else{let n;try{n=e.storeFactory()}catch(r){this.throwError(2,r instanceof Error?r.message:String(r))}(typeof n!="object"||Array.isArray(n))&&this.throwError(1,typeof n)}return Array.isArray(e.plugins)||this.throwError(9,typeof e.plugins),e}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,e){if(N[t]!==void 0){let r=this.errorTypes[t](e);throw new Error(r)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let e=t.split("/").filter(Boolean),n=e.map((s,a)=>this.matchers.paramOptionalSegment.test(s)?a:-1).filter(s=>s>=0);if(n.length===0)return["/"+e.join("/")];let r=new Set,i=1<<n.length;for(let s=0;s<i;s++){let a=e.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(e[c]))return!0;let g=n.indexOf(c);return!!(s&1<<g)}),l=a.length===0?"/":"/"+a.join("/");r.add(l)}return Array.from(r)}registerStaticPath(t,e){if(this.staticPathCache[t])return this.options.allowRegisterUpdateExisting||this.throwError(3,t),this.staticPathCache[t];let n=Object.create(e??this.options.storeFactory());return this.staticPathCache[t]=n,n}registerDynamicPath(t,e){let n=t.split("/").filter(Boolean),r=this.root,i=e??this.options.storeFactory(),s=n.length;for(let a=0;a<s;a++){let l=n[a];if(this.matchers.staticSegment.test(l))r=r.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==s-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,r.dynamicChildren??=[];let h=r.dynamicChildren,p=h.find(P=>P.pluginMeta?.id===g.id&&P.pluginMeta?.paramName===g.paramName);if(p)r=p;else{h.find(y=>y.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let m=this.createNode();m.pluginMeta=g,h.push(m),h.sort((y,I)=>y.pluginMeta.priority-I.pluginMeta.priority),r=m}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return r.store&&r.pluginMeta?.override!==!0&&!this.options.allowRegisterUpdateExisting&&this.throwError(3,t),r.store=r.store??i,r.registeredPath=t,r.store}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let e=this.root,n=t.split("/").filter(Boolean),{unregistered:r}=this.cleanupTraversal(e,n,0);return r}cleanupTraversal(t,e,n){let r=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,i=a=>r(a)&&!a.store;if(n===e.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:r(t),unregistered:!0}):{shouldDelete:r(t),unregistered:!1};let s=e[n];if(this.matchers.staticSegment.test(s)){let a=t.staticChildren[s];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,e,n+1);if(l)return delete t.staticChildren[s],{shouldDelete:i(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],h=g.pluginMeta,p=u?.handler(s);if(h?.id===u?.id&&h?.paramName===p?.paramName){let{shouldDelete:P,unregistered:m}=this.cleanupTraversal(g,e,n+1);if(P)return a.splice(c,1),{shouldDelete:i(t),unregistered:m};if(m)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let e;try{e=t()}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}(!e||typeof e!="object")&&this.throwError(8,typeof e),e.id||this.throwError(10),typeof e.id!="string"&&this.throwError(12,typeof e.id),this.plugins.some(s=>s.id===e.id)&&this.throwError(5,e.id),e.priority||this.throwError(11),typeof e.priority!="number"&&this.throwError(13,typeof e.priority),this.plugins.some(s=>s.priority===e.priority)&&this.throwError(6,String(e.priority)),e.syntax||this.throwError(14),typeof e.syntax!="string"&&this.throwError(15,typeof e.syntax),e.handler||this.throwError(16,e.id),typeof e.handler!="function"&&this.throwError(7,typeof e.handler);let n,r=e.syntax;try{n=e.handler(r)}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}n==null&&this.throwError(18,r),(!n||typeof n!="object")&&this.throwError(19,typeof n),n.match||this.throwError(20),typeof n.match!="function"&&this.throwError(21,typeof n.match);let i;try{i=n.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}return typeof i!="boolean"&&this.throwError(22,typeof i),e}use(t){let e=this.validatePlugin(t);return this.plugins.push(e),this.plugins.sort((n,r)=>n.priority-r.priority),this}inspect(){let t=[];for(let r in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,r)){let i=this.staticPathCache[r];t.push({path:r,type:"static",store:i})}let e=new Set,n=r=>{r.store&&r.registeredPath&&(e.has(r.registeredPath)||(t.push({path:r.registeredPath,type:"dynamic",store:r.store}),e.add(r.registeredPath)));for(let i in r.staticChildren)Object.prototype.hasOwnProperty.call(r.staticChildren,i)&&n(r.staticChildren[i]);if(r.dynamicChildren)for(let i of r.dynamicChildren)n(i)};return n(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=this.options.storeFactory();for(let r of e)this.matchers.staticPath.test(r)?this.registerStaticPath(r,n):this.registerDynamicPath(r,n);return n}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=!1;for(let r of e)this.matchers.staticPath.test(r)?n=this.unregisterStaticPath(r):n=this.unregisterDynamicPath(r);return n}return this.unregisterDynamicPath(t)}match(t){let e=this.staticPathCache[t];if(e)return e;let n=t.split("/").filter(Boolean),r=n.length,i=this.root,s=Object.create(null),a=0;for(;a<r;a++){let l=n[a],u=i.staticChildren[l];if(u){i=u;continue}let c=i.dynamicChildren;if(c){let g=!1;for(let h=0;h<c.length;h++){let p=c[h],P=p?.pluginMeta;if(P?.match({urlSegment:l,urlSegments:n,index:a,params:s})){if(P.wildcard&&p?.store!==void 0){let m=Object.create(p.store);return m.params=s,m}i=p,g=!0;break}}if(!g)return null}else return null}if(a===r&&i.store){let l=Object.create(i.store);return l.params=s,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(r);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:r=>{let i=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(r);if(!i)return null;let s=i.groups?.wildcardName??"*";return{paramName:s,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let h=l+1;h<g;h++)c+="/"+a[h];return u[s]=c||"",!0}}}});var w=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.regex)return null;let s=i.groups.paramName,a=new RegExp(`^${i.groups.regex}$`);return{paramName:s,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[s]=c[0],!0):!1}}}});var M=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.extension)return null;let s=i.groups.paramName,a=i.groups.extension;return{paramName:s,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[s]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.dynamicGroup)return null;let s=i.groups.paramName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:s,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[s]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:r=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:r=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(r);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return l[s]=s,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(r);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,override:!0,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var E=f;0&&(module.exports={extensionParam,groupParam,optionalParam,optionalPrefixGroup,param,prefixGroup,regexParam,wildcard});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../src/types.ts","../src/router.ts","../src/plugins/param.ts","../src/plugins/wildcard.ts","../src/plugins/regexParam.ts","../src/plugins/extensionParam.ts","../src/plugins/groupParam.ts","../src/plugins/prefixGroup.ts","../src/plugins/optionalPrefixGroup.ts","../src/plugins/optionalParam.ts"],"sourcesContent":["import Extreme from './src/router';\nimport type {\n Match,\n Options,\n Plugin,\n PluginConfig,\n PluginHandler,\n PluginMeta,\n ListedRoute,\n ErrorTypes,\n} from './src/types';\nimport { param } from './src/plugins/param';\nimport { wildcard } from './src/plugins/wildcard';\nimport { regexParam } from './src/plugins/regexParam';\nimport { extensionParam } from './src/plugins/extensionParam';\nimport { groupParam } from './src/plugins/groupParam';\nimport { prefixGroup } from './src/plugins/prefixGroup';\nimport { optionalPrefixGroup } from './src/plugins/optionalPrefixGroup';\nimport { optionalParam } from './src/plugins/optionalParam';\n\nexport default Extreme;\nexport type { Match, Options, Plugin, PluginConfig, PluginHandler, PluginMeta, ListedRoute, ErrorTypes };\nexport { param, wildcard, regexParam, extensionParam, groupParam, prefixGroup, optionalPrefixGroup, optionalParam };\n","export type Store = object;\n\nexport type PluginHandler = (segment: string) => PluginMeta | undefined | null;\n\nexport interface PluginConfig {\n id: string;\n priority: number;\n syntax: string;\n handler: PluginHandler;\n}\n\nexport type Plugin = () => PluginConfig;\n\nexport interface PluginMeta {\n // Priority, id, syntax are automatically added by the plugin manager according to the plugin config\n // So no need to add them in the plugin meta\n // They are used here for matching priority logic (Important!), id and syntax for debugging\n paramName: string;\n priority?: number;\n id?: string;\n syntax?: string;\n override?: boolean;\n wildcard?: boolean;\n additionalMeta?: {\n group?: Record<string | number, unknown>;\n regex?: RegExp;\n extension?: string;\n [k: string]: unknown;\n };\n match: ({\n urlSegment,\n urlSegments,\n index,\n params,\n }: {\n urlSegment: string;\n urlSegments: string[];\n index: number;\n params: Record<string, unknown>;\n }) => boolean;\n}\n\nexport interface Node<T extends Store = Store> {\n registeredPath?: string;\n staticChildren: Record<string, Node<T>>;\n dynamicChildren?: Node<T>[];\n pluginMeta?: PluginMeta;\n store?: T;\n}\n\nexport interface Options<T extends Store = Store> {\n storeFactory: () => T;\n plugins: Plugin[];\n}\n\nexport type Match<T extends Store = Store> = T & {\n params: Record<string, string>;\n};\n\nexport enum ErrorTypes {\n StoreIsNotFunction,\n StoreDoesNotReturnObject,\n StoreUnexpected,\n PathAlreadyRegistered,\n PathIsEmpty,\n PluginWithSameIdAlreadyExists,\n PluginWithSamePriorityAlreadyExists,\n PluginIsNotFunction,\n PluginDoesNotReturnObject,\n PluginsOptionNotArray,\n PluginMissingId,\n PluginMissingPriority,\n PluginIdIsNotString,\n PluginPriorityIsNotNumber,\n PluginMissingSyntax,\n PluginSyntaxIsNotString,\n PluginMissingHandler,\n PluginHandlerIsNotFunction,\n PluginHandlerReturnNullOrUndefinedForSyntax,\n PluginHandlerDoesNotReturnObject,\n PluginHandlerMissingMatch,\n PluginHandlerMatchIsNotFunction,\n PluginHandlerMatchDoesNotReturnBoolean,\n PluginUnexpected,\n PluginDoesNotExist,\n DynamicSegmentAlreadyExists,\n WildcardNotAtEnd,\n}\n\nexport interface ListedRoute<T extends Store> {\n path: string;\n type: 'static' | 'dynamic';\n store: T;\n}\n\nexport interface CleanupTraversalResult {\n shouldDelete: boolean;\n unregistered: boolean;\n}\n","import {\n ErrorTypes,\n type Match,\n type Node,\n type Options,\n type Plugin,\n type PluginConfig,\n type PluginMeta,\n type Store,\n type ListedRoute,\n type CleanupTraversalResult,\n} from './types';\n\n/**\n * High-performance, modular, tree-based router with plugin support.\n *\n * @template T The type of the store object associated with each route. Defaults to `Store`.\n */\nexport default class Extreme<T extends Store = Store> {\n /**\n * Creates a new Extreme router instance.\n * @param {Partial<Options<T>>} [options={}] Configuration options for the router.\n * @param {() => T} [options.storeFactory=() => Object.create(null)] A function that returns a new store object for each registered route.\n */\n constructor(options: Partial<Options<T>> = {}) {\n this.options = this.validateOptions(options);\n this.staticPathCache = Object.create(null);\n this.root = this.createNode();\n this.errorTypes = this.createErrorTypes();\n this.plugins = [];\n if (this.options.plugins.length > 0) {\n this.loadPlugins(this.options.plugins);\n }\n }\n\n /**\n * Default configuration options for the router.\n * @protected\n */\n protected defaultOptions: Options<T> = {\n storeFactory: () => Object.create(null),\n plugins: [],\n };\n\n /**\n * The validated configuration options for this router instance.\n * @protected\n */\n protected options: Options<T>;\n\n /**\n * Cache for quickly matching static paths (O(1) lookup).\n * Keys are static paths, values are the associated route stores.\n * @protected\n */\n protected staticPathCache: Record<string, Match<T>>;\n\n /**\n * The root node of the radix tree used for dynamic path matching.\n * @protected\n */\n protected root: Node<T>;\n\n /**\n * Array storing the registered plugin configurations, sorted by priority.\n * @protected\n */\n protected plugins: PluginConfig[];\n\n /**\n * Map of error types to their corresponding error message generators.\n * @protected\n */\n protected errorTypes: Record<ErrorTypes, (inline?: string) => string>;\n\n /**\n * Regular expressions used for matching path segments and types.\n * @protected\n */\n protected matchers = {\n staticPath: /^(?:\\/|\\/?(?:[a-zA-Z0-9 _.-]+)(?:\\/[a-zA-Z0-9 _.-]+)*)$/,\n paramOptionalInPath: /\\/:[a-zA-Z0-9_-]+\\?/,\n staticSegment: /^[a-zA-Z0-9 _.-]+$/,\n paramOptionalSegment: /^:[a-zA-Z0-9_-]+\\?$/,\n };\n\n /**\n * Loads and registers an array of plugins, validating each plugin before adding it to the internal plugins list.\n * After all plugins are added, the list is sorted by priority in ascending order (lower number indicates higher precedence).\n *\n * @param plugins - An array of plugins to be loaded and registered.\n */\n private loadPlugins(plugins: Plugin[]): void | never {\n plugins.forEach((plugin) => {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n });\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Validates the provided router options and merges them with defaults.\n * @param {Partial<Options<T>>} options The options provided to the constructor.\n * @returns {Options<T>} The validated and merged options.\n * @throws {Error} If the storeFactory is invalid.\n * @private\n */\n private validateOptions(options: Partial<Options<T>>): Options<T> {\n const finalOptions = { ...this.defaultOptions, ...options };\n // Store validation\n if (!finalOptions.storeFactory) {\n finalOptions.storeFactory = this.defaultOptions.storeFactory;\n } else if (typeof finalOptions.storeFactory !== 'function') {\n this.throwError(ErrorTypes.StoreIsNotFunction, typeof finalOptions.storeFactory);\n } else {\n let storeObject: T;\n try {\n storeObject = finalOptions.storeFactory();\n } catch (error) {\n this.throwError(ErrorTypes.StoreUnexpected, error instanceof Error ? error.message : String(error));\n }\n if (typeof storeObject !== 'object' || Array.isArray(storeObject)) {\n this.throwError(ErrorTypes.StoreDoesNotReturnObject, typeof storeObject);\n }\n }\n if (!Array.isArray(finalOptions.plugins)) {\n this.throwError(ErrorTypes.PluginsOptionNotArray, typeof finalOptions.plugins);\n }\n return finalOptions;\n }\n\n /**\n * Creates the map of error type enums to error message generator functions.\n * @returns {Record<ErrorTypes, (inline?: string) => string>} The error types map.\n * @private\n */\n private createErrorTypes(): typeof this.errorTypes {\n return {\n [ErrorTypes.StoreIsNotFunction]: (inline?: string) => `Store is not a function: ${inline}`,\n [ErrorTypes.StoreDoesNotReturnObject]: (inline?: string) => `Store does not return an object: ${inline}`,\n [ErrorTypes.StoreUnexpected]: (inline?: string) => `Store unexpected error: ${inline}`,\n [ErrorTypes.PathAlreadyRegistered]: (inline?: string) => `Path already registered: ${inline}`,\n [ErrorTypes.PluginWithSameIdAlreadyExists]: (inline?: string) => `Plugin with same ID already exists: ${inline}`,\n [ErrorTypes.PluginWithSamePriorityAlreadyExists]: (inline?: string) =>\n `Plugin with same priority already exists: ${inline}`,\n [ErrorTypes.PluginIsNotFunction]: (inline?: string) => `Plugin is not a function: ${inline}`,\n [ErrorTypes.PluginDoesNotReturnObject]: (inline?: string) => `Plugin does not return an object: ${inline}`,\n [ErrorTypes.PluginsOptionNotArray]: (inline?: string) => `Plugins option must be an array, got: ${inline}`,\n [ErrorTypes.PluginMissingId]: () => 'Plugin missing ID',\n [ErrorTypes.PluginMissingPriority]: () => 'Plugin missing priority',\n [ErrorTypes.PluginIdIsNotString]: (inline?: string) => `Plugin ID is not a string: ${inline}`,\n [ErrorTypes.PluginPriorityIsNotNumber]: (inline?: string) => `Plugin priority is not a number: ${inline}`,\n [ErrorTypes.PluginMissingSyntax]: () => 'Plugin missing syntax',\n [ErrorTypes.PluginSyntaxIsNotString]: (inline?: string) => `Plugin syntax is not a string: ${inline}`,\n [ErrorTypes.PluginMissingHandler]: (inline?: string) => `Plugin missing handler: ${inline}`,\n [ErrorTypes.PluginHandlerIsNotFunction]: (inline?: string) => `Plugin handler is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax]: (inline?: string) =>\n `Plugin handler returned null or undefined while matching syntax: ${inline}`,\n [ErrorTypes.PluginHandlerDoesNotReturnObject]: (inline?: string) =>\n `Plugin handler does not return an object: ${inline}`,\n [ErrorTypes.PluginHandlerMissingMatch]: () => 'Plugin handler missing match function',\n [ErrorTypes.PluginHandlerMatchIsNotFunction]: (inline?: string) =>\n `Plugin handler match is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean]: (inline?: string) =>\n `Plugin handler match does not return a boolean: ${inline}`,\n [ErrorTypes.PluginUnexpected]: (inline?: string) => `Plugin unexpected error: ${inline}`,\n [ErrorTypes.PluginDoesNotExist]: (inline?: string) => `Plugin does not exist for: ${inline}`,\n [ErrorTypes.DynamicSegmentAlreadyExists]: (inline?: string) => `Dynamic segment already exists: ${inline}`,\n [ErrorTypes.WildcardNotAtEnd]: () => 'Wildcard must be at the end of the path',\n [ErrorTypes.PathIsEmpty]: () => 'Path cannot be empty',\n };\n }\n\n /**\n * Throws a formatted error based on the ErrorTypes enum.\n * @param {ErrorTypes} type The type of error to throw.\n * @param {string} [inline] Optional additional context for the error message.\n * @throws {Error} Always throws an error.\n * @protected\n */\n protected throwError(type: ErrorTypes, inline?: string): never {\n const errorType = ErrorTypes[type];\n if (errorType !== undefined) {\n const errorMessage = this.errorTypes[type](inline);\n throw new Error(errorMessage);\n }\n throw new Error('Unknown error type');\n }\n\n /**\n * Creates a new, empty node for the routing tree.\n * @returns {Node<T>} A new node object.\n * @private\n */\n private createNode(): Node<T> {\n return {\n staticChildren: Object.create(null),\n };\n }\n\n /**\n * Generates all possible path combinations for a route containing optional parameters.\n * For example, '/a/:b?/:c?' generates ['/a', '/a/:b', '/a/:c', '/a/:b/:c'].\n * Note: This currently generates combinations based on presence, not order permutations if multiple optionals are adjacent.\n * @param {string} path The path string containing optional parameters (e.g., '/users/:id?').\n * @returns {string[]} An array of path strings representing all combinations.\n * @protected\n */\n protected generateOptionals(path: string): string[] {\n // Split path into segments\n const segments = path.split('/').filter(Boolean);\n\n // Identify optional segments\n const optionalIndexes = segments\n .map((s, i) => (this.matchers.paramOptionalSegment.test(s) ? i : -1))\n .filter((i) => i >= 0);\n\n // If no optional params, return original path\n if (optionalIndexes.length === 0) {\n return ['/' + segments.join('/')];\n }\n\n // Generate all combinations using bitwise operations\n const combinations: Set<string> = new Set();\n const total = 1 << optionalIndexes.length; // 2^n combinations\n\n for (let mask = 0; mask < total; mask++) {\n const includedSegments = segments.filter((_segment, idx) => {\n // Always include non-optional segments\n if (!this.matchers.paramOptionalSegment.test(segments[idx]!)) {\n return true;\n }\n // Include optional segment if corresponding bit is set\n const bit = optionalIndexes.indexOf(idx);\n return !!(mask & (1 << bit));\n });\n\n // Build path string for this combination\n const combinationPath = includedSegments.length === 0 ? '/' : '/' + includedSegments.join('/');\n\n combinations.add(combinationPath);\n }\n\n return Array.from(combinations);\n }\n\n /**\n * Registers a static path directly into the static path cache.\n * @param {string} path The static path to register.\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path is already registered.\n * @private\n */\n private registerStaticPath(path: string, store?: T): Match<T> | never {\n if (this.staticPathCache[path]) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n // No params in static path, so we can use the store directly\n // Even though Match<T> has params definitions and here we not return params,\n // I decided to leave the return type as Match<T> so TypeScript does not recognize params as unknown\n const newStore = Object.create(store ?? this.options.storeFactory());\n this.staticPathCache[path] = newStore;\n return newStore;\n }\n\n /**\n * Registers a dynamic path into the radix tree.\n * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path conflicts with an existing registration or uses invalid syntax.\n * @private\n */\n private registerDynamicPath(path: string, store?: T): T | never {\n const segments = path.split('/').filter(Boolean);\n let currentNode: Node<T> = this.root;\n const newStore = store ?? this.options.storeFactory();\n const segmentsLength = segments.length;\n for (let i = 0; i < segmentsLength; i++) {\n const segment = segments[i] as string;\n //Static segment\n if (this.matchers.staticSegment.test(segment)) {\n currentNode = currentNode.staticChildren[segment] ??= this.createNode();\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n let pluginMatch = false;\n for (const plugin of this.plugins) {\n const pluginMeta = plugin.handler(segment);\n if (pluginMeta !== null && pluginMeta !== undefined) {\n if (pluginMeta.wildcard && i !== segmentsLength - 1) {\n this.throwError(ErrorTypes.WildcardNotAtEnd, segment);\n }\n\n // Set plugin match flag\n pluginMatch = true;\n // Copy the plugin required properties to the pluginMeta object\n pluginMeta.id = plugin.id;\n pluginMeta.priority = plugin.priority;\n pluginMeta.syntax = plugin.syntax;\n\n // Ensure dynamicChildren array exists\n currentNode.dynamicChildren ??= [];\n const existingDynamicChildren = currentNode.dynamicChildren;\n\n // 1. Check if a node for this exact parameter (same plugin id, same param name) already exists\n const existingNodeForParam = existingDynamicChildren.find(\n (child) => child.pluginMeta?.id === pluginMeta.id && child.pluginMeta?.paramName === pluginMeta.paramName,\n );\n\n if (existingNodeForParam) {\n // Exact match found, reuse this node\n currentNode = existingNodeForParam;\n } else {\n // 2. No exact match. Check for conflicts: Does another dynamic node handled by the *same plugin* exist?\n const conflictingNode = existingDynamicChildren.find((child) => child.pluginMeta?.id === pluginMeta.id);\n\n if (conflictingNode) {\n // Conflict found. Throw error unless the *new* segment allows overriding.\n if (pluginMeta.override !== true) {\n this.throwError(\n ErrorTypes.DynamicSegmentAlreadyExists,\n `${segment} (Plugin ID conflict: ${pluginMeta.id})`,\n );\n }\n // If override is true, we allow adding a new node (handled below).\n }\n // 3. No exact match and no conflict (or override allowed), create a new node.\n const newNode: Node<T> = this.createNode();\n newNode.pluginMeta = pluginMeta;\n existingDynamicChildren.push(newNode);\n // Keep dynamic children sorted by priority for matching\n // ! assertion: pluginMeta and priority are guaranteed to exist here\n existingDynamicChildren.sort((a, b) => a.pluginMeta!.priority! - b.pluginMeta!.priority!);\n currentNode = newNode;\n }\n // Break out of the loop since we found a plugin match\n break;\n }\n }\n // No plugin match found after checking all plugins\n if (!pluginMatch) {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n // No plugin found\n else {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n\n if (currentNode.store && currentNode.pluginMeta?.override !== true) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n\n currentNode.store = newStore;\n currentNode.registeredPath = path;\n return newStore;\n }\n\n /**\n * Unregisters a static path from the routing tree.\n * @param {string} path The static path to unregister (e.g., '/users/123').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterStaticPath(path: string): boolean {\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n delete this.staticPathCache[path]; // Remove from static path cache\n return true; // Successfully unregistered\n }\n return false; // Path not found in static cache\n }\n\n /**\n * Unregisters a dynamic path from the routing tree.\n * @param {string} path The dynamic path to unregister (e.g., '/users/:id', '/files/*').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterDynamicPath(path: string): boolean {\n const rootNode = this.root;\n // Segments length are always greater than 0. (If not, it would be a static path)\n const segments = path.split('/').filter(Boolean);\n const { unregistered } = this.cleanupTraversal(rootNode, segments, 0);\n return unregistered;\n }\n\n /**\n * Recursively traverses and cleans up the dynamic routing tree to unregister a dynamic path.\n * @param node The current node in the tree.\n * @param segment The current segment being processed.\n * @param segments The full array of path segments.\n * @param index The current index in the segments array.\n * @returns {boolean} True if the node should be deleted from its parent, false otherwise.\n */\n private cleanupTraversal(node: Node<T>, segments: string[], index: number): CleanupTraversalResult {\n // Helper function to check if a node is empty (no children)\n const isNodeEmpty = (node: Node<T>): boolean =>\n Object.keys(node.staticChildren).length === 0 && !node.dynamicChildren?.length;\n\n // Helper function to check if a node can be deleted (empty and no store)\n const canDeleteNode = (node: Node<T>): boolean => isNodeEmpty(node) && !node.store;\n\n // Last segment reached\n if (index === segments.length) {\n if (node.store) {\n node.store = undefined; // Clear the store\n node.registeredPath = undefined; // Clear the registered path\n return { shouldDelete: isNodeEmpty(node), unregistered: true };\n }\n return { shouldDelete: isNodeEmpty(node), unregistered: false }; // No store to clear\n }\n\n const segment = segments[index] as string;\n\n // Static segment\n if (this.matchers.staticSegment.test(segment)) {\n const staticChild = node.staticChildren[segment];\n if (staticChild) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(staticChild, segments, index + 1);\n if (shouldDelete) {\n delete node.staticChildren[segment]; // Remove the static child\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n const dynamicChildren = node.dynamicChildren;\n if (dynamicChildren) {\n for (let i = 0; i < this.plugins.length; i++) {\n const pluginConfig = this.plugins[i];\n for (let j = 0; j < dynamicChildren.length; j++) {\n const childNode = dynamicChildren[j] as Node<T>;\n const childMeta = childNode.pluginMeta;\n const pluginMeta = pluginConfig?.handler(segment);\n if (childMeta?.id === pluginConfig?.id && childMeta?.paramName === pluginMeta?.paramName) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(childNode, segments, index + 1);\n if (shouldDelete) {\n // Remove the child node from the dynamic children array\n dynamicChildren.splice(j, 1);\n // Check if the parent node can be deleted\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n }\n }\n }\n return { shouldDelete: false, unregistered: false };\n }\n\n /**\n * Validates a plugin and its configuration.\n * @param {Plugin} plugin The plugin function to validate.\n * @returns {PluginConfig} The validated plugin configuration.\n * @throws {Error} If the plugin is invalid or fails validation checks.\n * @protected\n */\n protected validatePlugin(plugin: Plugin): PluginConfig | never {\n // Check if the plugin is a function\n if (typeof plugin !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof plugin);\n }\n let pluginConfig: PluginConfig;\n // Call the plugin function to get the configuration\n try {\n pluginConfig = plugin();\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginConfig is an object\n if (!pluginConfig || typeof pluginConfig !== 'object') {\n this.throwError(ErrorTypes.PluginDoesNotReturnObject, typeof pluginConfig);\n }\n // Check if the pluginConfig has a id, priority\n if (!pluginConfig.id) {\n this.throwError(ErrorTypes.PluginMissingId);\n }\n // Check if the pluginConfig id is a string\n if (typeof pluginConfig.id !== 'string') {\n this.throwError(ErrorTypes.PluginIdIsNotString, typeof pluginConfig.id);\n }\n // Check if the pluginConfig id is already registered\n if (this.plugins.some((p) => p.id === pluginConfig.id)) {\n this.throwError(ErrorTypes.PluginWithSameIdAlreadyExists, pluginConfig.id);\n }\n // Check if the pluginConfig has a priority\n if (!pluginConfig.priority) {\n this.throwError(ErrorTypes.PluginMissingPriority);\n }\n // Check if the pluginConfig priority is a number\n if (typeof pluginConfig.priority !== 'number') {\n this.throwError(ErrorTypes.PluginPriorityIsNotNumber, typeof pluginConfig.priority);\n }\n // Check if the pluginConfig priority is already registered\n if (this.plugins.some((p) => p.priority === pluginConfig.priority)) {\n this.throwError(ErrorTypes.PluginWithSamePriorityAlreadyExists, String(pluginConfig.priority));\n }\n // Check if the pluginConfig has a syntax\n if (!pluginConfig.syntax) {\n this.throwError(ErrorTypes.PluginMissingSyntax);\n }\n // Check if the pluginConfig syntax is a string\n if (typeof pluginConfig.syntax !== 'string') {\n this.throwError(ErrorTypes.PluginSyntaxIsNotString, typeof pluginConfig.syntax);\n }\n // Check if the pluginConfig has no handler\n if (!pluginConfig.handler) {\n this.throwError(ErrorTypes.PluginMissingHandler, pluginConfig.id);\n }\n // Check if the pluginConfig has a handler\n if (typeof pluginConfig.handler !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof pluginConfig.handler);\n }\n\n let pluginMeta: PluginMeta | undefined | null;\n const syntax = pluginConfig.syntax;\n // Call the pluginConfig.handler to get the pluginMeta\n try {\n pluginMeta = pluginConfig.handler(syntax);\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginMeta is not null or undefined\n // This is important when it return null or undefined it means that the plugin unsuccessfully hanldled the given syntax\n if (pluginMeta === null || pluginMeta === undefined) {\n this.throwError(ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax, syntax);\n }\n // Check if the pluginMeta is an object\n if (!pluginMeta || typeof pluginMeta !== 'object') {\n this.throwError(ErrorTypes.PluginHandlerDoesNotReturnObject, typeof pluginMeta);\n }\n // Check if the pluginMeta has a match function\n if (!pluginMeta.match) {\n this.throwError(ErrorTypes.PluginHandlerMissingMatch);\n }\n // Check if the pluginMeta match is a function\n if (typeof pluginMeta.match !== 'function') {\n this.throwError(ErrorTypes.PluginHandlerMatchIsNotFunction, typeof pluginMeta.match);\n }\n\n let matchResult: boolean;\n // Call the pluginMeta.match to get the match result\n try {\n matchResult = pluginMeta.match({ urlSegment: '', urlSegments: [''], index: 0, params: {} });\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the matchResult is a boolean. we dont care now about the exact value.\n if (typeof matchResult !== 'boolean') {\n this.throwError(ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean, typeof matchResult);\n }\n return pluginConfig;\n }\n\n /**\n * Registers a plugin with the router.\n * Plugins extend the router's ability to handle custom parameter types or wildcards in dynamic routes.\n * Plugins are validated and added in order of their `priority` (lower number = higher precedence).\n * @param {Plugin} plugin The plugin function to register.\n * @returns {this} The router instance (for chaining).\n * @throws {Error} If the plugin is invalid or conflicts with an existing plugin.\n * @public\n */\n public use(plugin: Plugin): this | never {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n return this;\n }\n\n /**\n * Retrieves a list of all registered routes.\n * Useful for debugging or administrative purposes.\n * @returns {ListedRoute<T>[]} An array of objects, each describing a registered route.\n * @public\n */\n public inspect(): ListedRoute<T>[] {\n const listedRoute: ListedRoute<T>[] = [];\n // 1. Static Routes\n for (const path in this.staticPathCache) {\n if (Object.prototype.hasOwnProperty.call(this.staticPathCache, path)) {\n const staticStore = this.staticPathCache[path] as T;\n listedRoute.push({\n path: path,\n type: 'static',\n store: staticStore,\n });\n }\n }\n\n // 2. Dynamic Routes\n // Use a Set to ensure each unique registeredPath is added only once,\n const addedDynamicRegisteredPaths = new Set<string>();\n\n const traverse = (node: Node<T>) => {\n // If a node has a store and a registeredPath, it's a terminal node for a dynamic route.\n if (node.store && node.registeredPath) {\n if (!addedDynamicRegisteredPaths.has(node.registeredPath)) {\n listedRoute.push({\n path: node.registeredPath, // Use the stored registeredPath\n type: 'dynamic', // Routes from the tree with registeredPath are dynamic\n store: node.store,\n });\n addedDynamicRegisteredPaths.add(node.registeredPath);\n }\n }\n\n // Traverse static children\n for (const segment in node.staticChildren) {\n if (Object.prototype.hasOwnProperty.call(node.staticChildren, segment)) {\n traverse(node.staticChildren[segment]!);\n }\n }\n\n // Traverse dynamic children\n if (node.dynamicChildren) {\n for (const childNode of node.dynamicChildren) {\n traverse(childNode);\n }\n }\n };\n\n traverse(this.root); // Start traversal from the root for dynamic paths\n\n return listedRoute;\n }\n\n /**\n * Registers a route path with the router.\n * Handles static paths, dynamic paths with parameters/wildcards (requires plugins),\n * and paths with optional parameters.\n * @param {string} path The route path to register.\n * @returns {T} The store object associated with this route. You can add route-specific data to this object.\n * @throws {Error} If the path is invalid, conflicts with an existing route, or requires an unregistered plugin.\n * @public\n */\n public register(path: string): T | never {\n if (!path) {\n this.throwError(ErrorTypes.PathIsEmpty);\n }\n // Static path registration\n if (this.matchers.staticPath.test(path)) {\n return this.registerStaticPath(path);\n }\n // Param optional in path registration\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n // Define a shared store for all generated paths\n const sharedStore: T = this.options.storeFactory();\n // Register each generated path with the shared store\n for (const generatedPath of generatedPaths) {\n // For static paths, register them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n this.registerStaticPath(generatedPath, sharedStore);\n }\n // For dynamic paths, register them in root tree\n else {\n this.registerDynamicPath(generatedPath, sharedStore);\n }\n }\n return sharedStore;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.registerDynamicPath(path);\n }\n\n public unregister(path: string): boolean {\n // Check if the path is a static path\n if (this.matchers.staticPath.test(path)) {\n return this.unregisterStaticPath(path);\n }\n // Check if the path is has optional params\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n let success = false;\n for (const generatedPath of generatedPaths) {\n // For static paths, unregister them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n success = this.unregisterStaticPath(generatedPath);\n }\n // For dynamic paths, unregister them in root tree\n else {\n success = this.unregisterDynamicPath(generatedPath);\n }\n }\n return success;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.unregisterDynamicPath(path);\n }\n\n /**\n * Matches a given path against the registered routes.\n * Checks the static cache first, then traverses the radix tree for dynamic matches.\n * @param {string} path The path to match (e.g., '/users/123').\n * @returns {Match<T> | T | null}\n * - The exact store object `T` if a static path matches.\n * - A `Match<T>` object (the store object `T` augmented with a `params` property) if a dynamic path matches.\n * - `null` if no matching route is found.\n * @public\n */\n public match(path: string): Match<T> | null {\n // 1. Check static path cache (Fast O(1))\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n return cachedStore;\n }\n\n // 2. Prepare for dynamic path traversal\n const segments = path.split('/').filter(Boolean);\n const segmentCount = segments.length; // Cache length\n let currentNode: Node<T> = this.root;\n const params: Record<string, string> = Object.create(null);\n let i = 0;\n\n // 3. Traverse segments\n for (; i < segmentCount; i++) {\n const segment = segments[i] as string;\n\n // 3a. Check static children first (Fast O(1) average)\n const staticChild = currentNode.staticChildren[segment];\n if (staticChild) {\n currentNode = staticChild;\n continue; // Move to the next segment\n }\n // 3b. Check dynamic children (Potentially O(N) where N is num dynamic children)\n const dynamicChildren = currentNode.dynamicChildren;\n if (dynamicChildren) {\n let dynamicMatchFound = false;\n // Iterate through dynamic children (sorted by priority during registration)\n for (let j = 0; j < dynamicChildren.length; j++) {\n const dynamicChildNode = dynamicChildren[j] as Node<T>;\n const pluginMeta = dynamicChildNode?.pluginMeta;\n if (pluginMeta?.match({ urlSegment: segment, urlSegments: segments, index: i, params })) {\n // If it's a wildcard match, return immediately.\n if (pluginMeta.wildcard && dynamicChildNode?.store !== undefined) {\n const matchResult = Object.create(dynamicChildNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n currentNode = dynamicChildNode; // Move to the matched dynamic node\n dynamicMatchFound = true;\n break; // Stop checking other dynamic children for this segment (priority respected)\n }\n }\n\n // If no dynamic child matched this segment after checking all possibilities, return null\n if (!dynamicMatchFound) {\n return null;\n }\n }\n // 3c. No static child and no dynamic children array at this node, return null\n else {\n return null;\n }\n }\n\n // 4. End of segments reached. Check if the final node has a store.\n // Ensure we consumed all segments (i === segmentCount)\n if (i === segmentCount && currentNode.store) {\n // Create result object inheriting from store\n const matchResult = Object.create(currentNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n // 5. End of segments reached, but no store at the final node, or not all segments consumed\n return null;\n }\n}\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle basic parameters.\n * Syntax: `:paramName`\n */\nexport const param: Plugin = () => {\n const id = 'param';\n const priority = 700;\n const syntax = ':paramName';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle wildcard parameters.\n * Syntax: `*` or `:wildcardName*`\n */\nexport const wildcard: Plugin = () => {\n const id = 'wildcard';\n const priority = 800;\n const syntax = '*';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?:\\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\\*)$/.exec(segment);\n if (!match) {\n return null;\n }\n const paramName = match.groups?.wildcardName ?? '*';\n return {\n paramName,\n wildcard: true,\n match({ urlSegments, index, params }) {\n let rest = urlSegments[index];\n const segmentsLength = urlSegments.length;\n for (let j = index + 1; j < segmentsLength; j++) {\n rest += '/' + urlSegments[j];\n }\n params[paramName] = rest || '';\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with regex validation.\n * Syntax: `:paramName<\\d+>`\n */\nexport const regexParam: Plugin = () => {\n const id = 'regexParam';\n const priority = 400;\n const syntax = ':paramName<\\\\d+>';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.regex) {\n return null;\n }\n const paramName = match.groups.paramName;\n const regex = new RegExp(`^${match.groups.regex}$`);\n return {\n paramName,\n additionalMeta: {\n regex,\n },\n match({ urlSegment, params }) {\n const match = urlSegment.match(regex);\n if (!match) return false;\n params[paramName] = match[0];\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with a specific file extension.\n * Syntax: `:file.css`\n */\nexport const extensionParam: Plugin = () => {\n const id = 'extensionParam';\n const priority = 500;\n const syntax = ':file.css';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\.(?<extension>.+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.extension) {\n return null;\n }\n const paramName = match.groups.paramName;\n const extension = match.groups.extension;\n return {\n paramName,\n additionalMeta: {\n extension,\n },\n match({ urlSegment, params }) {\n if (!urlSegment.endsWith(extension)) return false;\n params[paramName] = urlSegment.slice(0, -(extension.length + 1));\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle grouped parameters.\n * Syntax: `:paramName(a|b)`\n */\nexport const groupParam: Plugin = () => {\n const id = 'groupParam';\n const priority = 300;\n const syntax = ':paramName(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.dynamicGroup) {\n return null;\n }\n const paramName = match.groups.paramName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [g, g]));\n return {\n paramName,\n additionalMeta: { group },\n match({ urlSegment, params }) {\n if (!group || !group[urlSegment]) return false;\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle prefix groups.\n * Syntax: `prefix(a|b)`\n */\nexport const prefixGroup: Plugin = () => {\n const id = 'prefixGroup';\n const priority = 100;\n const syntax = 'prefix(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional prefix groups.\n * Syntax: `prefix(a|b)?`\n */\nexport const optionalPrefixGroup: Plugin = () => {\n const id = 'optionalPrefixGroup';\n const priority = 200;\n const syntax = 'prefix(a|b)?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n group[staticName] = staticName;\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional parameters.\n * Syntax: `:paramName?`\n */\nexport const optionalParam: Plugin = () => {\n const id = 'optionalParam';\n const priority = 600;\n const syntax = ':paramName?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n override: true,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n"],"mappings":"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","names":["index_exports","__export","index_default","extensionParam","groupParam","optionalParam","optionalPrefixGroup","param","prefixGroup","regexParam","wildcard","__toCommonJS","ErrorTypes","Extreme","options","plugins","plugin","pluginConfig","a","b","finalOptions","storeObject","error","inline","type","ErrorTypes","errorMessage","path","segments","optionalIndexes","s","i","combinations","total","mask","includedSegments","_segment","idx","bit","combinationPath","store","newStore","currentNode","segmentsLength","segment","pluginMatch","pluginMeta","existingDynamicChildren","existingNodeForParam","child","newNode","rootNode","unregistered","node","index","isNodeEmpty","canDeleteNode","staticChild","shouldDelete","dynamicChildren","j","childNode","childMeta","p","syntax","matchResult","listedRoute","staticStore","addedDynamicRegisteredPaths","traverse","generatedPaths","sharedStore","generatedPath","success","cachedStore","segmentCount","params","dynamicMatchFound","dynamicChildNode","param","segment","match","paramName","urlSegment","params","wildcard","segment","match","paramName","urlSegments","index","params","rest","segmentsLength","j","regexParam","segment","match","paramName","regex","urlSegment","params","extensionParam","segment","match","paramName","extension","urlSegment","params","groupParam","segment","match","paramName","dynamicGroup","group","g","urlSegment","params","prefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalPrefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalParam","segment","match","paramName","urlSegment","params","index_default","Extreme"]}
1
+ {"version":3,"sources":["../index.ts","../src/types.ts","../src/router.ts","../src/plugins/param.ts","../src/plugins/wildcard.ts","../src/plugins/regexParam.ts","../src/plugins/extensionParam.ts","../src/plugins/groupParam.ts","../src/plugins/prefixGroup.ts","../src/plugins/optionalPrefixGroup.ts","../src/plugins/optionalParam.ts"],"sourcesContent":["import Extreme from './src/router';\nimport type {\n Match,\n Options,\n Plugin,\n PluginConfig,\n PluginHandler,\n PluginMeta,\n ListedRoute,\n ErrorTypes,\n} from './src/types';\nimport { param } from './src/plugins/param';\nimport { wildcard } from './src/plugins/wildcard';\nimport { regexParam } from './src/plugins/regexParam';\nimport { extensionParam } from './src/plugins/extensionParam';\nimport { groupParam } from './src/plugins/groupParam';\nimport { prefixGroup } from './src/plugins/prefixGroup';\nimport { optionalPrefixGroup } from './src/plugins/optionalPrefixGroup';\nimport { optionalParam } from './src/plugins/optionalParam';\n\nexport default Extreme;\nexport type { Match, Options, Plugin, PluginConfig, PluginHandler, PluginMeta, ListedRoute, ErrorTypes };\nexport { param, wildcard, regexParam, extensionParam, groupParam, prefixGroup, optionalPrefixGroup, optionalParam };\n","export type Store = object;\n\nexport type PluginHandler = (segment: string) => PluginMeta | undefined | null;\n\nexport interface PluginConfig {\n id: string;\n priority: number;\n syntax: string;\n handler: PluginHandler;\n}\n\nexport type Plugin = () => PluginConfig;\n\nexport interface PluginMeta {\n // Priority, id, syntax are automatically added by the plugin manager according to the plugin config\n // So no need to add them in the plugin meta\n // They are used here for matching priority logic (Important!), id and syntax for debugging\n paramName: string;\n priority?: number;\n id?: string;\n syntax?: string;\n override?: boolean;\n wildcard?: boolean;\n additionalMeta?: {\n group?: Record<string | number, unknown>;\n regex?: RegExp;\n extension?: string;\n [k: string]: unknown;\n };\n match: ({\n urlSegment,\n urlSegments,\n index,\n params,\n }: {\n urlSegment: string;\n urlSegments: string[];\n index: number;\n params: Record<string, unknown>;\n }) => boolean;\n}\n\nexport interface Node<T extends Store = Store> {\n registeredPath?: string;\n staticChildren: Record<string, Node<T>>;\n dynamicChildren?: Node<T>[];\n pluginMeta?: PluginMeta;\n store?: T;\n}\n\nexport interface Options<T extends Store = Store> {\n storeFactory: () => T;\n plugins: Plugin[];\n allowRegisterUpdateExisting?: boolean;\n}\n\nexport type Match<T extends Store = Store> = T & {\n params: Record<string, string>;\n};\n\nexport enum ErrorTypes {\n StoreIsNotFunction,\n StoreDoesNotReturnObject,\n StoreUnexpected,\n PathAlreadyRegistered,\n PathIsEmpty,\n PluginWithSameIdAlreadyExists,\n PluginWithSamePriorityAlreadyExists,\n PluginIsNotFunction,\n PluginDoesNotReturnObject,\n PluginsOptionNotArray,\n PluginMissingId,\n PluginMissingPriority,\n PluginIdIsNotString,\n PluginPriorityIsNotNumber,\n PluginMissingSyntax,\n PluginSyntaxIsNotString,\n PluginMissingHandler,\n PluginHandlerIsNotFunction,\n PluginHandlerReturnNullOrUndefinedForSyntax,\n PluginHandlerDoesNotReturnObject,\n PluginHandlerMissingMatch,\n PluginHandlerMatchIsNotFunction,\n PluginHandlerMatchDoesNotReturnBoolean,\n PluginUnexpected,\n PluginDoesNotExist,\n DynamicSegmentAlreadyExists,\n WildcardNotAtEnd,\n}\n\nexport interface ListedRoute<T extends Store> {\n path: string;\n type: 'static' | 'dynamic';\n store: T;\n}\n\nexport interface CleanupTraversalResult {\n shouldDelete: boolean;\n unregistered: boolean;\n}\n","import {\n ErrorTypes,\n type Match,\n type Node,\n type Options,\n type Plugin,\n type PluginConfig,\n type PluginMeta,\n type Store,\n type ListedRoute,\n type CleanupTraversalResult,\n} from './types';\n\n/**\n * High-performance, modular, tree-based router with plugin support.\n *\n * @template T The type of the store object associated with each route. Defaults to `Store`.\n */\nexport default class Extreme<T extends Store = Store> {\n /**\n * Creates a new Extreme router instance.\n * @param {Partial<Options<T>>} [options={}] Configuration options for the router.\n * @param {() => T} [options.storeFactory=() => Object.create(null)] A function that returns a new store object for each registered route.\n */\n constructor(options: Partial<Options<T>> = {}) {\n this.options = this.validateOptions(options);\n this.staticPathCache = Object.create(null);\n this.root = this.createNode();\n this.errorTypes = this.createErrorTypes();\n this.plugins = [];\n if (this.options.plugins.length > 0) {\n this.loadPlugins(this.options.plugins);\n }\n }\n\n /**\n * Default configuration options for the router.\n * @protected\n */\n protected defaultOptions: Options<T> = {\n storeFactory: () => Object.create(null),\n plugins: [],\n allowRegisterUpdateExisting: false,\n };\n\n /**\n * The validated configuration options for this router instance.\n * @protected\n */\n protected options: Options<T>;\n\n /**\n * Cache for quickly matching static paths (O(1) lookup).\n * Keys are static paths, values are the associated route stores.\n * @protected\n */\n protected staticPathCache: Record<string, Match<T>>;\n\n /**\n * The root node of the radix tree used for dynamic path matching.\n * @protected\n */\n protected root: Node<T>;\n\n /**\n * Array storing the registered plugin configurations, sorted by priority.\n * @protected\n */\n protected plugins: PluginConfig[];\n\n /**\n * Map of error types to their corresponding error message generators.\n * @protected\n */\n protected errorTypes: Record<ErrorTypes, (inline?: string) => string>;\n\n /**\n * Regular expressions used for matching path segments and types.\n * @protected\n */\n protected matchers = {\n staticPath: /^(?:\\/|\\/?(?:[a-zA-Z0-9 _.-]+)(?:\\/[a-zA-Z0-9 _.-]+)*)$/,\n paramOptionalInPath: /\\/:[a-zA-Z0-9_-]+\\?/,\n staticSegment: /^[a-zA-Z0-9 _.-]+$/,\n paramOptionalSegment: /^:[a-zA-Z0-9_-]+\\?$/,\n };\n\n /**\n * Loads and registers an array of plugins, validating each plugin before adding it to the internal plugins list.\n * After all plugins are added, the list is sorted by priority in ascending order (lower number indicates higher precedence).\n *\n * @param plugins - An array of plugins to be loaded and registered.\n */\n private loadPlugins(plugins: Plugin[]): void | never {\n plugins.forEach((plugin) => {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n });\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Validates the provided router options and merges them with defaults.\n * @param {Partial<Options<T>>} options The options provided to the constructor.\n * @returns {Options<T>} The validated and merged options.\n * @throws {Error} If the storeFactory is invalid.\n * @private\n */\n private validateOptions(options: Partial<Options<T>>): Options<T> {\n const finalOptions = { ...this.defaultOptions, ...options };\n // Store validation\n if (!finalOptions.storeFactory) {\n finalOptions.storeFactory = this.defaultOptions.storeFactory;\n } else if (typeof finalOptions.storeFactory !== 'function') {\n this.throwError(ErrorTypes.StoreIsNotFunction, typeof finalOptions.storeFactory);\n } else {\n let storeObject: T;\n try {\n storeObject = finalOptions.storeFactory();\n } catch (error) {\n this.throwError(ErrorTypes.StoreUnexpected, error instanceof Error ? error.message : String(error));\n }\n if (typeof storeObject !== 'object' || Array.isArray(storeObject)) {\n this.throwError(ErrorTypes.StoreDoesNotReturnObject, typeof storeObject);\n }\n }\n if (!Array.isArray(finalOptions.plugins)) {\n this.throwError(ErrorTypes.PluginsOptionNotArray, typeof finalOptions.plugins);\n }\n return finalOptions;\n }\n\n /**\n * Creates the map of error type enums to error message generator functions.\n * @returns {Record<ErrorTypes, (inline?: string) => string>} The error types map.\n * @private\n */\n private createErrorTypes(): typeof this.errorTypes {\n return {\n [ErrorTypes.StoreIsNotFunction]: (inline?: string) => `Store is not a function: ${inline}`,\n [ErrorTypes.StoreDoesNotReturnObject]: (inline?: string) => `Store does not return an object: ${inline}`,\n [ErrorTypes.StoreUnexpected]: (inline?: string) => `Store unexpected error: ${inline}`,\n [ErrorTypes.PathAlreadyRegistered]: (inline?: string) => `Path already registered: ${inline}`,\n [ErrorTypes.PluginWithSameIdAlreadyExists]: (inline?: string) => `Plugin with same ID already exists: ${inline}`,\n [ErrorTypes.PluginWithSamePriorityAlreadyExists]: (inline?: string) =>\n `Plugin with same priority already exists: ${inline}`,\n [ErrorTypes.PluginIsNotFunction]: (inline?: string) => `Plugin is not a function: ${inline}`,\n [ErrorTypes.PluginDoesNotReturnObject]: (inline?: string) => `Plugin does not return an object: ${inline}`,\n [ErrorTypes.PluginsOptionNotArray]: (inline?: string) => `Plugins option must be an array, got: ${inline}`,\n [ErrorTypes.PluginMissingId]: () => 'Plugin missing ID',\n [ErrorTypes.PluginMissingPriority]: () => 'Plugin missing priority',\n [ErrorTypes.PluginIdIsNotString]: (inline?: string) => `Plugin ID is not a string: ${inline}`,\n [ErrorTypes.PluginPriorityIsNotNumber]: (inline?: string) => `Plugin priority is not a number: ${inline}`,\n [ErrorTypes.PluginMissingSyntax]: () => 'Plugin missing syntax',\n [ErrorTypes.PluginSyntaxIsNotString]: (inline?: string) => `Plugin syntax is not a string: ${inline}`,\n [ErrorTypes.PluginMissingHandler]: (inline?: string) => `Plugin missing handler: ${inline}`,\n [ErrorTypes.PluginHandlerIsNotFunction]: (inline?: string) => `Plugin handler is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax]: (inline?: string) =>\n `Plugin handler returned null or undefined while matching syntax: ${inline}`,\n [ErrorTypes.PluginHandlerDoesNotReturnObject]: (inline?: string) =>\n `Plugin handler does not return an object: ${inline}`,\n [ErrorTypes.PluginHandlerMissingMatch]: () => 'Plugin handler missing match function',\n [ErrorTypes.PluginHandlerMatchIsNotFunction]: (inline?: string) =>\n `Plugin handler match is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean]: (inline?: string) =>\n `Plugin handler match does not return a boolean: ${inline}`,\n [ErrorTypes.PluginUnexpected]: (inline?: string) => `Plugin unexpected error: ${inline}`,\n [ErrorTypes.PluginDoesNotExist]: (inline?: string) => `Plugin does not exist for: ${inline}`,\n [ErrorTypes.DynamicSegmentAlreadyExists]: (inline?: string) => `Dynamic segment already exists: ${inline}`,\n [ErrorTypes.WildcardNotAtEnd]: () => 'Wildcard must be at the end of the path',\n [ErrorTypes.PathIsEmpty]: () => 'Path cannot be empty',\n };\n }\n\n /**\n * Throws a formatted error based on the ErrorTypes enum.\n * @param {ErrorTypes} type The type of error to throw.\n * @param {string} [inline] Optional additional context for the error message.\n * @throws {Error} Always throws an error.\n * @protected\n */\n protected throwError(type: ErrorTypes, inline?: string): never {\n const errorType = ErrorTypes[type];\n if (errorType !== undefined) {\n const errorMessage = this.errorTypes[type](inline);\n throw new Error(errorMessage);\n }\n throw new Error('Unknown error type');\n }\n\n /**\n * Creates a new, empty node for the routing tree.\n * @returns {Node<T>} A new node object.\n * @private\n */\n private createNode(): Node<T> {\n return {\n staticChildren: Object.create(null),\n };\n }\n\n /**\n * Generates all possible path combinations for a route containing optional parameters.\n * For example, '/a/:b?/:c?' generates ['/a', '/a/:b', '/a/:c', '/a/:b/:c'].\n * Note: This currently generates combinations based on presence, not order permutations if multiple optionals are adjacent.\n * @param {string} path The path string containing optional parameters (e.g., '/users/:id?').\n * @returns {string[]} An array of path strings representing all combinations.\n * @protected\n */\n protected generateOptionals(path: string): string[] {\n // Split path into segments\n const segments = path.split('/').filter(Boolean);\n\n // Identify optional segments\n const optionalIndexes = segments\n .map((s, i) => (this.matchers.paramOptionalSegment.test(s) ? i : -1))\n .filter((i) => i >= 0);\n\n // If no optional params, return original path\n if (optionalIndexes.length === 0) {\n return ['/' + segments.join('/')];\n }\n\n // Generate all combinations using bitwise operations\n const combinations: Set<string> = new Set();\n const total = 1 << optionalIndexes.length; // 2^n combinations\n\n for (let mask = 0; mask < total; mask++) {\n const includedSegments = segments.filter((_segment, idx) => {\n // Always include non-optional segments\n if (!this.matchers.paramOptionalSegment.test(segments[idx]!)) {\n return true;\n }\n // Include optional segment if corresponding bit is set\n const bit = optionalIndexes.indexOf(idx);\n return !!(mask & (1 << bit));\n });\n\n // Build path string for this combination\n const combinationPath = includedSegments.length === 0 ? '/' : '/' + includedSegments.join('/');\n\n combinations.add(combinationPath);\n }\n\n return Array.from(combinations);\n }\n\n /**\n * Registers a static path directly into the static path cache.\n * @param {string} path The static path to register.\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path is already registered and `allowRegisterUpdateExisting` is false.\n * @private\n */\n private registerStaticPath(path: string, store?: T): Match<T> | never {\n if (this.staticPathCache[path]) {\n if (!this.options.allowRegisterUpdateExisting) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n return this.staticPathCache[path];\n }\n const newStore = Object.create(store ?? this.options.storeFactory());\n this.staticPathCache[path] = newStore;\n // No params in static path, so we can use the store directly\n // Even though Match<T> has params definitions and here we not return params,\n // I decided to leave the return type as Match<T> so TypeScript does not recognize params as unknown\n return newStore;\n }\n\n /**\n * Registers a dynamic path into the radix tree.\n * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path conflicts with an existing registration (with allowRegisterUpdateExisting = false) or uses invalid syntax.\n * @private\n */\n private registerDynamicPath(path: string, store?: T): T | never {\n const segments = path.split('/').filter(Boolean);\n let currentNode: Node<T> = this.root;\n const newStore = store ?? this.options.storeFactory();\n const segmentsLength = segments.length;\n for (let i = 0; i < segmentsLength; i++) {\n const segment = segments[i] as string;\n //Static segment\n if (this.matchers.staticSegment.test(segment)) {\n currentNode = currentNode.staticChildren[segment] ??= this.createNode();\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n let pluginMatch = false;\n for (const plugin of this.plugins) {\n const pluginMeta = plugin.handler(segment);\n if (pluginMeta !== null && pluginMeta !== undefined) {\n if (pluginMeta.wildcard && i !== segmentsLength - 1) {\n this.throwError(ErrorTypes.WildcardNotAtEnd, segment);\n }\n\n // Set plugin match flag\n pluginMatch = true;\n // Copy the plugin required properties to the pluginMeta object\n pluginMeta.id = plugin.id;\n pluginMeta.priority = plugin.priority;\n pluginMeta.syntax = plugin.syntax;\n\n // Ensure dynamicChildren array exists\n currentNode.dynamicChildren ??= [];\n const existingDynamicChildren = currentNode.dynamicChildren;\n\n // 1. Check if a node for this exact parameter (same plugin id, same param name) already exists\n const existingNodeForParam = existingDynamicChildren.find(\n (child) => child.pluginMeta?.id === pluginMeta.id && child.pluginMeta?.paramName === pluginMeta.paramName,\n );\n\n if (existingNodeForParam) {\n // Exact match found, reuse this node\n currentNode = existingNodeForParam;\n } else {\n // 2. No exact match. Check for conflicts: Does another dynamic node handled by the *same plugin* exist?\n const conflictingNode = existingDynamicChildren.find((child) => child.pluginMeta?.id === pluginMeta.id);\n\n if (conflictingNode) {\n // Conflict found. Throw error unless the *new* segment allows overriding.\n if (pluginMeta.override !== true) {\n this.throwError(\n ErrorTypes.DynamicSegmentAlreadyExists,\n `${segment} (Plugin ID conflict: ${pluginMeta.id})`,\n );\n }\n // If override is true, we allow adding a new node (handled below).\n }\n // 3. No exact match and no conflict (or override allowed), create a new node.\n const newNode: Node<T> = this.createNode();\n newNode.pluginMeta = pluginMeta;\n existingDynamicChildren.push(newNode);\n // Keep dynamic children sorted by priority for matching\n // ! assertion: pluginMeta and priority are guaranteed to exist here\n existingDynamicChildren.sort((a, b) => a.pluginMeta!.priority! - b.pluginMeta!.priority!);\n currentNode = newNode;\n }\n // Break out of the loop since we found a plugin match\n break;\n }\n }\n // No plugin match found after checking all plugins\n if (!pluginMatch) {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n // No plugin found\n else {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n\n if (currentNode.store && currentNode.pluginMeta?.override !== true && !this.options.allowRegisterUpdateExisting) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n currentNode.store = currentNode.store ?? newStore;\n currentNode.registeredPath = path;\n return currentNode.store;\n }\n\n /**\n * Unregisters a static path from the routing tree.\n * @param {string} path The static path to unregister (e.g., '/users/123').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterStaticPath(path: string): boolean {\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n delete this.staticPathCache[path]; // Remove from static path cache\n return true; // Successfully unregistered\n }\n return false; // Path not found in static cache\n }\n\n /**\n * Unregisters a dynamic path from the routing tree.\n * @param {string} path The dynamic path to unregister (e.g., '/users/:id', '/files/*').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterDynamicPath(path: string): boolean {\n const rootNode = this.root;\n // Segments length are always greater than 0. (If not, it would be a static path)\n const segments = path.split('/').filter(Boolean);\n const { unregistered } = this.cleanupTraversal(rootNode, segments, 0);\n return unregistered;\n }\n\n /**\n * Recursively traverses and cleans up the dynamic routing tree to unregister a dynamic path.\n * @param node The current node in the tree.\n * @param segment The current segment being processed.\n * @param segments The full array of path segments.\n * @param index The current index in the segments array.\n * @returns {boolean} True if the node should be deleted from its parent, false otherwise.\n */\n private cleanupTraversal(node: Node<T>, segments: string[], index: number): CleanupTraversalResult {\n // Helper function to check if a node is empty (no children)\n const isNodeEmpty = (node: Node<T>): boolean =>\n Object.keys(node.staticChildren).length === 0 && !node.dynamicChildren?.length;\n\n // Helper function to check if a node can be deleted (empty and no store)\n const canDeleteNode = (node: Node<T>): boolean => isNodeEmpty(node) && !node.store;\n\n // Last segment reached\n if (index === segments.length) {\n if (node.store) {\n node.store = undefined; // Clear the store\n node.registeredPath = undefined; // Clear the registered path\n return { shouldDelete: isNodeEmpty(node), unregistered: true };\n }\n return { shouldDelete: isNodeEmpty(node), unregistered: false }; // No store to clear\n }\n\n const segment = segments[index] as string;\n\n // Static segment\n if (this.matchers.staticSegment.test(segment)) {\n const staticChild = node.staticChildren[segment];\n if (staticChild) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(staticChild, segments, index + 1);\n if (shouldDelete) {\n delete node.staticChildren[segment]; // Remove the static child\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n const dynamicChildren = node.dynamicChildren;\n if (dynamicChildren) {\n for (let i = 0; i < this.plugins.length; i++) {\n const pluginConfig = this.plugins[i];\n for (let j = 0; j < dynamicChildren.length; j++) {\n const childNode = dynamicChildren[j] as Node<T>;\n const childMeta = childNode.pluginMeta;\n const pluginMeta = pluginConfig?.handler(segment);\n if (childMeta?.id === pluginConfig?.id && childMeta?.paramName === pluginMeta?.paramName) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(childNode, segments, index + 1);\n if (shouldDelete) {\n // Remove the child node from the dynamic children array\n dynamicChildren.splice(j, 1);\n // Check if the parent node can be deleted\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n }\n }\n }\n return { shouldDelete: false, unregistered: false };\n }\n\n /**\n * Validates a plugin and its configuration.\n * @param {Plugin} plugin The plugin function to validate.\n * @returns {PluginConfig} The validated plugin configuration.\n * @throws {Error} If the plugin is invalid or fails validation checks.\n * @protected\n */\n protected validatePlugin(plugin: Plugin): PluginConfig | never {\n // Check if the plugin is a function\n if (typeof plugin !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof plugin);\n }\n let pluginConfig: PluginConfig;\n // Call the plugin function to get the configuration\n try {\n pluginConfig = plugin();\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginConfig is an object\n if (!pluginConfig || typeof pluginConfig !== 'object') {\n this.throwError(ErrorTypes.PluginDoesNotReturnObject, typeof pluginConfig);\n }\n // Check if the pluginConfig has a id, priority\n if (!pluginConfig.id) {\n this.throwError(ErrorTypes.PluginMissingId);\n }\n // Check if the pluginConfig id is a string\n if (typeof pluginConfig.id !== 'string') {\n this.throwError(ErrorTypes.PluginIdIsNotString, typeof pluginConfig.id);\n }\n // Check if the pluginConfig id is already registered\n if (this.plugins.some((p) => p.id === pluginConfig.id)) {\n this.throwError(ErrorTypes.PluginWithSameIdAlreadyExists, pluginConfig.id);\n }\n // Check if the pluginConfig has a priority\n if (!pluginConfig.priority) {\n this.throwError(ErrorTypes.PluginMissingPriority);\n }\n // Check if the pluginConfig priority is a number\n if (typeof pluginConfig.priority !== 'number') {\n this.throwError(ErrorTypes.PluginPriorityIsNotNumber, typeof pluginConfig.priority);\n }\n // Check if the pluginConfig priority is already registered\n if (this.plugins.some((p) => p.priority === pluginConfig.priority)) {\n this.throwError(ErrorTypes.PluginWithSamePriorityAlreadyExists, String(pluginConfig.priority));\n }\n // Check if the pluginConfig has a syntax\n if (!pluginConfig.syntax) {\n this.throwError(ErrorTypes.PluginMissingSyntax);\n }\n // Check if the pluginConfig syntax is a string\n if (typeof pluginConfig.syntax !== 'string') {\n this.throwError(ErrorTypes.PluginSyntaxIsNotString, typeof pluginConfig.syntax);\n }\n // Check if the pluginConfig has no handler\n if (!pluginConfig.handler) {\n this.throwError(ErrorTypes.PluginMissingHandler, pluginConfig.id);\n }\n // Check if the pluginConfig has a handler\n if (typeof pluginConfig.handler !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof pluginConfig.handler);\n }\n\n let pluginMeta: PluginMeta | undefined | null;\n const syntax = pluginConfig.syntax;\n // Call the pluginConfig.handler to get the pluginMeta\n try {\n pluginMeta = pluginConfig.handler(syntax);\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginMeta is not null or undefined\n // This is important when it return null or undefined it means that the plugin unsuccessfully hanldled the given syntax\n if (pluginMeta === null || pluginMeta === undefined) {\n this.throwError(ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax, syntax);\n }\n // Check if the pluginMeta is an object\n if (!pluginMeta || typeof pluginMeta !== 'object') {\n this.throwError(ErrorTypes.PluginHandlerDoesNotReturnObject, typeof pluginMeta);\n }\n // Check if the pluginMeta has a match function\n if (!pluginMeta.match) {\n this.throwError(ErrorTypes.PluginHandlerMissingMatch);\n }\n // Check if the pluginMeta match is a function\n if (typeof pluginMeta.match !== 'function') {\n this.throwError(ErrorTypes.PluginHandlerMatchIsNotFunction, typeof pluginMeta.match);\n }\n\n let matchResult: boolean;\n // Call the pluginMeta.match to get the match result\n try {\n matchResult = pluginMeta.match({ urlSegment: '', urlSegments: [''], index: 0, params: {} });\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the matchResult is a boolean. we dont care now about the exact value.\n if (typeof matchResult !== 'boolean') {\n this.throwError(ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean, typeof matchResult);\n }\n return pluginConfig;\n }\n\n /**\n * Registers a plugin with the router.\n * Plugins extend the router's ability to handle custom parameter types or wildcards in dynamic routes.\n * Plugins are validated and added in order of their `priority` (lower number = higher precedence).\n * @param {Plugin} plugin The plugin function to register.\n * @returns {this} The router instance (for chaining).\n * @throws {Error} If the plugin is invalid or conflicts with an existing plugin.\n * @public\n */\n public use(plugin: Plugin): this | never {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n return this;\n }\n\n /**\n * Retrieves a list of all registered routes.\n * Useful for debugging or administrative purposes.\n * @returns {ListedRoute<T>[]} An array of objects, each describing a registered route.\n * @public\n */\n public inspect(): ListedRoute<T>[] {\n const listedRoute: ListedRoute<T>[] = [];\n // 1. Static Routes\n for (const path in this.staticPathCache) {\n if (Object.prototype.hasOwnProperty.call(this.staticPathCache, path)) {\n const staticStore = this.staticPathCache[path] as T;\n listedRoute.push({\n path: path,\n type: 'static',\n store: staticStore,\n });\n }\n }\n\n // 2. Dynamic Routes\n // Use a Set to ensure each unique registeredPath is added only once,\n const addedDynamicRegisteredPaths = new Set<string>();\n\n const traverse = (node: Node<T>) => {\n // If a node has a store and a registeredPath, it's a terminal node for a dynamic route.\n if (node.store && node.registeredPath) {\n if (!addedDynamicRegisteredPaths.has(node.registeredPath)) {\n listedRoute.push({\n path: node.registeredPath, // Use the stored registeredPath\n type: 'dynamic', // Routes from the tree with registeredPath are dynamic\n store: node.store,\n });\n addedDynamicRegisteredPaths.add(node.registeredPath);\n }\n }\n\n // Traverse static children\n for (const segment in node.staticChildren) {\n if (Object.prototype.hasOwnProperty.call(node.staticChildren, segment)) {\n traverse(node.staticChildren[segment]!);\n }\n }\n\n // Traverse dynamic children\n if (node.dynamicChildren) {\n for (const childNode of node.dynamicChildren) {\n traverse(childNode);\n }\n }\n };\n\n traverse(this.root); // Start traversal from the root for dynamic paths\n\n return listedRoute;\n }\n\n /**\n * Registers a route path with the router.\n * Handles static paths, dynamic paths with parameters/wildcards (requires plugins),\n * and paths with optional parameters.\n * @param {string} path The route path to register.\n * @returns {T} The store object associated with this route. You can add route-specific data to this object.\n * @throws {Error} If the path is invalid, conflicts with an existing route, or requires an unregistered plugin.\n * @public\n */\n public register(path: string): T | never {\n if (!path) {\n this.throwError(ErrorTypes.PathIsEmpty);\n }\n // Static path registration\n if (this.matchers.staticPath.test(path)) {\n return this.registerStaticPath(path);\n }\n // Param optional in path registration\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n // Define a shared store for all generated paths\n const sharedStore: T = this.options.storeFactory();\n // Register each generated path with the shared store\n for (const generatedPath of generatedPaths) {\n // For static paths, register them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n this.registerStaticPath(generatedPath, sharedStore);\n }\n // For dynamic paths, register them in root tree\n else {\n this.registerDynamicPath(generatedPath, sharedStore);\n }\n }\n return sharedStore;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.registerDynamicPath(path);\n }\n\n public unregister(path: string): boolean {\n // Check if the path is a static path\n if (this.matchers.staticPath.test(path)) {\n return this.unregisterStaticPath(path);\n }\n // Check if the path is has optional params\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n let success = false;\n for (const generatedPath of generatedPaths) {\n // For static paths, unregister them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n success = this.unregisterStaticPath(generatedPath);\n }\n // For dynamic paths, unregister them in root tree\n else {\n success = this.unregisterDynamicPath(generatedPath);\n }\n }\n return success;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.unregisterDynamicPath(path);\n }\n\n /**\n * Matches a given path against the registered routes.\n * Checks the static cache first, then traverses the radix tree for dynamic matches.\n * @param {string} path The path to match (e.g., '/users/123').\n * @returns {Match<T> | T | null}\n * - The exact store object `T` if a static path matches.\n * - A `Match<T>` object (the store object `T` augmented with a `params` property) if a dynamic path matches.\n * - `null` if no matching route is found.\n * @public\n */\n public match(path: string): Match<T> | null {\n // 1. Check static path cache (Fast O(1))\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n return cachedStore;\n }\n\n // 2. Prepare for dynamic path traversal\n const segments = path.split('/').filter(Boolean);\n const segmentCount = segments.length; // Cache length\n let currentNode: Node<T> = this.root;\n const params: Record<string, string> = Object.create(null);\n let i = 0;\n\n // 3. Traverse segments\n for (; i < segmentCount; i++) {\n const segment = segments[i] as string;\n\n // 3a. Check static children first (Fast O(1) average)\n const staticChild = currentNode.staticChildren[segment];\n if (staticChild) {\n currentNode = staticChild;\n continue; // Move to the next segment\n }\n // 3b. Check dynamic children (Potentially O(N) where N is num dynamic children)\n const dynamicChildren = currentNode.dynamicChildren;\n if (dynamicChildren) {\n let dynamicMatchFound = false;\n // Iterate through dynamic children (sorted by priority during registration)\n for (let j = 0; j < dynamicChildren.length; j++) {\n const dynamicChildNode = dynamicChildren[j] as Node<T>;\n const pluginMeta = dynamicChildNode?.pluginMeta;\n if (pluginMeta?.match({ urlSegment: segment, urlSegments: segments, index: i, params })) {\n // If it's a wildcard match, return immediately.\n if (pluginMeta.wildcard && dynamicChildNode?.store !== undefined) {\n const matchResult = Object.create(dynamicChildNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n currentNode = dynamicChildNode; // Move to the matched dynamic node\n dynamicMatchFound = true;\n break; // Stop checking other dynamic children for this segment (priority respected)\n }\n }\n\n // If no dynamic child matched this segment after checking all possibilities, return null\n if (!dynamicMatchFound) {\n return null;\n }\n }\n // 3c. No static child and no dynamic children array at this node, return null\n else {\n return null;\n }\n }\n\n // 4. End of segments reached. Check if the final node has a store.\n // Ensure we consumed all segments (i === segmentCount)\n if (i === segmentCount && currentNode.store) {\n // Create result object inheriting from store\n const matchResult = Object.create(currentNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n // 5. End of segments reached, but no store at the final node, or not all segments consumed\n return null;\n }\n}\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle basic parameters.\n * Syntax: `:paramName`\n */\nexport const param: Plugin = () => {\n const id = 'param';\n const priority = 700;\n const syntax = ':paramName';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle wildcard parameters.\n * Syntax: `*` or `:wildcardName*`\n */\nexport const wildcard: Plugin = () => {\n const id = 'wildcard';\n const priority = 800;\n const syntax = '*';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?:\\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\\*)$/.exec(segment);\n if (!match) {\n return null;\n }\n const paramName = match.groups?.wildcardName ?? '*';\n return {\n paramName,\n wildcard: true,\n match({ urlSegments, index, params }) {\n let rest = urlSegments[index];\n const segmentsLength = urlSegments.length;\n for (let j = index + 1; j < segmentsLength; j++) {\n rest += '/' + urlSegments[j];\n }\n params[paramName] = rest || '';\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with regex validation.\n * Syntax: `:paramName<\\d+>`\n */\nexport const regexParam: Plugin = () => {\n const id = 'regexParam';\n const priority = 400;\n const syntax = ':paramName<\\\\d+>';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.regex) {\n return null;\n }\n const paramName = match.groups.paramName;\n const regex = new RegExp(`^${match.groups.regex}$`);\n return {\n paramName,\n additionalMeta: {\n regex,\n },\n match({ urlSegment, params }) {\n const match = urlSegment.match(regex);\n if (!match) return false;\n params[paramName] = match[0];\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with a specific file extension.\n * Syntax: `:file.css`\n */\nexport const extensionParam: Plugin = () => {\n const id = 'extensionParam';\n const priority = 500;\n const syntax = ':file.css';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\.(?<extension>.+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.extension) {\n return null;\n }\n const paramName = match.groups.paramName;\n const extension = match.groups.extension;\n return {\n paramName,\n additionalMeta: {\n extension,\n },\n match({ urlSegment, params }) {\n if (!urlSegment.endsWith(extension)) return false;\n params[paramName] = urlSegment.slice(0, -(extension.length + 1));\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle grouped parameters.\n * Syntax: `:paramName(a|b)`\n */\nexport const groupParam: Plugin = () => {\n const id = 'groupParam';\n const priority = 300;\n const syntax = ':paramName(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.dynamicGroup) {\n return null;\n }\n const paramName = match.groups.paramName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [g, g]));\n return {\n paramName,\n additionalMeta: { group },\n match({ urlSegment, params }) {\n if (!group || !group[urlSegment]) return false;\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle prefix groups.\n * Syntax: `prefix(a|b)`\n */\nexport const prefixGroup: Plugin = () => {\n const id = 'prefixGroup';\n const priority = 100;\n const syntax = 'prefix(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional prefix groups.\n * Syntax: `prefix(a|b)?`\n */\nexport const optionalPrefixGroup: Plugin = () => {\n const id = 'optionalPrefixGroup';\n const priority = 200;\n const syntax = 'prefix(a|b)?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n group[staticName] = staticName;\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional parameters.\n * Syntax: `:paramName?`\n */\nexport const optionalParam: Plugin = () => {\n const id = 'optionalParam';\n const priority = 600;\n const syntax = ':paramName?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n override: true,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n"],"mappings":"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","names":["index_exports","__export","index_default","extensionParam","groupParam","optionalParam","optionalPrefixGroup","param","prefixGroup","regexParam","wildcard","__toCommonJS","ErrorTypes","Extreme","options","plugins","plugin","pluginConfig","a","b","finalOptions","storeObject","error","inline","type","ErrorTypes","errorMessage","path","segments","optionalIndexes","i","combinations","total","mask","includedSegments","_segment","idx","bit","combinationPath","store","newStore","currentNode","segmentsLength","segment","pluginMatch","pluginMeta","existingDynamicChildren","existingNodeForParam","child","newNode","rootNode","unregistered","node","index","isNodeEmpty","canDeleteNode","staticChild","shouldDelete","dynamicChildren","j","childNode","childMeta","p","syntax","matchResult","listedRoute","staticStore","addedDynamicRegisteredPaths","traverse","generatedPaths","sharedStore","generatedPath","success","cachedStore","segmentCount","params","dynamicMatchFound","dynamicChildNode","param","segment","match","paramName","urlSegment","params","wildcard","segment","match","paramName","urlSegments","index","params","rest","segmentsLength","j","regexParam","segment","match","paramName","regex","urlSegment","params","extensionParam","segment","match","paramName","extension","urlSegment","params","groupParam","segment","match","paramName","dynamicGroup","group","g","urlSegment","params","prefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalPrefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalParam","segment","match","paramName","urlSegment","params","index_default","Extreme"]}
package/dist/index.d.cts CHANGED
@@ -37,6 +37,7 @@ interface Node<T extends Store = Store> {
37
37
  interface Options<T extends Store = Store> {
38
38
  storeFactory: () => T;
39
39
  plugins: Plugin[];
40
+ allowRegisterUpdateExisting?: boolean;
40
41
  }
41
42
  type Match<T extends Store = Store> = T & {
42
43
  params: Record<string, string>;
@@ -178,7 +179,7 @@ declare class Extreme<T extends Store = Store> {
178
179
  * @param {string} path The static path to register.
179
180
  * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.
180
181
  * @returns {T} The store object associated with the path.
181
- * @throws {Error} If the path is already registered.
182
+ * @throws {Error} If the path is already registered and `allowRegisterUpdateExisting` is false.
182
183
  * @private
183
184
  */
184
185
  private registerStaticPath;
@@ -187,7 +188,7 @@ declare class Extreme<T extends Store = Store> {
187
188
  * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').
188
189
  * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.
189
190
  * @returns {T} The store object associated with the path.
190
- * @throws {Error} If the path conflicts with an existing registration or uses invalid syntax.
191
+ * @throws {Error} If the path conflicts with an existing registration (with allowRegisterUpdateExisting = false) or uses invalid syntax.
191
192
  * @private
192
193
  */
193
194
  private registerDynamicPath;
package/dist/index.d.ts CHANGED
@@ -37,6 +37,7 @@ interface Node<T extends Store = Store> {
37
37
  interface Options<T extends Store = Store> {
38
38
  storeFactory: () => T;
39
39
  plugins: Plugin[];
40
+ allowRegisterUpdateExisting?: boolean;
40
41
  }
41
42
  type Match<T extends Store = Store> = T & {
42
43
  params: Record<string, string>;
@@ -178,7 +179,7 @@ declare class Extreme<T extends Store = Store> {
178
179
  * @param {string} path The static path to register.
179
180
  * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.
180
181
  * @returns {T} The store object associated with the path.
181
- * @throws {Error} If the path is already registered.
182
+ * @throws {Error} If the path is already registered and `allowRegisterUpdateExisting` is false.
182
183
  * @private
183
184
  */
184
185
  private registerStaticPath;
@@ -187,7 +188,7 @@ declare class Extreme<T extends Store = Store> {
187
188
  * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').
188
189
  * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.
189
190
  * @returns {T} The store object associated with the path.
190
- * @throws {Error} If the path conflicts with an existing registration or uses invalid syntax.
191
+ * @throws {Error} If the path conflicts with an existing registration (with allowRegisterUpdateExisting = false) or uses invalid syntax.
191
192
  * @private
192
193
  */
193
194
  private registerDynamicPath;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var x=(s=>(s[s.StoreIsNotFunction=0]="StoreIsNotFunction",s[s.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",s[s.StoreUnexpected=2]="StoreUnexpected",s[s.PathAlreadyRegistered=3]="PathAlreadyRegistered",s[s.PathIsEmpty=4]="PathIsEmpty",s[s.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",s[s.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",s[s.PluginIsNotFunction=7]="PluginIsNotFunction",s[s.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",s[s.PluginsOptionNotArray=9]="PluginsOptionNotArray",s[s.PluginMissingId=10]="PluginMissingId",s[s.PluginMissingPriority=11]="PluginMissingPriority",s[s.PluginIdIsNotString=12]="PluginIdIsNotString",s[s.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",s[s.PluginMissingSyntax=14]="PluginMissingSyntax",s[s.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",s[s.PluginMissingHandler=16]="PluginMissingHandler",s[s.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",s[s.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",s[s.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",s[s.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",s[s.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",s[s.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",s[s.PluginUnexpected=23]="PluginUnexpected",s[s.PluginDoesNotExist=24]="PluginDoesNotExist",s[s.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",s[s.WildcardNotAtEnd=26]="WildcardNotAtEnd",s))(x||{});var f=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[]};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(e=>{let i=this.validatePlugin(e);this.plugins.push(i)}),this.plugins.sort((e,i)=>e.priority-i.priority)}validateOptions(t){let e={...this.defaultOptions,...t};if(!e.storeFactory)e.storeFactory=this.defaultOptions.storeFactory;else if(typeof e.storeFactory!="function")this.throwError(0,typeof e.storeFactory);else{let i;try{i=e.storeFactory()}catch(r){this.throwError(2,r instanceof Error?r.message:String(r))}(typeof i!="object"||Array.isArray(i))&&this.throwError(1,typeof i)}return Array.isArray(e.plugins)||this.throwError(9,typeof e.plugins),e}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,e){if(x[t]!==void 0){let r=this.errorTypes[t](e);throw new Error(r)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let e=t.split("/").filter(Boolean),i=e.map((o,a)=>this.matchers.paramOptionalSegment.test(o)?a:-1).filter(o=>o>=0);if(i.length===0)return["/"+e.join("/")];let r=new Set,n=1<<i.length;for(let o=0;o<n;o++){let a=e.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(e[c]))return!0;let g=i.indexOf(c);return!!(o&1<<g)}),l=a.length===0?"/":"/"+a.join("/");r.add(l)}return Array.from(r)}registerStaticPath(t,e){this.staticPathCache[t]&&this.throwError(3,t);let i=Object.create(e??this.options.storeFactory());return this.staticPathCache[t]=i,i}registerDynamicPath(t,e){let i=t.split("/").filter(Boolean),r=this.root,n=e??this.options.storeFactory(),o=i.length;for(let a=0;a<o;a++){let l=i[a];if(this.matchers.staticSegment.test(l))r=r.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==o-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,r.dynamicChildren??=[];let d=r.dynamicChildren,h=d.find(P=>P.pluginMeta?.id===g.id&&P.pluginMeta?.paramName===g.paramName);if(h)r=h;else{d.find(y=>y.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let p=this.createNode();p.pluginMeta=g,d.push(p),d.sort((y,N)=>y.pluginMeta.priority-N.pluginMeta.priority),r=p}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return r.store&&r.pluginMeta?.override!==!0&&this.throwError(3,t),r.store=n,r.registeredPath=t,n}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let e=this.root,i=t.split("/").filter(Boolean),{unregistered:r}=this.cleanupTraversal(e,i,0);return r}cleanupTraversal(t,e,i){let r=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,n=a=>r(a)&&!a.store;if(i===e.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:r(t),unregistered:!0}):{shouldDelete:r(t),unregistered:!1};let o=e[i];if(this.matchers.staticSegment.test(o)){let a=t.staticChildren[o];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,e,i+1);if(l)return delete t.staticChildren[o],{shouldDelete:n(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],d=g.pluginMeta,h=u?.handler(o);if(d?.id===u?.id&&d?.paramName===h?.paramName){let{shouldDelete:P,unregistered:p}=this.cleanupTraversal(g,e,i+1);if(P)return a.splice(c,1),{shouldDelete:n(t),unregistered:p};if(p)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let e;try{e=t()}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}(!e||typeof e!="object")&&this.throwError(8,typeof e),e.id||this.throwError(10),typeof e.id!="string"&&this.throwError(12,typeof e.id),this.plugins.some(o=>o.id===e.id)&&this.throwError(5,e.id),e.priority||this.throwError(11),typeof e.priority!="number"&&this.throwError(13,typeof e.priority),this.plugins.some(o=>o.priority===e.priority)&&this.throwError(6,String(e.priority)),e.syntax||this.throwError(14),typeof e.syntax!="string"&&this.throwError(15,typeof e.syntax),e.handler||this.throwError(16,e.id),typeof e.handler!="function"&&this.throwError(7,typeof e.handler);let i,r=e.syntax;try{i=e.handler(r)}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}i==null&&this.throwError(18,r),(!i||typeof i!="object")&&this.throwError(19,typeof i),i.match||this.throwError(20),typeof i.match!="function"&&this.throwError(21,typeof i.match);let n;try{n=i.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(o){this.throwError(23,o instanceof Error?o.message:String(o))}return typeof n!="boolean"&&this.throwError(22,typeof n),e}use(t){let e=this.validatePlugin(t);return this.plugins.push(e),this.plugins.sort((i,r)=>i.priority-r.priority),this}inspect(){let t=[];for(let r in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,r)){let n=this.staticPathCache[r];t.push({path:r,type:"static",store:n})}let e=new Set,i=r=>{r.store&&r.registeredPath&&(e.has(r.registeredPath)||(t.push({path:r.registeredPath,type:"dynamic",store:r.store}),e.add(r.registeredPath)));for(let n in r.staticChildren)Object.prototype.hasOwnProperty.call(r.staticChildren,n)&&i(r.staticChildren[n]);if(r.dynamicChildren)for(let n of r.dynamicChildren)i(n)};return i(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),i=this.options.storeFactory();for(let r of e)this.matchers.staticPath.test(r)?this.registerStaticPath(r,i):this.registerDynamicPath(r,i);return i}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),i=!1;for(let r of e)this.matchers.staticPath.test(r)?i=this.unregisterStaticPath(r):i=this.unregisterDynamicPath(r);return i}return this.unregisterDynamicPath(t)}match(t){let e=this.staticPathCache[t];if(e)return e;let i=t.split("/").filter(Boolean),r=i.length,n=this.root,o=Object.create(null),a=0;for(;a<r;a++){let l=i[a],u=n.staticChildren[l];if(u){n=u;continue}let c=n.dynamicChildren;if(c){let g=!1;for(let d=0;d<c.length;d++){let h=c[d],P=h?.pluginMeta;if(P?.match({urlSegment:l,urlSegments:i,index:a,params:o})){if(P.wildcard&&h?.store!==void 0){let p=Object.create(h.store);return p.params=o,p}n=h,g=!0;break}}if(!g)return null}else return null}if(a===r&&n.store){let l=Object.create(n.store);return l.params=o,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(r);if(!n||!n.groups||!n.groups.paramName)return null;let o=n.groups.paramName;return{paramName:o,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:r=>{let n=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(r);if(!n)return null;let o=n.groups?.wildcardName??"*";return{paramName:o,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let d=l+1;d<g;d++)c+="/"+a[d];return u[o]=c||"",!0}}}});var M=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.regex)return null;let o=n.groups.paramName,a=new RegExp(`^${n.groups.regex}$`);return{paramName:o,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[o]=c[0],!0):!1}}}});var w=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.extension)return null;let o=n.groups.paramName,a=n.groups.extension;return{paramName:o,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[o]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!n||!n.groups||!n.groups.paramName||!n.groups.dynamicGroup)return null;let o=n.groups.paramName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:o,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[o]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:r=>{let n=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!n||!n.groups||!n.groups.staticName||!n.groups.dynamicGroup)return null;let o=n.groups.staticName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:r=>{let n=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(r);if(!n||!n.groups||!n.groups.staticName||!n.groups.dynamicGroup)return null;let o=n.groups.staticName,a=n.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[o+u,o+u]));return l[o]=o,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:r=>{let n=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(r);if(!n||!n.groups||!n.groups.paramName)return null;let o=n.groups.paramName;return{paramName:o,override:!0,match({urlSegment:a,params:l}){return l[o]=a,!0}}}});var K=f;export{K as default,w as extensionParam,O as groupParam,R as optionalParam,D as optionalPrefixGroup,S as param,C as prefixGroup,M as regexParam,b as wildcard};
1
+ var x=(o=>(o[o.StoreIsNotFunction=0]="StoreIsNotFunction",o[o.StoreDoesNotReturnObject=1]="StoreDoesNotReturnObject",o[o.StoreUnexpected=2]="StoreUnexpected",o[o.PathAlreadyRegistered=3]="PathAlreadyRegistered",o[o.PathIsEmpty=4]="PathIsEmpty",o[o.PluginWithSameIdAlreadyExists=5]="PluginWithSameIdAlreadyExists",o[o.PluginWithSamePriorityAlreadyExists=6]="PluginWithSamePriorityAlreadyExists",o[o.PluginIsNotFunction=7]="PluginIsNotFunction",o[o.PluginDoesNotReturnObject=8]="PluginDoesNotReturnObject",o[o.PluginsOptionNotArray=9]="PluginsOptionNotArray",o[o.PluginMissingId=10]="PluginMissingId",o[o.PluginMissingPriority=11]="PluginMissingPriority",o[o.PluginIdIsNotString=12]="PluginIdIsNotString",o[o.PluginPriorityIsNotNumber=13]="PluginPriorityIsNotNumber",o[o.PluginMissingSyntax=14]="PluginMissingSyntax",o[o.PluginSyntaxIsNotString=15]="PluginSyntaxIsNotString",o[o.PluginMissingHandler=16]="PluginMissingHandler",o[o.PluginHandlerIsNotFunction=17]="PluginHandlerIsNotFunction",o[o.PluginHandlerReturnNullOrUndefinedForSyntax=18]="PluginHandlerReturnNullOrUndefinedForSyntax",o[o.PluginHandlerDoesNotReturnObject=19]="PluginHandlerDoesNotReturnObject",o[o.PluginHandlerMissingMatch=20]="PluginHandlerMissingMatch",o[o.PluginHandlerMatchIsNotFunction=21]="PluginHandlerMatchIsNotFunction",o[o.PluginHandlerMatchDoesNotReturnBoolean=22]="PluginHandlerMatchDoesNotReturnBoolean",o[o.PluginUnexpected=23]="PluginUnexpected",o[o.PluginDoesNotExist=24]="PluginDoesNotExist",o[o.DynamicSegmentAlreadyExists=25]="DynamicSegmentAlreadyExists",o[o.WildcardNotAtEnd=26]="WildcardNotAtEnd",o))(x||{});var f=class{constructor(t={}){this.options=this.validateOptions(t),this.staticPathCache=Object.create(null),this.root=this.createNode(),this.errorTypes=this.createErrorTypes(),this.plugins=[],this.options.plugins.length>0&&this.loadPlugins(this.options.plugins)}defaultOptions={storeFactory:()=>Object.create(null),plugins:[],allowRegisterUpdateExisting:!1};options;staticPathCache;root;plugins;errorTypes;matchers={staticPath:/^(?:\/|\/?(?:[a-zA-Z0-9 _.-]+)(?:\/[a-zA-Z0-9 _.-]+)*)$/,paramOptionalInPath:/\/:[a-zA-Z0-9_-]+\?/,staticSegment:/^[a-zA-Z0-9 _.-]+$/,paramOptionalSegment:/^:[a-zA-Z0-9_-]+\?$/};loadPlugins(t){t.forEach(e=>{let n=this.validatePlugin(e);this.plugins.push(n)}),this.plugins.sort((e,n)=>e.priority-n.priority)}validateOptions(t){let e={...this.defaultOptions,...t};if(!e.storeFactory)e.storeFactory=this.defaultOptions.storeFactory;else if(typeof e.storeFactory!="function")this.throwError(0,typeof e.storeFactory);else{let n;try{n=e.storeFactory()}catch(r){this.throwError(2,r instanceof Error?r.message:String(r))}(typeof n!="object"||Array.isArray(n))&&this.throwError(1,typeof n)}return Array.isArray(e.plugins)||this.throwError(9,typeof e.plugins),e}createErrorTypes(){return{0:t=>`Store is not a function: ${t}`,1:t=>`Store does not return an object: ${t}`,2:t=>`Store unexpected error: ${t}`,3:t=>`Path already registered: ${t}`,5:t=>`Plugin with same ID already exists: ${t}`,6:t=>`Plugin with same priority already exists: ${t}`,7:t=>`Plugin is not a function: ${t}`,8:t=>`Plugin does not return an object: ${t}`,9:t=>`Plugins option must be an array, got: ${t}`,10:()=>"Plugin missing ID",11:()=>"Plugin missing priority",12:t=>`Plugin ID is not a string: ${t}`,13:t=>`Plugin priority is not a number: ${t}`,14:()=>"Plugin missing syntax",15:t=>`Plugin syntax is not a string: ${t}`,16:t=>`Plugin missing handler: ${t}`,17:t=>`Plugin handler is not a function: ${t}`,18:t=>`Plugin handler returned null or undefined while matching syntax: ${t}`,19:t=>`Plugin handler does not return an object: ${t}`,20:()=>"Plugin handler missing match function",21:t=>`Plugin handler match is not a function: ${t}`,22:t=>`Plugin handler match does not return a boolean: ${t}`,23:t=>`Plugin unexpected error: ${t}`,24:t=>`Plugin does not exist for: ${t}`,25:t=>`Dynamic segment already exists: ${t}`,26:()=>"Wildcard must be at the end of the path",4:()=>"Path cannot be empty"}}throwError(t,e){if(x[t]!==void 0){let r=this.errorTypes[t](e);throw new Error(r)}throw new Error("Unknown error type")}createNode(){return{staticChildren:Object.create(null)}}generateOptionals(t){let e=t.split("/").filter(Boolean),n=e.map((s,a)=>this.matchers.paramOptionalSegment.test(s)?a:-1).filter(s=>s>=0);if(n.length===0)return["/"+e.join("/")];let r=new Set,i=1<<n.length;for(let s=0;s<i;s++){let a=e.filter((u,c)=>{if(!this.matchers.paramOptionalSegment.test(e[c]))return!0;let g=n.indexOf(c);return!!(s&1<<g)}),l=a.length===0?"/":"/"+a.join("/");r.add(l)}return Array.from(r)}registerStaticPath(t,e){if(this.staticPathCache[t])return this.options.allowRegisterUpdateExisting||this.throwError(3,t),this.staticPathCache[t];let n=Object.create(e??this.options.storeFactory());return this.staticPathCache[t]=n,n}registerDynamicPath(t,e){let n=t.split("/").filter(Boolean),r=this.root,i=e??this.options.storeFactory(),s=n.length;for(let a=0;a<s;a++){let l=n[a];if(this.matchers.staticSegment.test(l))r=r.staticChildren[l]??=this.createNode();else if(this.plugins.length>0){let u=!1;for(let c of this.plugins){let g=c.handler(l);if(g!=null){g.wildcard&&a!==s-1&&this.throwError(26,l),u=!0,g.id=c.id,g.priority=c.priority,g.syntax=c.syntax,r.dynamicChildren??=[];let d=r.dynamicChildren,h=d.find(P=>P.pluginMeta?.id===g.id&&P.pluginMeta?.paramName===g.paramName);if(h)r=h;else{d.find(y=>y.pluginMeta?.id===g.id)&&g.override!==!0&&this.throwError(25,`${l} (Plugin ID conflict: ${g.id})`);let p=this.createNode();p.pluginMeta=g,d.push(p),d.sort((y,N)=>y.pluginMeta.priority-N.pluginMeta.priority),r=p}break}}u||this.throwError(24,l)}else this.throwError(24,l)}return r.store&&r.pluginMeta?.override!==!0&&!this.options.allowRegisterUpdateExisting&&this.throwError(3,t),r.store=r.store??i,r.registeredPath=t,r.store}unregisterStaticPath(t){return this.staticPathCache[t]?(delete this.staticPathCache[t],!0):!1}unregisterDynamicPath(t){let e=this.root,n=t.split("/").filter(Boolean),{unregistered:r}=this.cleanupTraversal(e,n,0);return r}cleanupTraversal(t,e,n){let r=a=>Object.keys(a.staticChildren).length===0&&!a.dynamicChildren?.length,i=a=>r(a)&&!a.store;if(n===e.length)return t.store?(t.store=void 0,t.registeredPath=void 0,{shouldDelete:r(t),unregistered:!0}):{shouldDelete:r(t),unregistered:!1};let s=e[n];if(this.matchers.staticSegment.test(s)){let a=t.staticChildren[s];if(a){let{shouldDelete:l,unregistered:u}=this.cleanupTraversal(a,e,n+1);if(l)return delete t.staticChildren[s],{shouldDelete:i(t),unregistered:u};if(u)return{shouldDelete:!1,unregistered:!0}}}else if(this.plugins.length>0){let a=t.dynamicChildren;if(a)for(let l=0;l<this.plugins.length;l++){let u=this.plugins[l];for(let c=0;c<a.length;c++){let g=a[c],d=g.pluginMeta,h=u?.handler(s);if(d?.id===u?.id&&d?.paramName===h?.paramName){let{shouldDelete:P,unregistered:p}=this.cleanupTraversal(g,e,n+1);if(P)return a.splice(c,1),{shouldDelete:i(t),unregistered:p};if(p)return{shouldDelete:!1,unregistered:!0}}}}}return{shouldDelete:!1,unregistered:!1}}validatePlugin(t){typeof t!="function"&&this.throwError(7,typeof t);let e;try{e=t()}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}(!e||typeof e!="object")&&this.throwError(8,typeof e),e.id||this.throwError(10),typeof e.id!="string"&&this.throwError(12,typeof e.id),this.plugins.some(s=>s.id===e.id)&&this.throwError(5,e.id),e.priority||this.throwError(11),typeof e.priority!="number"&&this.throwError(13,typeof e.priority),this.plugins.some(s=>s.priority===e.priority)&&this.throwError(6,String(e.priority)),e.syntax||this.throwError(14),typeof e.syntax!="string"&&this.throwError(15,typeof e.syntax),e.handler||this.throwError(16,e.id),typeof e.handler!="function"&&this.throwError(7,typeof e.handler);let n,r=e.syntax;try{n=e.handler(r)}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}n==null&&this.throwError(18,r),(!n||typeof n!="object")&&this.throwError(19,typeof n),n.match||this.throwError(20),typeof n.match!="function"&&this.throwError(21,typeof n.match);let i;try{i=n.match({urlSegment:"",urlSegments:[""],index:0,params:{}})}catch(s){this.throwError(23,s instanceof Error?s.message:String(s))}return typeof i!="boolean"&&this.throwError(22,typeof i),e}use(t){let e=this.validatePlugin(t);return this.plugins.push(e),this.plugins.sort((n,r)=>n.priority-r.priority),this}inspect(){let t=[];for(let r in this.staticPathCache)if(Object.prototype.hasOwnProperty.call(this.staticPathCache,r)){let i=this.staticPathCache[r];t.push({path:r,type:"static",store:i})}let e=new Set,n=r=>{r.store&&r.registeredPath&&(e.has(r.registeredPath)||(t.push({path:r.registeredPath,type:"dynamic",store:r.store}),e.add(r.registeredPath)));for(let i in r.staticChildren)Object.prototype.hasOwnProperty.call(r.staticChildren,i)&&n(r.staticChildren[i]);if(r.dynamicChildren)for(let i of r.dynamicChildren)n(i)};return n(this.root),t}register(t){if(t||this.throwError(4),this.matchers.staticPath.test(t))return this.registerStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=this.options.storeFactory();for(let r of e)this.matchers.staticPath.test(r)?this.registerStaticPath(r,n):this.registerDynamicPath(r,n);return n}return this.registerDynamicPath(t)}unregister(t){if(this.matchers.staticPath.test(t))return this.unregisterStaticPath(t);if(this.matchers.paramOptionalInPath.test(t)){let e=this.generateOptionals(t),n=!1;for(let r of e)this.matchers.staticPath.test(r)?n=this.unregisterStaticPath(r):n=this.unregisterDynamicPath(r);return n}return this.unregisterDynamicPath(t)}match(t){let e=this.staticPathCache[t];if(e)return e;let n=t.split("/").filter(Boolean),r=n.length,i=this.root,s=Object.create(null),a=0;for(;a<r;a++){let l=n[a],u=i.staticChildren[l];if(u){i=u;continue}let c=i.dynamicChildren;if(c){let g=!1;for(let d=0;d<c.length;d++){let h=c[d],P=h?.pluginMeta;if(P?.match({urlSegment:l,urlSegments:n,index:a,params:s})){if(P.wildcard&&h?.store!==void 0){let p=Object.create(h.store);return p.params=s,p}i=h,g=!0;break}}if(!g)return null}else return null}if(a===r&&i.store){let l=Object.create(i.store);return l.params=s,l}return null}};var S=()=>({id:"param",priority:700,syntax:":paramName",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(r);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var b=()=>({id:"wildcard",priority:800,syntax:"*",handler:r=>{let i=/^(?:\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\*)$/.exec(r);if(!i)return null;let s=i.groups?.wildcardName??"*";return{paramName:s,wildcard:!0,match({urlSegments:a,index:l,params:u}){let c=a[l],g=a.length;for(let d=l+1;d<g;d++)c+="/"+a[d];return u[s]=c||"",!0}}}});var w=()=>({id:"regexParam",priority:400,syntax:":paramName<\\d+>",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.regex)return null;let s=i.groups.paramName,a=new RegExp(`^${i.groups.regex}$`);return{paramName:s,additionalMeta:{regex:a},match({urlSegment:l,params:u}){let c=l.match(a);return c?(u[s]=c[0],!0):!1}}}});var M=()=>({id:"extensionParam",priority:500,syntax:":file.css",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\.(?<extension>.+)$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.extension)return null;let s=i.groups.paramName,a=i.groups.extension;return{paramName:s,additionalMeta:{extension:a},match({urlSegment:l,params:u}){return l.endsWith(a)?(u[s]=l.slice(0,-(a.length+1)),!0):!1}}}});var O=()=>({id:"groupParam",priority:300,syntax:":paramName(a|b)",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!i||!i.groups||!i.groups.paramName||!i.groups.dynamicGroup)return null;let s=i.groups.paramName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[u,u]));return{paramName:s,additionalMeta:{group:l},match({urlSegment:u,params:c}){return!l||!l[u]?!1:(c[s]=u,!0)}}}});var C=()=>({id:"prefixGroup",priority:100,syntax:"prefix(a|b)",handler:r=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)$/.exec(r);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var R=()=>({id:"optionalPrefixGroup",priority:200,syntax:"prefix(a|b)?",handler:r=>{let i=/^(?<staticName>[a-zA-Z0-9_.-]+)\((?<dynamicGroup>[^|)]+(\|[^|)]+)+)\)\?$/.exec(r);if(!i||!i.groups||!i.groups.staticName||!i.groups.dynamicGroup)return null;let s=i.groups.staticName,a=i.groups.dynamicGroup,l=Object.fromEntries(a.split("|").map(u=>[s+u,s+u]));return l[s]=s,{paramName:"",additionalMeta:{group:l},match({urlSegment:u}){return!!(l&&l[u])}}}});var D=()=>({id:"optionalParam",priority:600,syntax:":paramName?",handler:r=>{let i=/^:(?<paramName>[a-zA-Z0-9_-]+)\?$/.exec(r);if(!i||!i.groups||!i.groups.paramName)return null;let s=i.groups.paramName;return{paramName:s,override:!0,match({urlSegment:a,params:l}){return l[s]=a,!0}}}});var K=f;export{K as default,M as extensionParam,O as groupParam,D as optionalParam,R as optionalPrefixGroup,S as param,C as prefixGroup,w as regexParam,b as wildcard};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/router.ts","../src/plugins/param.ts","../src/plugins/wildcard.ts","../src/plugins/regexParam.ts","../src/plugins/extensionParam.ts","../src/plugins/groupParam.ts","../src/plugins/prefixGroup.ts","../src/plugins/optionalPrefixGroup.ts","../src/plugins/optionalParam.ts","../index.ts"],"sourcesContent":["export type Store = object;\n\nexport type PluginHandler = (segment: string) => PluginMeta | undefined | null;\n\nexport interface PluginConfig {\n id: string;\n priority: number;\n syntax: string;\n handler: PluginHandler;\n}\n\nexport type Plugin = () => PluginConfig;\n\nexport interface PluginMeta {\n // Priority, id, syntax are automatically added by the plugin manager according to the plugin config\n // So no need to add them in the plugin meta\n // They are used here for matching priority logic (Important!), id and syntax for debugging\n paramName: string;\n priority?: number;\n id?: string;\n syntax?: string;\n override?: boolean;\n wildcard?: boolean;\n additionalMeta?: {\n group?: Record<string | number, unknown>;\n regex?: RegExp;\n extension?: string;\n [k: string]: unknown;\n };\n match: ({\n urlSegment,\n urlSegments,\n index,\n params,\n }: {\n urlSegment: string;\n urlSegments: string[];\n index: number;\n params: Record<string, unknown>;\n }) => boolean;\n}\n\nexport interface Node<T extends Store = Store> {\n registeredPath?: string;\n staticChildren: Record<string, Node<T>>;\n dynamicChildren?: Node<T>[];\n pluginMeta?: PluginMeta;\n store?: T;\n}\n\nexport interface Options<T extends Store = Store> {\n storeFactory: () => T;\n plugins: Plugin[];\n}\n\nexport type Match<T extends Store = Store> = T & {\n params: Record<string, string>;\n};\n\nexport enum ErrorTypes {\n StoreIsNotFunction,\n StoreDoesNotReturnObject,\n StoreUnexpected,\n PathAlreadyRegistered,\n PathIsEmpty,\n PluginWithSameIdAlreadyExists,\n PluginWithSamePriorityAlreadyExists,\n PluginIsNotFunction,\n PluginDoesNotReturnObject,\n PluginsOptionNotArray,\n PluginMissingId,\n PluginMissingPriority,\n PluginIdIsNotString,\n PluginPriorityIsNotNumber,\n PluginMissingSyntax,\n PluginSyntaxIsNotString,\n PluginMissingHandler,\n PluginHandlerIsNotFunction,\n PluginHandlerReturnNullOrUndefinedForSyntax,\n PluginHandlerDoesNotReturnObject,\n PluginHandlerMissingMatch,\n PluginHandlerMatchIsNotFunction,\n PluginHandlerMatchDoesNotReturnBoolean,\n PluginUnexpected,\n PluginDoesNotExist,\n DynamicSegmentAlreadyExists,\n WildcardNotAtEnd,\n}\n\nexport interface ListedRoute<T extends Store> {\n path: string;\n type: 'static' | 'dynamic';\n store: T;\n}\n\nexport interface CleanupTraversalResult {\n shouldDelete: boolean;\n unregistered: boolean;\n}\n","import {\n ErrorTypes,\n type Match,\n type Node,\n type Options,\n type Plugin,\n type PluginConfig,\n type PluginMeta,\n type Store,\n type ListedRoute,\n type CleanupTraversalResult,\n} from './types';\n\n/**\n * High-performance, modular, tree-based router with plugin support.\n *\n * @template T The type of the store object associated with each route. Defaults to `Store`.\n */\nexport default class Extreme<T extends Store = Store> {\n /**\n * Creates a new Extreme router instance.\n * @param {Partial<Options<T>>} [options={}] Configuration options for the router.\n * @param {() => T} [options.storeFactory=() => Object.create(null)] A function that returns a new store object for each registered route.\n */\n constructor(options: Partial<Options<T>> = {}) {\n this.options = this.validateOptions(options);\n this.staticPathCache = Object.create(null);\n this.root = this.createNode();\n this.errorTypes = this.createErrorTypes();\n this.plugins = [];\n if (this.options.plugins.length > 0) {\n this.loadPlugins(this.options.plugins);\n }\n }\n\n /**\n * Default configuration options for the router.\n * @protected\n */\n protected defaultOptions: Options<T> = {\n storeFactory: () => Object.create(null),\n plugins: [],\n };\n\n /**\n * The validated configuration options for this router instance.\n * @protected\n */\n protected options: Options<T>;\n\n /**\n * Cache for quickly matching static paths (O(1) lookup).\n * Keys are static paths, values are the associated route stores.\n * @protected\n */\n protected staticPathCache: Record<string, Match<T>>;\n\n /**\n * The root node of the radix tree used for dynamic path matching.\n * @protected\n */\n protected root: Node<T>;\n\n /**\n * Array storing the registered plugin configurations, sorted by priority.\n * @protected\n */\n protected plugins: PluginConfig[];\n\n /**\n * Map of error types to their corresponding error message generators.\n * @protected\n */\n protected errorTypes: Record<ErrorTypes, (inline?: string) => string>;\n\n /**\n * Regular expressions used for matching path segments and types.\n * @protected\n */\n protected matchers = {\n staticPath: /^(?:\\/|\\/?(?:[a-zA-Z0-9 _.-]+)(?:\\/[a-zA-Z0-9 _.-]+)*)$/,\n paramOptionalInPath: /\\/:[a-zA-Z0-9_-]+\\?/,\n staticSegment: /^[a-zA-Z0-9 _.-]+$/,\n paramOptionalSegment: /^:[a-zA-Z0-9_-]+\\?$/,\n };\n\n /**\n * Loads and registers an array of plugins, validating each plugin before adding it to the internal plugins list.\n * After all plugins are added, the list is sorted by priority in ascending order (lower number indicates higher precedence).\n *\n * @param plugins - An array of plugins to be loaded and registered.\n */\n private loadPlugins(plugins: Plugin[]): void | never {\n plugins.forEach((plugin) => {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n });\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Validates the provided router options and merges them with defaults.\n * @param {Partial<Options<T>>} options The options provided to the constructor.\n * @returns {Options<T>} The validated and merged options.\n * @throws {Error} If the storeFactory is invalid.\n * @private\n */\n private validateOptions(options: Partial<Options<T>>): Options<T> {\n const finalOptions = { ...this.defaultOptions, ...options };\n // Store validation\n if (!finalOptions.storeFactory) {\n finalOptions.storeFactory = this.defaultOptions.storeFactory;\n } else if (typeof finalOptions.storeFactory !== 'function') {\n this.throwError(ErrorTypes.StoreIsNotFunction, typeof finalOptions.storeFactory);\n } else {\n let storeObject: T;\n try {\n storeObject = finalOptions.storeFactory();\n } catch (error) {\n this.throwError(ErrorTypes.StoreUnexpected, error instanceof Error ? error.message : String(error));\n }\n if (typeof storeObject !== 'object' || Array.isArray(storeObject)) {\n this.throwError(ErrorTypes.StoreDoesNotReturnObject, typeof storeObject);\n }\n }\n if (!Array.isArray(finalOptions.plugins)) {\n this.throwError(ErrorTypes.PluginsOptionNotArray, typeof finalOptions.plugins);\n }\n return finalOptions;\n }\n\n /**\n * Creates the map of error type enums to error message generator functions.\n * @returns {Record<ErrorTypes, (inline?: string) => string>} The error types map.\n * @private\n */\n private createErrorTypes(): typeof this.errorTypes {\n return {\n [ErrorTypes.StoreIsNotFunction]: (inline?: string) => `Store is not a function: ${inline}`,\n [ErrorTypes.StoreDoesNotReturnObject]: (inline?: string) => `Store does not return an object: ${inline}`,\n [ErrorTypes.StoreUnexpected]: (inline?: string) => `Store unexpected error: ${inline}`,\n [ErrorTypes.PathAlreadyRegistered]: (inline?: string) => `Path already registered: ${inline}`,\n [ErrorTypes.PluginWithSameIdAlreadyExists]: (inline?: string) => `Plugin with same ID already exists: ${inline}`,\n [ErrorTypes.PluginWithSamePriorityAlreadyExists]: (inline?: string) =>\n `Plugin with same priority already exists: ${inline}`,\n [ErrorTypes.PluginIsNotFunction]: (inline?: string) => `Plugin is not a function: ${inline}`,\n [ErrorTypes.PluginDoesNotReturnObject]: (inline?: string) => `Plugin does not return an object: ${inline}`,\n [ErrorTypes.PluginsOptionNotArray]: (inline?: string) => `Plugins option must be an array, got: ${inline}`,\n [ErrorTypes.PluginMissingId]: () => 'Plugin missing ID',\n [ErrorTypes.PluginMissingPriority]: () => 'Plugin missing priority',\n [ErrorTypes.PluginIdIsNotString]: (inline?: string) => `Plugin ID is not a string: ${inline}`,\n [ErrorTypes.PluginPriorityIsNotNumber]: (inline?: string) => `Plugin priority is not a number: ${inline}`,\n [ErrorTypes.PluginMissingSyntax]: () => 'Plugin missing syntax',\n [ErrorTypes.PluginSyntaxIsNotString]: (inline?: string) => `Plugin syntax is not a string: ${inline}`,\n [ErrorTypes.PluginMissingHandler]: (inline?: string) => `Plugin missing handler: ${inline}`,\n [ErrorTypes.PluginHandlerIsNotFunction]: (inline?: string) => `Plugin handler is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax]: (inline?: string) =>\n `Plugin handler returned null or undefined while matching syntax: ${inline}`,\n [ErrorTypes.PluginHandlerDoesNotReturnObject]: (inline?: string) =>\n `Plugin handler does not return an object: ${inline}`,\n [ErrorTypes.PluginHandlerMissingMatch]: () => 'Plugin handler missing match function',\n [ErrorTypes.PluginHandlerMatchIsNotFunction]: (inline?: string) =>\n `Plugin handler match is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean]: (inline?: string) =>\n `Plugin handler match does not return a boolean: ${inline}`,\n [ErrorTypes.PluginUnexpected]: (inline?: string) => `Plugin unexpected error: ${inline}`,\n [ErrorTypes.PluginDoesNotExist]: (inline?: string) => `Plugin does not exist for: ${inline}`,\n [ErrorTypes.DynamicSegmentAlreadyExists]: (inline?: string) => `Dynamic segment already exists: ${inline}`,\n [ErrorTypes.WildcardNotAtEnd]: () => 'Wildcard must be at the end of the path',\n [ErrorTypes.PathIsEmpty]: () => 'Path cannot be empty',\n };\n }\n\n /**\n * Throws a formatted error based on the ErrorTypes enum.\n * @param {ErrorTypes} type The type of error to throw.\n * @param {string} [inline] Optional additional context for the error message.\n * @throws {Error} Always throws an error.\n * @protected\n */\n protected throwError(type: ErrorTypes, inline?: string): never {\n const errorType = ErrorTypes[type];\n if (errorType !== undefined) {\n const errorMessage = this.errorTypes[type](inline);\n throw new Error(errorMessage);\n }\n throw new Error('Unknown error type');\n }\n\n /**\n * Creates a new, empty node for the routing tree.\n * @returns {Node<T>} A new node object.\n * @private\n */\n private createNode(): Node<T> {\n return {\n staticChildren: Object.create(null),\n };\n }\n\n /**\n * Generates all possible path combinations for a route containing optional parameters.\n * For example, '/a/:b?/:c?' generates ['/a', '/a/:b', '/a/:c', '/a/:b/:c'].\n * Note: This currently generates combinations based on presence, not order permutations if multiple optionals are adjacent.\n * @param {string} path The path string containing optional parameters (e.g., '/users/:id?').\n * @returns {string[]} An array of path strings representing all combinations.\n * @protected\n */\n protected generateOptionals(path: string): string[] {\n // Split path into segments\n const segments = path.split('/').filter(Boolean);\n\n // Identify optional segments\n const optionalIndexes = segments\n .map((s, i) => (this.matchers.paramOptionalSegment.test(s) ? i : -1))\n .filter((i) => i >= 0);\n\n // If no optional params, return original path\n if (optionalIndexes.length === 0) {\n return ['/' + segments.join('/')];\n }\n\n // Generate all combinations using bitwise operations\n const combinations: Set<string> = new Set();\n const total = 1 << optionalIndexes.length; // 2^n combinations\n\n for (let mask = 0; mask < total; mask++) {\n const includedSegments = segments.filter((_segment, idx) => {\n // Always include non-optional segments\n if (!this.matchers.paramOptionalSegment.test(segments[idx]!)) {\n return true;\n }\n // Include optional segment if corresponding bit is set\n const bit = optionalIndexes.indexOf(idx);\n return !!(mask & (1 << bit));\n });\n\n // Build path string for this combination\n const combinationPath = includedSegments.length === 0 ? '/' : '/' + includedSegments.join('/');\n\n combinations.add(combinationPath);\n }\n\n return Array.from(combinations);\n }\n\n /**\n * Registers a static path directly into the static path cache.\n * @param {string} path The static path to register.\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path is already registered.\n * @private\n */\n private registerStaticPath(path: string, store?: T): Match<T> | never {\n if (this.staticPathCache[path]) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n // No params in static path, so we can use the store directly\n // Even though Match<T> has params definitions and here we not return params,\n // I decided to leave the return type as Match<T> so TypeScript does not recognize params as unknown\n const newStore = Object.create(store ?? this.options.storeFactory());\n this.staticPathCache[path] = newStore;\n return newStore;\n }\n\n /**\n * Registers a dynamic path into the radix tree.\n * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path conflicts with an existing registration or uses invalid syntax.\n * @private\n */\n private registerDynamicPath(path: string, store?: T): T | never {\n const segments = path.split('/').filter(Boolean);\n let currentNode: Node<T> = this.root;\n const newStore = store ?? this.options.storeFactory();\n const segmentsLength = segments.length;\n for (let i = 0; i < segmentsLength; i++) {\n const segment = segments[i] as string;\n //Static segment\n if (this.matchers.staticSegment.test(segment)) {\n currentNode = currentNode.staticChildren[segment] ??= this.createNode();\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n let pluginMatch = false;\n for (const plugin of this.plugins) {\n const pluginMeta = plugin.handler(segment);\n if (pluginMeta !== null && pluginMeta !== undefined) {\n if (pluginMeta.wildcard && i !== segmentsLength - 1) {\n this.throwError(ErrorTypes.WildcardNotAtEnd, segment);\n }\n\n // Set plugin match flag\n pluginMatch = true;\n // Copy the plugin required properties to the pluginMeta object\n pluginMeta.id = plugin.id;\n pluginMeta.priority = plugin.priority;\n pluginMeta.syntax = plugin.syntax;\n\n // Ensure dynamicChildren array exists\n currentNode.dynamicChildren ??= [];\n const existingDynamicChildren = currentNode.dynamicChildren;\n\n // 1. Check if a node for this exact parameter (same plugin id, same param name) already exists\n const existingNodeForParam = existingDynamicChildren.find(\n (child) => child.pluginMeta?.id === pluginMeta.id && child.pluginMeta?.paramName === pluginMeta.paramName,\n );\n\n if (existingNodeForParam) {\n // Exact match found, reuse this node\n currentNode = existingNodeForParam;\n } else {\n // 2. No exact match. Check for conflicts: Does another dynamic node handled by the *same plugin* exist?\n const conflictingNode = existingDynamicChildren.find((child) => child.pluginMeta?.id === pluginMeta.id);\n\n if (conflictingNode) {\n // Conflict found. Throw error unless the *new* segment allows overriding.\n if (pluginMeta.override !== true) {\n this.throwError(\n ErrorTypes.DynamicSegmentAlreadyExists,\n `${segment} (Plugin ID conflict: ${pluginMeta.id})`,\n );\n }\n // If override is true, we allow adding a new node (handled below).\n }\n // 3. No exact match and no conflict (or override allowed), create a new node.\n const newNode: Node<T> = this.createNode();\n newNode.pluginMeta = pluginMeta;\n existingDynamicChildren.push(newNode);\n // Keep dynamic children sorted by priority for matching\n // ! assertion: pluginMeta and priority are guaranteed to exist here\n existingDynamicChildren.sort((a, b) => a.pluginMeta!.priority! - b.pluginMeta!.priority!);\n currentNode = newNode;\n }\n // Break out of the loop since we found a plugin match\n break;\n }\n }\n // No plugin match found after checking all plugins\n if (!pluginMatch) {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n // No plugin found\n else {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n\n if (currentNode.store && currentNode.pluginMeta?.override !== true) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n\n currentNode.store = newStore;\n currentNode.registeredPath = path;\n return newStore;\n }\n\n /**\n * Unregisters a static path from the routing tree.\n * @param {string} path The static path to unregister (e.g., '/users/123').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterStaticPath(path: string): boolean {\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n delete this.staticPathCache[path]; // Remove from static path cache\n return true; // Successfully unregistered\n }\n return false; // Path not found in static cache\n }\n\n /**\n * Unregisters a dynamic path from the routing tree.\n * @param {string} path The dynamic path to unregister (e.g., '/users/:id', '/files/*').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterDynamicPath(path: string): boolean {\n const rootNode = this.root;\n // Segments length are always greater than 0. (If not, it would be a static path)\n const segments = path.split('/').filter(Boolean);\n const { unregistered } = this.cleanupTraversal(rootNode, segments, 0);\n return unregistered;\n }\n\n /**\n * Recursively traverses and cleans up the dynamic routing tree to unregister a dynamic path.\n * @param node The current node in the tree.\n * @param segment The current segment being processed.\n * @param segments The full array of path segments.\n * @param index The current index in the segments array.\n * @returns {boolean} True if the node should be deleted from its parent, false otherwise.\n */\n private cleanupTraversal(node: Node<T>, segments: string[], index: number): CleanupTraversalResult {\n // Helper function to check if a node is empty (no children)\n const isNodeEmpty = (node: Node<T>): boolean =>\n Object.keys(node.staticChildren).length === 0 && !node.dynamicChildren?.length;\n\n // Helper function to check if a node can be deleted (empty and no store)\n const canDeleteNode = (node: Node<T>): boolean => isNodeEmpty(node) && !node.store;\n\n // Last segment reached\n if (index === segments.length) {\n if (node.store) {\n node.store = undefined; // Clear the store\n node.registeredPath = undefined; // Clear the registered path\n return { shouldDelete: isNodeEmpty(node), unregistered: true };\n }\n return { shouldDelete: isNodeEmpty(node), unregistered: false }; // No store to clear\n }\n\n const segment = segments[index] as string;\n\n // Static segment\n if (this.matchers.staticSegment.test(segment)) {\n const staticChild = node.staticChildren[segment];\n if (staticChild) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(staticChild, segments, index + 1);\n if (shouldDelete) {\n delete node.staticChildren[segment]; // Remove the static child\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n const dynamicChildren = node.dynamicChildren;\n if (dynamicChildren) {\n for (let i = 0; i < this.plugins.length; i++) {\n const pluginConfig = this.plugins[i];\n for (let j = 0; j < dynamicChildren.length; j++) {\n const childNode = dynamicChildren[j] as Node<T>;\n const childMeta = childNode.pluginMeta;\n const pluginMeta = pluginConfig?.handler(segment);\n if (childMeta?.id === pluginConfig?.id && childMeta?.paramName === pluginMeta?.paramName) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(childNode, segments, index + 1);\n if (shouldDelete) {\n // Remove the child node from the dynamic children array\n dynamicChildren.splice(j, 1);\n // Check if the parent node can be deleted\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n }\n }\n }\n return { shouldDelete: false, unregistered: false };\n }\n\n /**\n * Validates a plugin and its configuration.\n * @param {Plugin} plugin The plugin function to validate.\n * @returns {PluginConfig} The validated plugin configuration.\n * @throws {Error} If the plugin is invalid or fails validation checks.\n * @protected\n */\n protected validatePlugin(plugin: Plugin): PluginConfig | never {\n // Check if the plugin is a function\n if (typeof plugin !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof plugin);\n }\n let pluginConfig: PluginConfig;\n // Call the plugin function to get the configuration\n try {\n pluginConfig = plugin();\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginConfig is an object\n if (!pluginConfig || typeof pluginConfig !== 'object') {\n this.throwError(ErrorTypes.PluginDoesNotReturnObject, typeof pluginConfig);\n }\n // Check if the pluginConfig has a id, priority\n if (!pluginConfig.id) {\n this.throwError(ErrorTypes.PluginMissingId);\n }\n // Check if the pluginConfig id is a string\n if (typeof pluginConfig.id !== 'string') {\n this.throwError(ErrorTypes.PluginIdIsNotString, typeof pluginConfig.id);\n }\n // Check if the pluginConfig id is already registered\n if (this.plugins.some((p) => p.id === pluginConfig.id)) {\n this.throwError(ErrorTypes.PluginWithSameIdAlreadyExists, pluginConfig.id);\n }\n // Check if the pluginConfig has a priority\n if (!pluginConfig.priority) {\n this.throwError(ErrorTypes.PluginMissingPriority);\n }\n // Check if the pluginConfig priority is a number\n if (typeof pluginConfig.priority !== 'number') {\n this.throwError(ErrorTypes.PluginPriorityIsNotNumber, typeof pluginConfig.priority);\n }\n // Check if the pluginConfig priority is already registered\n if (this.plugins.some((p) => p.priority === pluginConfig.priority)) {\n this.throwError(ErrorTypes.PluginWithSamePriorityAlreadyExists, String(pluginConfig.priority));\n }\n // Check if the pluginConfig has a syntax\n if (!pluginConfig.syntax) {\n this.throwError(ErrorTypes.PluginMissingSyntax);\n }\n // Check if the pluginConfig syntax is a string\n if (typeof pluginConfig.syntax !== 'string') {\n this.throwError(ErrorTypes.PluginSyntaxIsNotString, typeof pluginConfig.syntax);\n }\n // Check if the pluginConfig has no handler\n if (!pluginConfig.handler) {\n this.throwError(ErrorTypes.PluginMissingHandler, pluginConfig.id);\n }\n // Check if the pluginConfig has a handler\n if (typeof pluginConfig.handler !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof pluginConfig.handler);\n }\n\n let pluginMeta: PluginMeta | undefined | null;\n const syntax = pluginConfig.syntax;\n // Call the pluginConfig.handler to get the pluginMeta\n try {\n pluginMeta = pluginConfig.handler(syntax);\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginMeta is not null or undefined\n // This is important when it return null or undefined it means that the plugin unsuccessfully hanldled the given syntax\n if (pluginMeta === null || pluginMeta === undefined) {\n this.throwError(ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax, syntax);\n }\n // Check if the pluginMeta is an object\n if (!pluginMeta || typeof pluginMeta !== 'object') {\n this.throwError(ErrorTypes.PluginHandlerDoesNotReturnObject, typeof pluginMeta);\n }\n // Check if the pluginMeta has a match function\n if (!pluginMeta.match) {\n this.throwError(ErrorTypes.PluginHandlerMissingMatch);\n }\n // Check if the pluginMeta match is a function\n if (typeof pluginMeta.match !== 'function') {\n this.throwError(ErrorTypes.PluginHandlerMatchIsNotFunction, typeof pluginMeta.match);\n }\n\n let matchResult: boolean;\n // Call the pluginMeta.match to get the match result\n try {\n matchResult = pluginMeta.match({ urlSegment: '', urlSegments: [''], index: 0, params: {} });\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the matchResult is a boolean. we dont care now about the exact value.\n if (typeof matchResult !== 'boolean') {\n this.throwError(ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean, typeof matchResult);\n }\n return pluginConfig;\n }\n\n /**\n * Registers a plugin with the router.\n * Plugins extend the router's ability to handle custom parameter types or wildcards in dynamic routes.\n * Plugins are validated and added in order of their `priority` (lower number = higher precedence).\n * @param {Plugin} plugin The plugin function to register.\n * @returns {this} The router instance (for chaining).\n * @throws {Error} If the plugin is invalid or conflicts with an existing plugin.\n * @public\n */\n public use(plugin: Plugin): this | never {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n return this;\n }\n\n /**\n * Retrieves a list of all registered routes.\n * Useful for debugging or administrative purposes.\n * @returns {ListedRoute<T>[]} An array of objects, each describing a registered route.\n * @public\n */\n public inspect(): ListedRoute<T>[] {\n const listedRoute: ListedRoute<T>[] = [];\n // 1. Static Routes\n for (const path in this.staticPathCache) {\n if (Object.prototype.hasOwnProperty.call(this.staticPathCache, path)) {\n const staticStore = this.staticPathCache[path] as T;\n listedRoute.push({\n path: path,\n type: 'static',\n store: staticStore,\n });\n }\n }\n\n // 2. Dynamic Routes\n // Use a Set to ensure each unique registeredPath is added only once,\n const addedDynamicRegisteredPaths = new Set<string>();\n\n const traverse = (node: Node<T>) => {\n // If a node has a store and a registeredPath, it's a terminal node for a dynamic route.\n if (node.store && node.registeredPath) {\n if (!addedDynamicRegisteredPaths.has(node.registeredPath)) {\n listedRoute.push({\n path: node.registeredPath, // Use the stored registeredPath\n type: 'dynamic', // Routes from the tree with registeredPath are dynamic\n store: node.store,\n });\n addedDynamicRegisteredPaths.add(node.registeredPath);\n }\n }\n\n // Traverse static children\n for (const segment in node.staticChildren) {\n if (Object.prototype.hasOwnProperty.call(node.staticChildren, segment)) {\n traverse(node.staticChildren[segment]!);\n }\n }\n\n // Traverse dynamic children\n if (node.dynamicChildren) {\n for (const childNode of node.dynamicChildren) {\n traverse(childNode);\n }\n }\n };\n\n traverse(this.root); // Start traversal from the root for dynamic paths\n\n return listedRoute;\n }\n\n /**\n * Registers a route path with the router.\n * Handles static paths, dynamic paths with parameters/wildcards (requires plugins),\n * and paths with optional parameters.\n * @param {string} path The route path to register.\n * @returns {T} The store object associated with this route. You can add route-specific data to this object.\n * @throws {Error} If the path is invalid, conflicts with an existing route, or requires an unregistered plugin.\n * @public\n */\n public register(path: string): T | never {\n if (!path) {\n this.throwError(ErrorTypes.PathIsEmpty);\n }\n // Static path registration\n if (this.matchers.staticPath.test(path)) {\n return this.registerStaticPath(path);\n }\n // Param optional in path registration\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n // Define a shared store for all generated paths\n const sharedStore: T = this.options.storeFactory();\n // Register each generated path with the shared store\n for (const generatedPath of generatedPaths) {\n // For static paths, register them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n this.registerStaticPath(generatedPath, sharedStore);\n }\n // For dynamic paths, register them in root tree\n else {\n this.registerDynamicPath(generatedPath, sharedStore);\n }\n }\n return sharedStore;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.registerDynamicPath(path);\n }\n\n public unregister(path: string): boolean {\n // Check if the path is a static path\n if (this.matchers.staticPath.test(path)) {\n return this.unregisterStaticPath(path);\n }\n // Check if the path is has optional params\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n let success = false;\n for (const generatedPath of generatedPaths) {\n // For static paths, unregister them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n success = this.unregisterStaticPath(generatedPath);\n }\n // For dynamic paths, unregister them in root tree\n else {\n success = this.unregisterDynamicPath(generatedPath);\n }\n }\n return success;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.unregisterDynamicPath(path);\n }\n\n /**\n * Matches a given path against the registered routes.\n * Checks the static cache first, then traverses the radix tree for dynamic matches.\n * @param {string} path The path to match (e.g., '/users/123').\n * @returns {Match<T> | T | null}\n * - The exact store object `T` if a static path matches.\n * - A `Match<T>` object (the store object `T` augmented with a `params` property) if a dynamic path matches.\n * - `null` if no matching route is found.\n * @public\n */\n public match(path: string): Match<T> | null {\n // 1. Check static path cache (Fast O(1))\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n return cachedStore;\n }\n\n // 2. Prepare for dynamic path traversal\n const segments = path.split('/').filter(Boolean);\n const segmentCount = segments.length; // Cache length\n let currentNode: Node<T> = this.root;\n const params: Record<string, string> = Object.create(null);\n let i = 0;\n\n // 3. Traverse segments\n for (; i < segmentCount; i++) {\n const segment = segments[i] as string;\n\n // 3a. Check static children first (Fast O(1) average)\n const staticChild = currentNode.staticChildren[segment];\n if (staticChild) {\n currentNode = staticChild;\n continue; // Move to the next segment\n }\n // 3b. Check dynamic children (Potentially O(N) where N is num dynamic children)\n const dynamicChildren = currentNode.dynamicChildren;\n if (dynamicChildren) {\n let dynamicMatchFound = false;\n // Iterate through dynamic children (sorted by priority during registration)\n for (let j = 0; j < dynamicChildren.length; j++) {\n const dynamicChildNode = dynamicChildren[j] as Node<T>;\n const pluginMeta = dynamicChildNode?.pluginMeta;\n if (pluginMeta?.match({ urlSegment: segment, urlSegments: segments, index: i, params })) {\n // If it's a wildcard match, return immediately.\n if (pluginMeta.wildcard && dynamicChildNode?.store !== undefined) {\n const matchResult = Object.create(dynamicChildNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n currentNode = dynamicChildNode; // Move to the matched dynamic node\n dynamicMatchFound = true;\n break; // Stop checking other dynamic children for this segment (priority respected)\n }\n }\n\n // If no dynamic child matched this segment after checking all possibilities, return null\n if (!dynamicMatchFound) {\n return null;\n }\n }\n // 3c. No static child and no dynamic children array at this node, return null\n else {\n return null;\n }\n }\n\n // 4. End of segments reached. Check if the final node has a store.\n // Ensure we consumed all segments (i === segmentCount)\n if (i === segmentCount && currentNode.store) {\n // Create result object inheriting from store\n const matchResult = Object.create(currentNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n // 5. End of segments reached, but no store at the final node, or not all segments consumed\n return null;\n }\n}\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle basic parameters.\n * Syntax: `:paramName`\n */\nexport const param: Plugin = () => {\n const id = 'param';\n const priority = 700;\n const syntax = ':paramName';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle wildcard parameters.\n * Syntax: `*` or `:wildcardName*`\n */\nexport const wildcard: Plugin = () => {\n const id = 'wildcard';\n const priority = 800;\n const syntax = '*';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?:\\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\\*)$/.exec(segment);\n if (!match) {\n return null;\n }\n const paramName = match.groups?.wildcardName ?? '*';\n return {\n paramName,\n wildcard: true,\n match({ urlSegments, index, params }) {\n let rest = urlSegments[index];\n const segmentsLength = urlSegments.length;\n for (let j = index + 1; j < segmentsLength; j++) {\n rest += '/' + urlSegments[j];\n }\n params[paramName] = rest || '';\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with regex validation.\n * Syntax: `:paramName<\\d+>`\n */\nexport const regexParam: Plugin = () => {\n const id = 'regexParam';\n const priority = 400;\n const syntax = ':paramName<\\\\d+>';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.regex) {\n return null;\n }\n const paramName = match.groups.paramName;\n const regex = new RegExp(`^${match.groups.regex}$`);\n return {\n paramName,\n additionalMeta: {\n regex,\n },\n match({ urlSegment, params }) {\n const match = urlSegment.match(regex);\n if (!match) return false;\n params[paramName] = match[0];\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with a specific file extension.\n * Syntax: `:file.css`\n */\nexport const extensionParam: Plugin = () => {\n const id = 'extensionParam';\n const priority = 500;\n const syntax = ':file.css';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\.(?<extension>.+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.extension) {\n return null;\n }\n const paramName = match.groups.paramName;\n const extension = match.groups.extension;\n return {\n paramName,\n additionalMeta: {\n extension,\n },\n match({ urlSegment, params }) {\n if (!urlSegment.endsWith(extension)) return false;\n params[paramName] = urlSegment.slice(0, -(extension.length + 1));\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle grouped parameters.\n * Syntax: `:paramName(a|b)`\n */\nexport const groupParam: Plugin = () => {\n const id = 'groupParam';\n const priority = 300;\n const syntax = ':paramName(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.dynamicGroup) {\n return null;\n }\n const paramName = match.groups.paramName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [g, g]));\n return {\n paramName,\n additionalMeta: { group },\n match({ urlSegment, params }) {\n if (!group || !group[urlSegment]) return false;\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle prefix groups.\n * Syntax: `prefix(a|b)`\n */\nexport const prefixGroup: Plugin = () => {\n const id = 'prefixGroup';\n const priority = 100;\n const syntax = 'prefix(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional prefix groups.\n * Syntax: `prefix(a|b)?`\n */\nexport const optionalPrefixGroup: Plugin = () => {\n const id = 'optionalPrefixGroup';\n const priority = 200;\n const syntax = 'prefix(a|b)?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n group[staticName] = staticName;\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional parameters.\n * Syntax: `:paramName?`\n */\nexport const optionalParam: Plugin = () => {\n const id = 'optionalParam';\n const priority = 600;\n const syntax = ':paramName?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n override: true,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import Extreme from './src/router';\nimport type {\n Match,\n Options,\n Plugin,\n PluginConfig,\n PluginHandler,\n PluginMeta,\n ListedRoute,\n ErrorTypes,\n} from './src/types';\nimport { param } from './src/plugins/param';\nimport { wildcard } from './src/plugins/wildcard';\nimport { regexParam } from './src/plugins/regexParam';\nimport { extensionParam } from './src/plugins/extensionParam';\nimport { groupParam } from './src/plugins/groupParam';\nimport { prefixGroup } from './src/plugins/prefixGroup';\nimport { optionalPrefixGroup } from './src/plugins/optionalPrefixGroup';\nimport { optionalParam } from './src/plugins/optionalParam';\n\nexport default Extreme;\nexport type { Match, Options, Plugin, PluginConfig, PluginHandler, PluginMeta, ListedRoute, ErrorTypes };\nexport { param, wildcard, regexParam, extensionParam, groupParam, prefixGroup, optionalPrefixGroup, optionalParam };\n"],"mappings":"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","names":["ErrorTypes","Extreme","options","plugins","plugin","pluginConfig","a","b","finalOptions","storeObject","error","inline","type","ErrorTypes","errorMessage","path","segments","optionalIndexes","s","i","combinations","total","mask","includedSegments","_segment","idx","bit","combinationPath","store","newStore","currentNode","segmentsLength","segment","pluginMatch","pluginMeta","existingDynamicChildren","existingNodeForParam","child","newNode","rootNode","unregistered","node","index","isNodeEmpty","canDeleteNode","staticChild","shouldDelete","dynamicChildren","j","childNode","childMeta","p","syntax","matchResult","listedRoute","staticStore","addedDynamicRegisteredPaths","traverse","generatedPaths","sharedStore","generatedPath","success","cachedStore","segmentCount","params","dynamicMatchFound","dynamicChildNode","param","segment","match","paramName","urlSegment","params","wildcard","segment","match","paramName","urlSegments","index","params","rest","segmentsLength","j","regexParam","segment","match","paramName","regex","urlSegment","params","extensionParam","segment","match","paramName","extension","urlSegment","params","groupParam","segment","match","paramName","dynamicGroup","group","g","urlSegment","params","prefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalPrefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalParam","segment","match","paramName","urlSegment","params","index_default","Extreme"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/router.ts","../src/plugins/param.ts","../src/plugins/wildcard.ts","../src/plugins/regexParam.ts","../src/plugins/extensionParam.ts","../src/plugins/groupParam.ts","../src/plugins/prefixGroup.ts","../src/plugins/optionalPrefixGroup.ts","../src/plugins/optionalParam.ts","../index.ts"],"sourcesContent":["export type Store = object;\n\nexport type PluginHandler = (segment: string) => PluginMeta | undefined | null;\n\nexport interface PluginConfig {\n id: string;\n priority: number;\n syntax: string;\n handler: PluginHandler;\n}\n\nexport type Plugin = () => PluginConfig;\n\nexport interface PluginMeta {\n // Priority, id, syntax are automatically added by the plugin manager according to the plugin config\n // So no need to add them in the plugin meta\n // They are used here for matching priority logic (Important!), id and syntax for debugging\n paramName: string;\n priority?: number;\n id?: string;\n syntax?: string;\n override?: boolean;\n wildcard?: boolean;\n additionalMeta?: {\n group?: Record<string | number, unknown>;\n regex?: RegExp;\n extension?: string;\n [k: string]: unknown;\n };\n match: ({\n urlSegment,\n urlSegments,\n index,\n params,\n }: {\n urlSegment: string;\n urlSegments: string[];\n index: number;\n params: Record<string, unknown>;\n }) => boolean;\n}\n\nexport interface Node<T extends Store = Store> {\n registeredPath?: string;\n staticChildren: Record<string, Node<T>>;\n dynamicChildren?: Node<T>[];\n pluginMeta?: PluginMeta;\n store?: T;\n}\n\nexport interface Options<T extends Store = Store> {\n storeFactory: () => T;\n plugins: Plugin[];\n allowRegisterUpdateExisting?: boolean;\n}\n\nexport type Match<T extends Store = Store> = T & {\n params: Record<string, string>;\n};\n\nexport enum ErrorTypes {\n StoreIsNotFunction,\n StoreDoesNotReturnObject,\n StoreUnexpected,\n PathAlreadyRegistered,\n PathIsEmpty,\n PluginWithSameIdAlreadyExists,\n PluginWithSamePriorityAlreadyExists,\n PluginIsNotFunction,\n PluginDoesNotReturnObject,\n PluginsOptionNotArray,\n PluginMissingId,\n PluginMissingPriority,\n PluginIdIsNotString,\n PluginPriorityIsNotNumber,\n PluginMissingSyntax,\n PluginSyntaxIsNotString,\n PluginMissingHandler,\n PluginHandlerIsNotFunction,\n PluginHandlerReturnNullOrUndefinedForSyntax,\n PluginHandlerDoesNotReturnObject,\n PluginHandlerMissingMatch,\n PluginHandlerMatchIsNotFunction,\n PluginHandlerMatchDoesNotReturnBoolean,\n PluginUnexpected,\n PluginDoesNotExist,\n DynamicSegmentAlreadyExists,\n WildcardNotAtEnd,\n}\n\nexport interface ListedRoute<T extends Store> {\n path: string;\n type: 'static' | 'dynamic';\n store: T;\n}\n\nexport interface CleanupTraversalResult {\n shouldDelete: boolean;\n unregistered: boolean;\n}\n","import {\n ErrorTypes,\n type Match,\n type Node,\n type Options,\n type Plugin,\n type PluginConfig,\n type PluginMeta,\n type Store,\n type ListedRoute,\n type CleanupTraversalResult,\n} from './types';\n\n/**\n * High-performance, modular, tree-based router with plugin support.\n *\n * @template T The type of the store object associated with each route. Defaults to `Store`.\n */\nexport default class Extreme<T extends Store = Store> {\n /**\n * Creates a new Extreme router instance.\n * @param {Partial<Options<T>>} [options={}] Configuration options for the router.\n * @param {() => T} [options.storeFactory=() => Object.create(null)] A function that returns a new store object for each registered route.\n */\n constructor(options: Partial<Options<T>> = {}) {\n this.options = this.validateOptions(options);\n this.staticPathCache = Object.create(null);\n this.root = this.createNode();\n this.errorTypes = this.createErrorTypes();\n this.plugins = [];\n if (this.options.plugins.length > 0) {\n this.loadPlugins(this.options.plugins);\n }\n }\n\n /**\n * Default configuration options for the router.\n * @protected\n */\n protected defaultOptions: Options<T> = {\n storeFactory: () => Object.create(null),\n plugins: [],\n allowRegisterUpdateExisting: false,\n };\n\n /**\n * The validated configuration options for this router instance.\n * @protected\n */\n protected options: Options<T>;\n\n /**\n * Cache for quickly matching static paths (O(1) lookup).\n * Keys are static paths, values are the associated route stores.\n * @protected\n */\n protected staticPathCache: Record<string, Match<T>>;\n\n /**\n * The root node of the radix tree used for dynamic path matching.\n * @protected\n */\n protected root: Node<T>;\n\n /**\n * Array storing the registered plugin configurations, sorted by priority.\n * @protected\n */\n protected plugins: PluginConfig[];\n\n /**\n * Map of error types to their corresponding error message generators.\n * @protected\n */\n protected errorTypes: Record<ErrorTypes, (inline?: string) => string>;\n\n /**\n * Regular expressions used for matching path segments and types.\n * @protected\n */\n protected matchers = {\n staticPath: /^(?:\\/|\\/?(?:[a-zA-Z0-9 _.-]+)(?:\\/[a-zA-Z0-9 _.-]+)*)$/,\n paramOptionalInPath: /\\/:[a-zA-Z0-9_-]+\\?/,\n staticSegment: /^[a-zA-Z0-9 _.-]+$/,\n paramOptionalSegment: /^:[a-zA-Z0-9_-]+\\?$/,\n };\n\n /**\n * Loads and registers an array of plugins, validating each plugin before adding it to the internal plugins list.\n * After all plugins are added, the list is sorted by priority in ascending order (lower number indicates higher precedence).\n *\n * @param plugins - An array of plugins to be loaded and registered.\n */\n private loadPlugins(plugins: Plugin[]): void | never {\n plugins.forEach((plugin) => {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n });\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Validates the provided router options and merges them with defaults.\n * @param {Partial<Options<T>>} options The options provided to the constructor.\n * @returns {Options<T>} The validated and merged options.\n * @throws {Error} If the storeFactory is invalid.\n * @private\n */\n private validateOptions(options: Partial<Options<T>>): Options<T> {\n const finalOptions = { ...this.defaultOptions, ...options };\n // Store validation\n if (!finalOptions.storeFactory) {\n finalOptions.storeFactory = this.defaultOptions.storeFactory;\n } else if (typeof finalOptions.storeFactory !== 'function') {\n this.throwError(ErrorTypes.StoreIsNotFunction, typeof finalOptions.storeFactory);\n } else {\n let storeObject: T;\n try {\n storeObject = finalOptions.storeFactory();\n } catch (error) {\n this.throwError(ErrorTypes.StoreUnexpected, error instanceof Error ? error.message : String(error));\n }\n if (typeof storeObject !== 'object' || Array.isArray(storeObject)) {\n this.throwError(ErrorTypes.StoreDoesNotReturnObject, typeof storeObject);\n }\n }\n if (!Array.isArray(finalOptions.plugins)) {\n this.throwError(ErrorTypes.PluginsOptionNotArray, typeof finalOptions.plugins);\n }\n return finalOptions;\n }\n\n /**\n * Creates the map of error type enums to error message generator functions.\n * @returns {Record<ErrorTypes, (inline?: string) => string>} The error types map.\n * @private\n */\n private createErrorTypes(): typeof this.errorTypes {\n return {\n [ErrorTypes.StoreIsNotFunction]: (inline?: string) => `Store is not a function: ${inline}`,\n [ErrorTypes.StoreDoesNotReturnObject]: (inline?: string) => `Store does not return an object: ${inline}`,\n [ErrorTypes.StoreUnexpected]: (inline?: string) => `Store unexpected error: ${inline}`,\n [ErrorTypes.PathAlreadyRegistered]: (inline?: string) => `Path already registered: ${inline}`,\n [ErrorTypes.PluginWithSameIdAlreadyExists]: (inline?: string) => `Plugin with same ID already exists: ${inline}`,\n [ErrorTypes.PluginWithSamePriorityAlreadyExists]: (inline?: string) =>\n `Plugin with same priority already exists: ${inline}`,\n [ErrorTypes.PluginIsNotFunction]: (inline?: string) => `Plugin is not a function: ${inline}`,\n [ErrorTypes.PluginDoesNotReturnObject]: (inline?: string) => `Plugin does not return an object: ${inline}`,\n [ErrorTypes.PluginsOptionNotArray]: (inline?: string) => `Plugins option must be an array, got: ${inline}`,\n [ErrorTypes.PluginMissingId]: () => 'Plugin missing ID',\n [ErrorTypes.PluginMissingPriority]: () => 'Plugin missing priority',\n [ErrorTypes.PluginIdIsNotString]: (inline?: string) => `Plugin ID is not a string: ${inline}`,\n [ErrorTypes.PluginPriorityIsNotNumber]: (inline?: string) => `Plugin priority is not a number: ${inline}`,\n [ErrorTypes.PluginMissingSyntax]: () => 'Plugin missing syntax',\n [ErrorTypes.PluginSyntaxIsNotString]: (inline?: string) => `Plugin syntax is not a string: ${inline}`,\n [ErrorTypes.PluginMissingHandler]: (inline?: string) => `Plugin missing handler: ${inline}`,\n [ErrorTypes.PluginHandlerIsNotFunction]: (inline?: string) => `Plugin handler is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax]: (inline?: string) =>\n `Plugin handler returned null or undefined while matching syntax: ${inline}`,\n [ErrorTypes.PluginHandlerDoesNotReturnObject]: (inline?: string) =>\n `Plugin handler does not return an object: ${inline}`,\n [ErrorTypes.PluginHandlerMissingMatch]: () => 'Plugin handler missing match function',\n [ErrorTypes.PluginHandlerMatchIsNotFunction]: (inline?: string) =>\n `Plugin handler match is not a function: ${inline}`,\n [ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean]: (inline?: string) =>\n `Plugin handler match does not return a boolean: ${inline}`,\n [ErrorTypes.PluginUnexpected]: (inline?: string) => `Plugin unexpected error: ${inline}`,\n [ErrorTypes.PluginDoesNotExist]: (inline?: string) => `Plugin does not exist for: ${inline}`,\n [ErrorTypes.DynamicSegmentAlreadyExists]: (inline?: string) => `Dynamic segment already exists: ${inline}`,\n [ErrorTypes.WildcardNotAtEnd]: () => 'Wildcard must be at the end of the path',\n [ErrorTypes.PathIsEmpty]: () => 'Path cannot be empty',\n };\n }\n\n /**\n * Throws a formatted error based on the ErrorTypes enum.\n * @param {ErrorTypes} type The type of error to throw.\n * @param {string} [inline] Optional additional context for the error message.\n * @throws {Error} Always throws an error.\n * @protected\n */\n protected throwError(type: ErrorTypes, inline?: string): never {\n const errorType = ErrorTypes[type];\n if (errorType !== undefined) {\n const errorMessage = this.errorTypes[type](inline);\n throw new Error(errorMessage);\n }\n throw new Error('Unknown error type');\n }\n\n /**\n * Creates a new, empty node for the routing tree.\n * @returns {Node<T>} A new node object.\n * @private\n */\n private createNode(): Node<T> {\n return {\n staticChildren: Object.create(null),\n };\n }\n\n /**\n * Generates all possible path combinations for a route containing optional parameters.\n * For example, '/a/:b?/:c?' generates ['/a', '/a/:b', '/a/:c', '/a/:b/:c'].\n * Note: This currently generates combinations based on presence, not order permutations if multiple optionals are adjacent.\n * @param {string} path The path string containing optional parameters (e.g., '/users/:id?').\n * @returns {string[]} An array of path strings representing all combinations.\n * @protected\n */\n protected generateOptionals(path: string): string[] {\n // Split path into segments\n const segments = path.split('/').filter(Boolean);\n\n // Identify optional segments\n const optionalIndexes = segments\n .map((s, i) => (this.matchers.paramOptionalSegment.test(s) ? i : -1))\n .filter((i) => i >= 0);\n\n // If no optional params, return original path\n if (optionalIndexes.length === 0) {\n return ['/' + segments.join('/')];\n }\n\n // Generate all combinations using bitwise operations\n const combinations: Set<string> = new Set();\n const total = 1 << optionalIndexes.length; // 2^n combinations\n\n for (let mask = 0; mask < total; mask++) {\n const includedSegments = segments.filter((_segment, idx) => {\n // Always include non-optional segments\n if (!this.matchers.paramOptionalSegment.test(segments[idx]!)) {\n return true;\n }\n // Include optional segment if corresponding bit is set\n const bit = optionalIndexes.indexOf(idx);\n return !!(mask & (1 << bit));\n });\n\n // Build path string for this combination\n const combinationPath = includedSegments.length === 0 ? '/' : '/' + includedSegments.join('/');\n\n combinations.add(combinationPath);\n }\n\n return Array.from(combinations);\n }\n\n /**\n * Registers a static path directly into the static path cache.\n * @param {string} path The static path to register.\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path is already registered and `allowRegisterUpdateExisting` is false.\n * @private\n */\n private registerStaticPath(path: string, store?: T): Match<T> | never {\n if (this.staticPathCache[path]) {\n if (!this.options.allowRegisterUpdateExisting) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n return this.staticPathCache[path];\n }\n const newStore = Object.create(store ?? this.options.storeFactory());\n this.staticPathCache[path] = newStore;\n // No params in static path, so we can use the store directly\n // Even though Match<T> has params definitions and here we not return params,\n // I decided to leave the return type as Match<T> so TypeScript does not recognize params as unknown\n return newStore;\n }\n\n /**\n * Registers a dynamic path into the radix tree.\n * @param {string} path The dynamic path to register (e.g., '/users/:id', '/files/*').\n * @param {T} [store] Optional pre-created store object. If not provided, a new one is created using `storeFactory`.\n * @returns {T} The store object associated with the path.\n * @throws {Error} If the path conflicts with an existing registration (with allowRegisterUpdateExisting = false) or uses invalid syntax.\n * @private\n */\n private registerDynamicPath(path: string, store?: T): T | never {\n const segments = path.split('/').filter(Boolean);\n let currentNode: Node<T> = this.root;\n const newStore = store ?? this.options.storeFactory();\n const segmentsLength = segments.length;\n for (let i = 0; i < segmentsLength; i++) {\n const segment = segments[i] as string;\n //Static segment\n if (this.matchers.staticSegment.test(segment)) {\n currentNode = currentNode.staticChildren[segment] ??= this.createNode();\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n let pluginMatch = false;\n for (const plugin of this.plugins) {\n const pluginMeta = plugin.handler(segment);\n if (pluginMeta !== null && pluginMeta !== undefined) {\n if (pluginMeta.wildcard && i !== segmentsLength - 1) {\n this.throwError(ErrorTypes.WildcardNotAtEnd, segment);\n }\n\n // Set plugin match flag\n pluginMatch = true;\n // Copy the plugin required properties to the pluginMeta object\n pluginMeta.id = plugin.id;\n pluginMeta.priority = plugin.priority;\n pluginMeta.syntax = plugin.syntax;\n\n // Ensure dynamicChildren array exists\n currentNode.dynamicChildren ??= [];\n const existingDynamicChildren = currentNode.dynamicChildren;\n\n // 1. Check if a node for this exact parameter (same plugin id, same param name) already exists\n const existingNodeForParam = existingDynamicChildren.find(\n (child) => child.pluginMeta?.id === pluginMeta.id && child.pluginMeta?.paramName === pluginMeta.paramName,\n );\n\n if (existingNodeForParam) {\n // Exact match found, reuse this node\n currentNode = existingNodeForParam;\n } else {\n // 2. No exact match. Check for conflicts: Does another dynamic node handled by the *same plugin* exist?\n const conflictingNode = existingDynamicChildren.find((child) => child.pluginMeta?.id === pluginMeta.id);\n\n if (conflictingNode) {\n // Conflict found. Throw error unless the *new* segment allows overriding.\n if (pluginMeta.override !== true) {\n this.throwError(\n ErrorTypes.DynamicSegmentAlreadyExists,\n `${segment} (Plugin ID conflict: ${pluginMeta.id})`,\n );\n }\n // If override is true, we allow adding a new node (handled below).\n }\n // 3. No exact match and no conflict (or override allowed), create a new node.\n const newNode: Node<T> = this.createNode();\n newNode.pluginMeta = pluginMeta;\n existingDynamicChildren.push(newNode);\n // Keep dynamic children sorted by priority for matching\n // ! assertion: pluginMeta and priority are guaranteed to exist here\n existingDynamicChildren.sort((a, b) => a.pluginMeta!.priority! - b.pluginMeta!.priority!);\n currentNode = newNode;\n }\n // Break out of the loop since we found a plugin match\n break;\n }\n }\n // No plugin match found after checking all plugins\n if (!pluginMatch) {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n // No plugin found\n else {\n this.throwError(ErrorTypes.PluginDoesNotExist, segment);\n }\n }\n\n if (currentNode.store && currentNode.pluginMeta?.override !== true && !this.options.allowRegisterUpdateExisting) {\n this.throwError(ErrorTypes.PathAlreadyRegistered, path);\n }\n currentNode.store = currentNode.store ?? newStore;\n currentNode.registeredPath = path;\n return currentNode.store;\n }\n\n /**\n * Unregisters a static path from the routing tree.\n * @param {string} path The static path to unregister (e.g., '/users/123').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterStaticPath(path: string): boolean {\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n delete this.staticPathCache[path]; // Remove from static path cache\n return true; // Successfully unregistered\n }\n return false; // Path not found in static cache\n }\n\n /**\n * Unregisters a dynamic path from the routing tree.\n * @param {string} path The dynamic path to unregister (e.g., '/users/:id', '/files/*').\n * @returns {boolean} `true` if the path was successfully unregistered, `false` otherwise.\n * @private\n */\n private unregisterDynamicPath(path: string): boolean {\n const rootNode = this.root;\n // Segments length are always greater than 0. (If not, it would be a static path)\n const segments = path.split('/').filter(Boolean);\n const { unregistered } = this.cleanupTraversal(rootNode, segments, 0);\n return unregistered;\n }\n\n /**\n * Recursively traverses and cleans up the dynamic routing tree to unregister a dynamic path.\n * @param node The current node in the tree.\n * @param segment The current segment being processed.\n * @param segments The full array of path segments.\n * @param index The current index in the segments array.\n * @returns {boolean} True if the node should be deleted from its parent, false otherwise.\n */\n private cleanupTraversal(node: Node<T>, segments: string[], index: number): CleanupTraversalResult {\n // Helper function to check if a node is empty (no children)\n const isNodeEmpty = (node: Node<T>): boolean =>\n Object.keys(node.staticChildren).length === 0 && !node.dynamicChildren?.length;\n\n // Helper function to check if a node can be deleted (empty and no store)\n const canDeleteNode = (node: Node<T>): boolean => isNodeEmpty(node) && !node.store;\n\n // Last segment reached\n if (index === segments.length) {\n if (node.store) {\n node.store = undefined; // Clear the store\n node.registeredPath = undefined; // Clear the registered path\n return { shouldDelete: isNodeEmpty(node), unregistered: true };\n }\n return { shouldDelete: isNodeEmpty(node), unregistered: false }; // No store to clear\n }\n\n const segment = segments[index] as string;\n\n // Static segment\n if (this.matchers.staticSegment.test(segment)) {\n const staticChild = node.staticChildren[segment];\n if (staticChild) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(staticChild, segments, index + 1);\n if (shouldDelete) {\n delete node.staticChildren[segment]; // Remove the static child\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n // Dynamic segment\n else if (this.plugins.length > 0) {\n const dynamicChildren = node.dynamicChildren;\n if (dynamicChildren) {\n for (let i = 0; i < this.plugins.length; i++) {\n const pluginConfig = this.plugins[i];\n for (let j = 0; j < dynamicChildren.length; j++) {\n const childNode = dynamicChildren[j] as Node<T>;\n const childMeta = childNode.pluginMeta;\n const pluginMeta = pluginConfig?.handler(segment);\n if (childMeta?.id === pluginConfig?.id && childMeta?.paramName === pluginMeta?.paramName) {\n const { shouldDelete, unregistered } = this.cleanupTraversal(childNode, segments, index + 1);\n if (shouldDelete) {\n // Remove the child node from the dynamic children array\n dynamicChildren.splice(j, 1);\n // Check if the parent node can be deleted\n return { shouldDelete: canDeleteNode(node), unregistered };\n }\n if (unregistered) {\n return { shouldDelete: false, unregistered: true };\n }\n }\n }\n }\n }\n }\n return { shouldDelete: false, unregistered: false };\n }\n\n /**\n * Validates a plugin and its configuration.\n * @param {Plugin} plugin The plugin function to validate.\n * @returns {PluginConfig} The validated plugin configuration.\n * @throws {Error} If the plugin is invalid or fails validation checks.\n * @protected\n */\n protected validatePlugin(plugin: Plugin): PluginConfig | never {\n // Check if the plugin is a function\n if (typeof plugin !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof plugin);\n }\n let pluginConfig: PluginConfig;\n // Call the plugin function to get the configuration\n try {\n pluginConfig = plugin();\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginConfig is an object\n if (!pluginConfig || typeof pluginConfig !== 'object') {\n this.throwError(ErrorTypes.PluginDoesNotReturnObject, typeof pluginConfig);\n }\n // Check if the pluginConfig has a id, priority\n if (!pluginConfig.id) {\n this.throwError(ErrorTypes.PluginMissingId);\n }\n // Check if the pluginConfig id is a string\n if (typeof pluginConfig.id !== 'string') {\n this.throwError(ErrorTypes.PluginIdIsNotString, typeof pluginConfig.id);\n }\n // Check if the pluginConfig id is already registered\n if (this.plugins.some((p) => p.id === pluginConfig.id)) {\n this.throwError(ErrorTypes.PluginWithSameIdAlreadyExists, pluginConfig.id);\n }\n // Check if the pluginConfig has a priority\n if (!pluginConfig.priority) {\n this.throwError(ErrorTypes.PluginMissingPriority);\n }\n // Check if the pluginConfig priority is a number\n if (typeof pluginConfig.priority !== 'number') {\n this.throwError(ErrorTypes.PluginPriorityIsNotNumber, typeof pluginConfig.priority);\n }\n // Check if the pluginConfig priority is already registered\n if (this.plugins.some((p) => p.priority === pluginConfig.priority)) {\n this.throwError(ErrorTypes.PluginWithSamePriorityAlreadyExists, String(pluginConfig.priority));\n }\n // Check if the pluginConfig has a syntax\n if (!pluginConfig.syntax) {\n this.throwError(ErrorTypes.PluginMissingSyntax);\n }\n // Check if the pluginConfig syntax is a string\n if (typeof pluginConfig.syntax !== 'string') {\n this.throwError(ErrorTypes.PluginSyntaxIsNotString, typeof pluginConfig.syntax);\n }\n // Check if the pluginConfig has no handler\n if (!pluginConfig.handler) {\n this.throwError(ErrorTypes.PluginMissingHandler, pluginConfig.id);\n }\n // Check if the pluginConfig has a handler\n if (typeof pluginConfig.handler !== 'function') {\n this.throwError(ErrorTypes.PluginIsNotFunction, typeof pluginConfig.handler);\n }\n\n let pluginMeta: PluginMeta | undefined | null;\n const syntax = pluginConfig.syntax;\n // Call the pluginConfig.handler to get the pluginMeta\n try {\n pluginMeta = pluginConfig.handler(syntax);\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the pluginMeta is not null or undefined\n // This is important when it return null or undefined it means that the plugin unsuccessfully hanldled the given syntax\n if (pluginMeta === null || pluginMeta === undefined) {\n this.throwError(ErrorTypes.PluginHandlerReturnNullOrUndefinedForSyntax, syntax);\n }\n // Check if the pluginMeta is an object\n if (!pluginMeta || typeof pluginMeta !== 'object') {\n this.throwError(ErrorTypes.PluginHandlerDoesNotReturnObject, typeof pluginMeta);\n }\n // Check if the pluginMeta has a match function\n if (!pluginMeta.match) {\n this.throwError(ErrorTypes.PluginHandlerMissingMatch);\n }\n // Check if the pluginMeta match is a function\n if (typeof pluginMeta.match !== 'function') {\n this.throwError(ErrorTypes.PluginHandlerMatchIsNotFunction, typeof pluginMeta.match);\n }\n\n let matchResult: boolean;\n // Call the pluginMeta.match to get the match result\n try {\n matchResult = pluginMeta.match({ urlSegment: '', urlSegments: [''], index: 0, params: {} });\n } catch (error) {\n this.throwError(ErrorTypes.PluginUnexpected, error instanceof Error ? error.message : String(error));\n }\n // Check if the matchResult is a boolean. we dont care now about the exact value.\n if (typeof matchResult !== 'boolean') {\n this.throwError(ErrorTypes.PluginHandlerMatchDoesNotReturnBoolean, typeof matchResult);\n }\n return pluginConfig;\n }\n\n /**\n * Registers a plugin with the router.\n * Plugins extend the router's ability to handle custom parameter types or wildcards in dynamic routes.\n * Plugins are validated and added in order of their `priority` (lower number = higher precedence).\n * @param {Plugin} plugin The plugin function to register.\n * @returns {this} The router instance (for chaining).\n * @throws {Error} If the plugin is invalid or conflicts with an existing plugin.\n * @public\n */\n public use(plugin: Plugin): this | never {\n const pluginConfig = this.validatePlugin(plugin);\n this.plugins.push(pluginConfig);\n // Keep this.plugins sorted by priority (lower number = higher precedence)\n this.plugins.sort((a, b) => a.priority - b.priority);\n return this;\n }\n\n /**\n * Retrieves a list of all registered routes.\n * Useful for debugging or administrative purposes.\n * @returns {ListedRoute<T>[]} An array of objects, each describing a registered route.\n * @public\n */\n public inspect(): ListedRoute<T>[] {\n const listedRoute: ListedRoute<T>[] = [];\n // 1. Static Routes\n for (const path in this.staticPathCache) {\n if (Object.prototype.hasOwnProperty.call(this.staticPathCache, path)) {\n const staticStore = this.staticPathCache[path] as T;\n listedRoute.push({\n path: path,\n type: 'static',\n store: staticStore,\n });\n }\n }\n\n // 2. Dynamic Routes\n // Use a Set to ensure each unique registeredPath is added only once,\n const addedDynamicRegisteredPaths = new Set<string>();\n\n const traverse = (node: Node<T>) => {\n // If a node has a store and a registeredPath, it's a terminal node for a dynamic route.\n if (node.store && node.registeredPath) {\n if (!addedDynamicRegisteredPaths.has(node.registeredPath)) {\n listedRoute.push({\n path: node.registeredPath, // Use the stored registeredPath\n type: 'dynamic', // Routes from the tree with registeredPath are dynamic\n store: node.store,\n });\n addedDynamicRegisteredPaths.add(node.registeredPath);\n }\n }\n\n // Traverse static children\n for (const segment in node.staticChildren) {\n if (Object.prototype.hasOwnProperty.call(node.staticChildren, segment)) {\n traverse(node.staticChildren[segment]!);\n }\n }\n\n // Traverse dynamic children\n if (node.dynamicChildren) {\n for (const childNode of node.dynamicChildren) {\n traverse(childNode);\n }\n }\n };\n\n traverse(this.root); // Start traversal from the root for dynamic paths\n\n return listedRoute;\n }\n\n /**\n * Registers a route path with the router.\n * Handles static paths, dynamic paths with parameters/wildcards (requires plugins),\n * and paths with optional parameters.\n * @param {string} path The route path to register.\n * @returns {T} The store object associated with this route. You can add route-specific data to this object.\n * @throws {Error} If the path is invalid, conflicts with an existing route, or requires an unregistered plugin.\n * @public\n */\n public register(path: string): T | never {\n if (!path) {\n this.throwError(ErrorTypes.PathIsEmpty);\n }\n // Static path registration\n if (this.matchers.staticPath.test(path)) {\n return this.registerStaticPath(path);\n }\n // Param optional in path registration\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n // Define a shared store for all generated paths\n const sharedStore: T = this.options.storeFactory();\n // Register each generated path with the shared store\n for (const generatedPath of generatedPaths) {\n // For static paths, register them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n this.registerStaticPath(generatedPath, sharedStore);\n }\n // For dynamic paths, register them in root tree\n else {\n this.registerDynamicPath(generatedPath, sharedStore);\n }\n }\n return sharedStore;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.registerDynamicPath(path);\n }\n\n public unregister(path: string): boolean {\n // Check if the path is a static path\n if (this.matchers.staticPath.test(path)) {\n return this.unregisterStaticPath(path);\n }\n // Check if the path is has optional params\n if (this.matchers.paramOptionalInPath.test(path)) {\n // Generate all combinations of optional segments\n const generatedPaths = this.generateOptionals(path);\n let success = false;\n for (const generatedPath of generatedPaths) {\n // For static paths, unregister them in the static path cache\n if (this.matchers.staticPath.test(generatedPath)) {\n success = this.unregisterStaticPath(generatedPath);\n }\n // For dynamic paths, unregister them in root tree\n else {\n success = this.unregisterDynamicPath(generatedPath);\n }\n }\n return success;\n }\n // No static path, no param optional in path, so it must be a dynamic path\n return this.unregisterDynamicPath(path);\n }\n\n /**\n * Matches a given path against the registered routes.\n * Checks the static cache first, then traverses the radix tree for dynamic matches.\n * @param {string} path The path to match (e.g., '/users/123').\n * @returns {Match<T> | T | null}\n * - The exact store object `T` if a static path matches.\n * - A `Match<T>` object (the store object `T` augmented with a `params` property) if a dynamic path matches.\n * - `null` if no matching route is found.\n * @public\n */\n public match(path: string): Match<T> | null {\n // 1. Check static path cache (Fast O(1))\n const cachedStore = this.staticPathCache[path];\n if (cachedStore) {\n return cachedStore;\n }\n\n // 2. Prepare for dynamic path traversal\n const segments = path.split('/').filter(Boolean);\n const segmentCount = segments.length; // Cache length\n let currentNode: Node<T> = this.root;\n const params: Record<string, string> = Object.create(null);\n let i = 0;\n\n // 3. Traverse segments\n for (; i < segmentCount; i++) {\n const segment = segments[i] as string;\n\n // 3a. Check static children first (Fast O(1) average)\n const staticChild = currentNode.staticChildren[segment];\n if (staticChild) {\n currentNode = staticChild;\n continue; // Move to the next segment\n }\n // 3b. Check dynamic children (Potentially O(N) where N is num dynamic children)\n const dynamicChildren = currentNode.dynamicChildren;\n if (dynamicChildren) {\n let dynamicMatchFound = false;\n // Iterate through dynamic children (sorted by priority during registration)\n for (let j = 0; j < dynamicChildren.length; j++) {\n const dynamicChildNode = dynamicChildren[j] as Node<T>;\n const pluginMeta = dynamicChildNode?.pluginMeta;\n if (pluginMeta?.match({ urlSegment: segment, urlSegments: segments, index: i, params })) {\n // If it's a wildcard match, return immediately.\n if (pluginMeta.wildcard && dynamicChildNode?.store !== undefined) {\n const matchResult = Object.create(dynamicChildNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n currentNode = dynamicChildNode; // Move to the matched dynamic node\n dynamicMatchFound = true;\n break; // Stop checking other dynamic children for this segment (priority respected)\n }\n }\n\n // If no dynamic child matched this segment after checking all possibilities, return null\n if (!dynamicMatchFound) {\n return null;\n }\n }\n // 3c. No static child and no dynamic children array at this node, return null\n else {\n return null;\n }\n }\n\n // 4. End of segments reached. Check if the final node has a store.\n // Ensure we consumed all segments (i === segmentCount)\n if (i === segmentCount && currentNode.store) {\n // Create result object inheriting from store\n const matchResult = Object.create(currentNode.store);\n matchResult.params = params;\n return matchResult;\n }\n\n // 5. End of segments reached, but no store at the final node, or not all segments consumed\n return null;\n }\n}\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle basic parameters.\n * Syntax: `:paramName`\n */\nexport const param: Plugin = () => {\n const id = 'param';\n const priority = 700;\n const syntax = ':paramName';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle wildcard parameters.\n * Syntax: `*` or `:wildcardName*`\n */\nexport const wildcard: Plugin = () => {\n const id = 'wildcard';\n const priority = 800;\n const syntax = '*';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?:\\*|:(?<wildcardName>[a-zA-Z0-9_-]+)\\*)$/.exec(segment);\n if (!match) {\n return null;\n }\n const paramName = match.groups?.wildcardName ?? '*';\n return {\n paramName,\n wildcard: true,\n match({ urlSegments, index, params }) {\n let rest = urlSegments[index];\n const segmentsLength = urlSegments.length;\n for (let j = index + 1; j < segmentsLength; j++) {\n rest += '/' + urlSegments[j];\n }\n params[paramName] = rest || '';\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with regex validation.\n * Syntax: `:paramName<\\d+>`\n */\nexport const regexParam: Plugin = () => {\n const id = 'regexParam';\n const priority = 400;\n const syntax = ':paramName<\\\\d+>';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)<(?<regex>.+)>$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.regex) {\n return null;\n }\n const paramName = match.groups.paramName;\n const regex = new RegExp(`^${match.groups.regex}$`);\n return {\n paramName,\n additionalMeta: {\n regex,\n },\n match({ urlSegment, params }) {\n const match = urlSegment.match(regex);\n if (!match) return false;\n params[paramName] = match[0];\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle parameters with a specific file extension.\n * Syntax: `:file.css`\n */\nexport const extensionParam: Plugin = () => {\n const id = 'extensionParam';\n const priority = 500;\n const syntax = ':file.css';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\.(?<extension>.+)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.extension) {\n return null;\n }\n const paramName = match.groups.paramName;\n const extension = match.groups.extension;\n return {\n paramName,\n additionalMeta: {\n extension,\n },\n match({ urlSegment, params }) {\n if (!urlSegment.endsWith(extension)) return false;\n params[paramName] = urlSegment.slice(0, -(extension.length + 1));\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle grouped parameters.\n * Syntax: `:paramName(a|b)`\n */\nexport const groupParam: Plugin = () => {\n const id = 'groupParam';\n const priority = 300;\n const syntax = ':paramName(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName || !match.groups.dynamicGroup) {\n return null;\n }\n const paramName = match.groups.paramName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [g, g]));\n return {\n paramName,\n additionalMeta: { group },\n match({ urlSegment, params }) {\n if (!group || !group[urlSegment]) return false;\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle prefix groups.\n * Syntax: `prefix(a|b)`\n */\nexport const prefixGroup: Plugin = () => {\n const id = 'prefixGroup';\n const priority = 100;\n const syntax = 'prefix(a|b)';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional prefix groups.\n * Syntax: `prefix(a|b)?`\n */\nexport const optionalPrefixGroup: Plugin = () => {\n const id = 'optionalPrefixGroup';\n const priority = 200;\n const syntax = 'prefix(a|b)?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^(?<staticName>[a-zA-Z0-9_.-]+)\\((?<dynamicGroup>[^|)]+(\\|[^|)]+)+)\\)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.staticName || !match.groups.dynamicGroup) {\n return null;\n }\n const staticName = match.groups.staticName;\n const dynamicGroup = match.groups.dynamicGroup;\n const group = Object.fromEntries(dynamicGroup.split('|').map((g) => [staticName + g, staticName + g]));\n group[staticName] = staticName;\n return {\n paramName: '',\n additionalMeta: { group },\n match({ urlSegment }) {\n return !!(group && group[urlSegment]);\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import type { Plugin, PluginHandler } from '../types';\n\n/**\n * Plugin to handle optional parameters.\n * Syntax: `:paramName?`\n */\nexport const optionalParam: Plugin = () => {\n const id = 'optionalParam';\n const priority = 600;\n const syntax = ':paramName?';\n\n const handler: PluginHandler = (segment) => {\n const match = /^:(?<paramName>[a-zA-Z0-9_-]+)\\?$/.exec(segment);\n if (!match || !match.groups || !match.groups.paramName) {\n return null;\n }\n const paramName = match.groups.paramName;\n return {\n paramName,\n override: true,\n match({ urlSegment, params }) {\n params[paramName] = urlSegment;\n return true;\n },\n };\n };\n\n return {\n id,\n priority,\n syntax,\n handler,\n };\n};\n","import Extreme from './src/router';\nimport type {\n Match,\n Options,\n Plugin,\n PluginConfig,\n PluginHandler,\n PluginMeta,\n ListedRoute,\n ErrorTypes,\n} from './src/types';\nimport { param } from './src/plugins/param';\nimport { wildcard } from './src/plugins/wildcard';\nimport { regexParam } from './src/plugins/regexParam';\nimport { extensionParam } from './src/plugins/extensionParam';\nimport { groupParam } from './src/plugins/groupParam';\nimport { prefixGroup } from './src/plugins/prefixGroup';\nimport { optionalPrefixGroup } from './src/plugins/optionalPrefixGroup';\nimport { optionalParam } from './src/plugins/optionalParam';\n\nexport default Extreme;\nexport type { Match, Options, Plugin, PluginConfig, PluginHandler, PluginMeta, ListedRoute, ErrorTypes };\nexport { param, wildcard, regexParam, extensionParam, groupParam, prefixGroup, optionalPrefixGroup, optionalParam };\n"],"mappings":"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","names":["ErrorTypes","Extreme","options","plugins","plugin","pluginConfig","a","b","finalOptions","storeObject","error","inline","type","ErrorTypes","errorMessage","path","segments","optionalIndexes","i","combinations","total","mask","includedSegments","_segment","idx","bit","combinationPath","store","newStore","currentNode","segmentsLength","segment","pluginMatch","pluginMeta","existingDynamicChildren","existingNodeForParam","child","newNode","rootNode","unregistered","node","index","isNodeEmpty","canDeleteNode","staticChild","shouldDelete","dynamicChildren","j","childNode","childMeta","p","syntax","matchResult","listedRoute","staticStore","addedDynamicRegisteredPaths","traverse","generatedPaths","sharedStore","generatedPath","success","cachedStore","segmentCount","params","dynamicMatchFound","dynamicChildNode","param","segment","match","paramName","urlSegment","params","wildcard","segment","match","paramName","urlSegments","index","params","rest","segmentsLength","j","regexParam","segment","match","paramName","regex","urlSegment","params","extensionParam","segment","match","paramName","extension","urlSegment","params","groupParam","segment","match","paramName","dynamicGroup","group","g","urlSegment","params","prefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalPrefixGroup","segment","match","staticName","dynamicGroup","group","g","urlSegment","optionalParam","segment","match","paramName","urlSegment","params","index_default","Extreme"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extreme-router",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "A high-performance, tree-based router for JavaScript and TypeScript, featuring a powerful plugin system for extreme extensibility",
5
5
  "author": "lior cohen",
6
6
  "homepage": "https://github.com/liorcodev/extreme-router#readme",
@@ -38,20 +38,20 @@
38
38
  "parameters"
39
39
  ],
40
40
  "devDependencies": {
41
- "@eslint/js": "^9.26.0",
41
+ "@eslint/js": "^9.27.0",
42
42
  "@types/benchmark": "^2.1.5",
43
43
  "@types/bun": "latest",
44
44
  "@vitest/coverage-v8": "3.1.2",
45
45
  "benchmark": "^2.1.4",
46
46
  "chalk": "^5.4.1",
47
- "eslint": "^9.26.0",
47
+ "eslint": "^9.27.0",
48
48
  "globals": "^16.1.0",
49
49
  "husky": "^9.1.7",
50
50
  "lint-staged": "^15.5.2",
51
51
  "minimist": "^1.2.8",
52
52
  "prettier": "^3.5.3",
53
- "tsup": "^8.4.0",
54
- "typescript-eslint": "^8.32.0",
53
+ "tsup": "^8.5.0",
54
+ "typescript-eslint": "^8.32.1",
55
55
  "vitest": "^3.1.3"
56
56
  },
57
57
  "files": [