express-version-api 1.0.1 → 2.0.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/config.ts","../src/version-extractor.ts","../src/version-parser.ts","../src/handler-compiler.ts","../src/middleware.ts"],"names":[],"mappings":";;;AAySO,IAAM,eAAA,GAAN,MAAM,gBAAA,SAAwB,KAAA,CAAM;AAAA,EACzB,IAAA;AAAA,EACA,gBAAA;AAAA,EACA,iBAAA;AAAA,EACA,UAAA;AAAA,EAEhB,WAAA,CAAY,OAAA,EAAiC,UAAA,GAAa,GAAA,EAAK;AAC7D,IAAA,KAAA,CAAM,QAAQ,OAAO,CAAA;AACrB,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,mBAAmB,OAAA,CAAQ,gBAAA;AAChC,IAAA,IAAA,CAAK,oBAAoB,OAAA,CAAQ,iBAAA;AACjC,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAGlB,IAAA,KAAA,CAAM,iBAAA,CAAkB,MAAM,gBAAe,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAA0D;AACxD,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,GAAI,IAAA,CAAK,gBAAA,KAAqB,UAAa,EAAE,gBAAA,EAAkB,KAAK,gBAAA,EAAiB;AAAA,MACrF,GAAI,IAAA,CAAK,iBAAA,KAAsB,UAAa,EAAE,iBAAA,EAAmB,KAAK,iBAAA;AAAkB,KAC1F;AAAA,EACF;AACF;;;AC9TO,SAAS,yBAAA,CACd,UAAA,GAAa,GAAA,EACb,OAAA,GAAU,yBAAA,EACO;AACjB,EAAA,OAAO,IAAI,eAAA;AAAA,IACT;AAAA,MACE,IAAA,EAAM,iBAAA;AAAA,MACN;AAAA,KACF;AAAA,IACA;AAAA,GACF;AACF;AAKO,SAAS,0BAAA,CACd,gBAAA,EACA,iBAAA,EACA,UAAA,GAAa,GAAA,EACI;AACjB,EAAA,OAAO,IAAI,eAAA;AAAA,IACT;AAAA,MACE,IAAA,EAAM,mBAAA;AAAA,MACN,OAAA,EAAS,qCAAqC,gBAAgB,CAAA,CAAA,CAAA;AAAA,MAC9D,gBAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA;AAAA,GACF;AACF;AAKO,SAAS,+BAAA,CACd,OAAA,EACA,UAAA,GAAa,GAAA,EACI;AACjB,EAAA,OAAO,IAAI,eAAA;AAAA,IACT;AAAA,MACE,IAAA,EAAM,wBAAA;AAAA,MACN,OAAA,EAAS,4BAA4B,OAAO,CAAA,CAAA,CAAA;AAAA,MAC5C,gBAAA,EAAkB;AAAA,KACpB;AAAA,IACA;AAAA,GACF;AACF;AAKO,SAAS,yBAAA,CAA0B,KAAa,MAAA,EAAiC;AACtF,EAAA,OAAO,IAAI,eAAA;AAAA,IACT;AAAA,MACE,IAAA,EAAM,iBAAA;AAAA,MACN,OAAA,EAAS,CAAA,6BAAA,EAAgC,GAAG,CAAA,GAAA,EAAM,MAAM,CAAA;AAAA,KAC1D;AAAA,IACA;AAAA,GACF;AACF;AAKO,SAAS,gCAAgC,MAAA,EAAiC;AAC/E,EAAA,OAAO,IAAI,eAAA;AAAA,IACT;AAAA,MACE,IAAA,EAAM,uBAAA;AAAA,MACN,OAAA,EAAS,0BAA0B,MAAM,CAAA;AAAA,KAC3C;AAAA,IACA;AAAA,GACF;AACF;AASO,SAAS,kBAAkB,KAAA,EAA0C;AAC1E,EAAA,OAAO,KAAA,YAAiB,eAAA;AAC1B;AAKO,SAAS,YAAA,CAAa,OAAgB,IAAA,EAAqD;AAChG,EAAA,OAAO,iBAAA,CAAkB,KAAK,CAAA,IAAK,KAAA,CAAM,IAAA,KAAS,IAAA;AACpD;;;AC1FO,IAAM,cAAA,GAAiC;AAAA,EAC5C,UAAA,EAAY;AAAA,IACV,OAAA,EAAS,CAAC,QAAA,EAAU,OAAO,CAAA;AAAA,IAC3B,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,KAAA,EAAO;AAAA,MACL,IAAA,EAAM;AAAA,KACR;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,OAAA,EAAS;AAAA,KACX;AAAA,IACA,MAAA,EAAQ;AAAA,GACV;AAAA,EACA,aAAA,EAAe;AAAA,IACb,oBAAA,EAAsB,GAAA;AAAA,IACtB,qBAAA,EAAuB,GAAA;AAAA,IACvB,qBAAA,EAAuB,yBAAA;AAAA,IACvB,sBAAA,EAAwB,gDAAA;AAAA,IACxB,uBAAA,EAAyB,IAAA;AAAA,IACzB,OAAA,EAAS;AAAA,GACX;AAAA,EACA,gBAAA,EAAkB,SAAA;AAAA,EAClB,cAAA,EAAgB,MAAA;AAAA,EAChB,gBAAA,EAAkB,IAAA;AAAA,EAClB,iBAAA,EAAmB,IAAA;AAAA,EACnB,cAAA,EAAgB;AAClB;AAaO,SAAS,cAAc,OAAA,EAA6C;AACzE,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,cAAA;AAAA,EACT;AAGA,EAAA,IAAI,OAAA,CAAQ,qBAAqB,MAAA,EAAW;AAC1C,IAAA,MAAM,kCAAkB,IAAI,GAAA,CAAI,CAAC,QAAA,EAAU,MAAA,EAAQ,SAAS,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,OAAA,CAAQ,gBAAgB,CAAA,EAAG;AAClD,MAAA,MAAM,+BAAA;AAAA,QACJ,CAAA,4BAAA,EAA+B,QAAQ,gBAAgB,CAAA,0CAAA;AAAA,OACzD;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI,OAAA,CAAQ,YAAY,OAAA,EAAS;AAC/B,IAAA,MAAM,YAAA,uBAAmB,GAAA,CAAI,CAAC,UAAU,OAAA,EAAS,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAClE,IAAA,KAAA,MAAW,MAAA,IAAU,OAAA,CAAQ,UAAA,CAAW,OAAA,EAAS;AAC/C,MAAA,IAAI,CAAC,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA,EAAG;AAC7B,QAAA,MAAM,+BAAA;AAAA,UACJ,4BAA4B,MAAM,CAAA,6CAAA;AAAA,SACpC;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,OAAA,CAAQ,WAAW,OAAA,CAAQ,QAAA,CAAS,QAAQ,CAAA,IAAK,CAAC,OAAA,CAAQ,UAAA,CAAW,MAAA,EAAQ;AAC/E,MAAA,MAAM,+BAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI,OAAA,CAAQ,YAAY,IAAA,EAAM,OAAA,IAAW,EAAE,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,OAAA,YAAmB,MAAA,CAAA,EAAS;AAC7F,IAAA,MAAM,gCAAgC,wCAAwC,CAAA;AAAA,EAChF;AAGA,EAAA,IAAI,OAAA,CAAQ,aAAA,EAAe,oBAAA,KAAyB,MAAA,EAAW;AAC7D,IAAA,MAAM,MAAA,GAAS,QAAQ,aAAA,CAAc,oBAAA;AACrC,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,GAAA,IAAO,SAAS,GAAA,EAAK;AAC7D,MAAA,MAAM,+BAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,CAAQ,aAAA,EAAe,qBAAA,KAA0B,MAAA,EAAW;AAC9D,IAAA,MAAM,MAAA,GAAS,QAAQ,aAAA,CAAc,qBAAA;AACrC,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,GAAA,IAAO,SAAS,GAAA,EAAK;AAC7D,MAAA,MAAM,+BAAA;AAAA,QACJ;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,OAAO;AAAA,IACL,UAAA,EAAY;AAAA,MACV,OAAA,EAAS,OAAA,CAAQ,UAAA,EAAY,OAAA,IAAW,eAAe,UAAA,CAAW,OAAA;AAAA,MAClE,MAAA,EAAQ;AAAA,QACN,MAAM,OAAA,CAAQ,UAAA,EAAY,QAAQ,IAAA,IAAQ,cAAA,CAAe,WAAW,MAAA,CAAO;AAAA,OAC7E;AAAA,MACA,KAAA,EAAO;AAAA,QACL,MAAM,OAAA,CAAQ,UAAA,EAAY,OAAO,IAAA,IAAQ,cAAA,CAAe,WAAW,KAAA,CAAM;AAAA,OAC3E;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,SAAS,OAAA,CAAQ,UAAA,EAAY,MAAM,OAAA,IAAW,cAAA,CAAe,WAAW,IAAA,CAAK;AAAA,OAC/E;AAAA,MACA,MAAA,EAAQ,QAAQ,UAAA,EAAY;AAAA,KAC9B;AAAA,IACA,aAAA,EAAe;AAAA,MACb,oBAAA,EACE,OAAA,CAAQ,aAAA,EAAe,oBAAA,IACvB,eAAe,aAAA,CAAc,oBAAA;AAAA,MAC/B,qBAAA,EACE,OAAA,CAAQ,aAAA,EAAe,qBAAA,IACvB,eAAe,aAAA,CAAc,qBAAA;AAAA,MAC/B,qBAAA,EACE,OAAA,CAAQ,aAAA,EAAe,qBAAA,IACvB,eAAe,aAAA,CAAc,qBAAA;AAAA,MAC/B,sBAAA,EACE,OAAA,CAAQ,aAAA,EAAe,sBAAA,IACvB,eAAe,aAAA,CAAc,sBAAA;AAAA,MAC/B,uBAAA,EACE,OAAA,CAAQ,aAAA,EAAe,uBAAA,IACvB,eAAe,aAAA,CAAc,uBAAA;AAAA,MAC/B,OAAA,EAAS,QAAQ,aAAA,EAAe;AAAA,KAClC;AAAA,IACA,gBAAA,EAAkB,OAAA,CAAQ,gBAAA,IAAoB,cAAA,CAAe,gBAAA;AAAA,IAC7D,cAAA,EAAgB,OAAA,CAAQ,cAAA,IAAkB,cAAA,CAAe,cAAA;AAAA,IACzD,gBAAA,EAAkB,OAAA,CAAQ,gBAAA,IAAoB,cAAA,CAAe,gBAAA;AAAA,IAC7D,iBAAA,EAAmB,OAAA,CAAQ,iBAAA,IAAqB,cAAA,CAAe,iBAAA;AAAA,IAC/D,cAAA,EAAgB,OAAA,CAAQ,cAAA,IAAkB,cAAA,CAAe;AAAA,GAC3D;AACF;AAQO,SAAS,aAAa,MAAA,EAAkD;AAC7E,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,GAAG,MAAA;AAAA,IACH,UAAA,EAAY,OAAO,MAAA,CAAO;AAAA,MACxB,GAAG,MAAA,CAAO,UAAA;AAAA,MACV,OAAA,EAAS,OAAO,MAAA,CAAO,CAAC,GAAG,MAAA,CAAO,UAAA,CAAW,OAAO,CAAC,CAAA;AAAA,MACrD,MAAA,EAAQ,OAAO,MAAA,CAAO,EAAE,GAAG,MAAA,CAAO,UAAA,CAAW,QAAQ,CAAA;AAAA,MACrD,KAAA,EAAO,OAAO,MAAA,CAAO,EAAE,GAAG,MAAA,CAAO,UAAA,CAAW,OAAO,CAAA;AAAA,MACnD,IAAA,EAAM,OAAO,MAAA,CAAO,EAAE,GAAG,MAAA,CAAO,UAAA,CAAW,MAAM;AAAA,KAClD,CAAA;AAAA,IACD,eAAe,MAAA,CAAO,MAAA,CAAO,EAAE,GAAG,MAAA,CAAO,eAAe;AAAA,GACzD,CAAA;AACH;;;AC1JA,SAAS,iBAAA,CAAkB,KAAuB,UAAA,EAAwC;AACxF,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,CAAQ,UAAA,CAAW,aAAa,CAAA;AAElD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,IAAA,EAAK,CAAE,SAAS,CAAA,EAAG;AACxD,IAAA,OAAO,MAAM,IAAA,EAAK;AAAA,EACpB;AAGA,EAAA,IAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,IAAK,MAAM,MAAA,GAAS,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,EAAG;AACxD,IAAA,OAAO,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,EAAK;AAAA,EACvB;AAEA,EAAA,OAAO,MAAA;AACT;AASA,SAAS,gBAAA,CAAiB,KAAuB,SAAA,EAAuC;AACtF,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,SAAS,CAAA;AAEjC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,IAAA,EAAK,CAAE,SAAS,CAAA,EAAG;AACxD,IAAA,OAAO,MAAM,IAAA,EAAK;AAAA,EACpB;AAEA,EAAA,OAAO,MAAA;AACT;AASA,SAAS,eAAA,CAAgB,KAAuB,OAAA,EAAqC;AACnF,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,GAAA,CAAI,GAAA;AAC7B,EAAA,MAAM,iBAAA,GAAoB,OAAA,CAAQ,MAAA,IAAU,OAAA,CAAQ,MAAA;AAEpD,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,OAAA,CAAQ,SAAA,GAAY,CAAA;AAAA,EACtB;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAE/B,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,OAAA,CAAQ,SAAA,GAAY,CAAA;AAAA,EACtB;AAEA,EAAA,IAAI,KAAA,GAAQ,CAAC,CAAA,EAAG;AACd,IAAA,OAAO,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,EAAK;AAAA,EACvB;AAEA,EAAA,OAAO,MAAA;AACT;AAQA,SAAS,mBAAmB,GAAA,EAA2C;AACrE,EAAA,IAAI,OAAO,IAAI,OAAA,KAAY,QAAA,IAAY,IAAI,OAAA,CAAQ,IAAA,EAAK,CAAE,MAAA,GAAS,CAAA,EAAG;AACpE,IAAA,OAAO,GAAA,CAAI,QAAQ,IAAA,EAAK;AAAA,EAC1B;AAEA,EAAA,OAAO,MAAA;AACT;AAYO,SAAS,uBACd,MAAA,EACyD;AACzD,EAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAQ,OAAO,IAAA,EAAM,MAAA,KAAW,MAAA,CAAO,UAAA;AAExD,EAAA,OAAO,CAAC,GAAA,KAAwD;AAE9D,IAAA,MAAM,cAAA,GAAiB,mBAAmB,GAAG,CAAA;AAC7C,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,OAAO,EAAE,OAAA,EAAS,cAAA,EAAgB,MAAA,EAAQ,QAAA,EAAS;AAAA,IACrD;AAGA,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,OAAA;AAEJ,MAAA,QAAQ,MAAA;AAAQ,QACd,KAAK,QAAA;AACH,UAAA,OAAA,GAAU,iBAAA,CAAkB,GAAA,EAAK,MAAA,CAAO,IAAI,CAAA;AAC5C,UAAA;AAAA,QAEF,KAAK,OAAA;AACH,UAAA,OAAA,GAAU,gBAAA,CAAiB,GAAA,EAAK,KAAA,CAAM,IAAI,CAAA;AAC1C,UAAA;AAAA,QAEF,KAAK,MAAA;AACH,UAAA,OAAA,GAAU,eAAA,CAAgB,GAAA,EAAK,IAAA,CAAK,OAAO,CAAA;AAC3C,UAAA;AAAA,QAEF,KAAK,QAAA;AACH,UAAA,OAAA,GAAU,SAAS,GAAG,CAAA;AACtB,UAAA;AAAA;AAGJ,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,OAAO,EAAE,SAAS,MAAA,EAAO;AAAA,MAC3B;AAAA,IACF;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF;;;AChIA,IAAM,aAAA,GAAgB,wCAAA;AAKtB,SAAS,2BAA2B,QAAA,EAA6B;AAC/D,EAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA;AAEvC,EAAA,IAAI,SAAS,CAAA,EAAG;AACd,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,IAAI,UAAU,CAAA,EAAG;AACf,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,OAAO,CAAA;AACT;AAoBO,SAAS,aAAa,OAAA,EAAuC;AAClE,EAAA,MAAM,OAAA,GAAU,QAAQ,IAAA,EAAK;AAC7B,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,IAAA,CAAK,OAAO,CAAA;AAExC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAK,QAAA,EAAU,QAAA,EAAU,QAAQ,CAAA,GAAI,KAAA;AAE3C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAG1C,EAAA,IAAI,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,CAAM,KAAK,CAAA,EAAG;AAChD,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,CAAA,IAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,KAAA;AAAA,IACA,KAAA;AAAA,IACA,GAAA,EAAK;AAAA,GACP;AACF;AAeO,SAAS,kBAAkB,KAAA,EAA0C;AAC1E,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,IAAA,CAAK,OAAO,CAAA;AAExC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAG,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAU,QAAQ,CAAA,GAAI,KAAA;AAEjD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,IAAY,GAAA,EAAK,EAAE,CAAA;AAE1C,EAAA,IAAI,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,CAAM,KAAK,CAAA,EAAG;AAChD,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,WAAW,GAAA,EAAK;AAClB,IAAA,IAAA,GAAO,OAAA;AAAA,EACT,CAAA,MAAA,IAAW,WAAW,GAAA,EAAK;AACzB,IAAA,IAAA,GAAO,OAAA;AAAA,EACT,CAAA,MAAO;AACL,IAAA,IAAA,GAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,OAAA,EAAS,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAK,OAAA,CAAQ,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA,EAAE;AAAA,IACnE,GAAA,EAAK;AAAA,GACP;AACF;AA4BO,SAAS,gBAAA,CAAiB,eAA8B,KAAA,EAAoC;AACjG,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,OAAO,MAAA,EAAQ,KAAA,EAAO,QAAO,GAAI,aAAA;AACxD,EAAA,MAAM,EAAE,OAAO,MAAA,EAAQ,KAAA,EAAO,QAAQ,KAAA,EAAO,MAAA,KAAW,KAAA,CAAM,OAAA;AAE9D,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,OAAA,EAAS;AAEZ,MAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,QAAA,OAAO,KAAA;AAAA,MACT;AAGA,MAAA,IAAI,SAAS,CAAA,EAAG;AACd,QAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,UAAA,OAAO,KAAA;AAAA,QACT;AAEA,QAAA,IAAI,MAAA,KAAW,MAAA,IAAU,MAAA,GAAS,MAAA,EAAQ;AACxC,UAAA,OAAO,KAAA;AAAA,QACT;AAEA,QAAA,OAAO,IAAA;AAAA,MACT;AAIA,MAAA,IAAI,SAAS,CAAA,EAAG;AACd,QAAA,OAAO,MAAA,KAAW,UAAU,MAAA,IAAU,MAAA;AAAA,MACxC;AAKA,MAAA,MAAM,cAAA,GAAiB,0BAAA,CAA2B,KAAA,CAAM,GAAG,CAAA;AAE3D,MAAA,IAAI,mBAAmB,CAAA,EAAG;AACxB,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,IAAI,mBAAmB,CAAA,EAAG;AACxB,QAAA,OAAO,MAAA,KAAW,CAAA;AAAA,MACpB;AAEA,MAAA,OAAO,MAAA,KAAW,KAAK,MAAA,KAAW,MAAA;AAAA,IACpC;AAAA,IAEA,KAAK,OAAA,EAAS;AAEZ,MAAA,IAAI,MAAA,KAAW,MAAA,IAAU,MAAA,KAAW,MAAA,EAAQ;AAC1C,QAAA,OAAO,KAAA;AAAA,MACT;AAEA,MAAA,OAAO,MAAA,IAAU,MAAA;AAAA,IACnB;AAAA,IAEA,KAAK,OAAA,EAAS;AAEZ,MAAA,OAAO,MAAA,KAAW,MAAA,IAAU,MAAA,KAAW,MAAA,IAAU,MAAA,KAAW,MAAA;AAAA,IAC9D;AAAA;AAEJ;AAaO,SAAS,eAAA,CAAgB,GAAkB,CAAA,EAA8B;AAC9E,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,CAAA,CAAE,KAAA,EAAO;AACvB,IAAA,OAAO,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,GAAQ,CAAA,GAAI,EAAA;AAAA,EACjC;AACA,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,CAAA,CAAE,KAAA,EAAO;AACvB,IAAA,OAAO,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,GAAQ,CAAA,GAAI,EAAA;AAAA,EACjC;AACA,EAAA,IAAI,CAAA,CAAE,KAAA,KAAU,CAAA,CAAE,KAAA,EAAO;AACvB,IAAA,OAAO,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAA,GAAQ,CAAA,GAAI,EAAA;AAAA,EACjC;AACA,EAAA,OAAO,CAAA;AACT;AAeO,SAAS,kBAAkB,QAAA,EAA4C;AAC5E,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA,GAA+B,IAAA;AACnC,EAAA,IAAI,SAAA,GAAY,EAAA;AAEhB,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,IAAA,MAAM,KAAA,GAAQ,kBAAkB,OAAO,CAAA;AACvC,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,MAAA,IAAU,eAAA,CAAgB,MAAM,OAAA,EAAS,MAAM,IAAI,CAAA,EAAG;AACzD,MAAA,MAAA,GAAS,KAAA,CAAM,OAAA;AACf,MAAA,SAAA,GAAY,MAAM,OAAA,CAAQ,GAAA;AAAA,IAC5B;AAAA,EACF;AAEA,EAAA,OAAO,SAAA,IAAa,IAAA;AACtB;AAYO,SAAS,eAAe,OAAA,EAA0B;AACvD,EAAA,OAAO,YAAA,CAAa,OAAO,CAAA,KAAM,IAAA;AACnC;AAQO,SAAS,oBAAoB,KAAA,EAAwB;AAC1D,EAAA,OAAO,iBAAA,CAAkB,KAAK,CAAA,KAAM,IAAA;AACtC;AAgBO,SAAS,iBAAiB,OAAA,EAAgC;AAC/D,EAAA,MAAM,KAAA,GAAQ,kBAAkB,OAAO,CAAA;AACvC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,KAAU,KAAA,CAAM,OAAA;AACtC,EAAA,OAAO,CAAA,EAAG,KAAA,CAAM,QAAA,EAAU,CAAA,CAAA,EAAI,KAAA,CAAM,QAAA,EAAU,CAAA,CAAA,EAAI,KAAA,CAAM,QAAA,EAAU,CAAA,CAAA;AACpE;;;ACxSA,SAAS,kBAAkB,OAAA,EAAkC;AAC3D,EAAA,MAAM,EAAE,OAAM,GAAI,OAAA;AAClB,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,KAAU,KAAA,CAAM,OAAA;AAGtC,EAAA,MAAM,YAAA,GAAe,KAAA,GAAQ,GAAA,GAAY,KAAA,GAAQ,GAAA,GAAQ,KAAA;AAEzD,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,OAAA;AACH,MAAA,OAAO,GAAA,GAAgB,YAAA;AAAA,IACzB,KAAK,OAAA;AACH,MAAA,OAAO,GAAA,GAAc,YAAA;AAAA,IACvB,KAAK,OAAA;AACH,MAAA,OAAO,YAAA;AAAA;AAEb;AAoBO,SAAS,eAAA,CAAgB,QAAA,EAA2B,QAAA,GAAW,IAAA,EAAyB;AAC7F,EAAA,MAAM,WAA8B,EAAC;AAErC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,OAAO,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAErD,IAAA,IAAI,QAAA,IAAY,OAAO,OAAA,KAAY,UAAA,EAAY;AAC7C,MAAA,MAAM,yBAAA,CAA0B,KAAK,4BAA4B,CAAA;AAAA,IACnE;AAGA,IAAA,MAAM,KAAA,GAAQ,kBAAkB,GAAG,CAAA;AAEnC,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,yBAAA;AAAA,UACJ,GAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,eAAA,GAAmC;AAAA,MACvC,GAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA,EAAU;AAAA;AAAA,KACZ;AAGA,IAAC,eAAA,CAAyC,QAAA,GAAW,iBAAA,CAAkB,eAAe,CAAA;AAEtF,IAAA,QAAA,CAAS,KAAK,eAAe,CAAA;AAAA,EAC/B;AAGA,EAAA,QAAA,CAAS,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,GAAW,EAAE,QAAQ,CAAA;AAE/C,EAAA,OAAO,QAAA;AACT;AAkBO,SAAS,mBAAA,CACd,eACA,gBAAA,EAC6B;AAC7B,EAAA,KAAA,MAAW,WAAW,gBAAA,EAAkB;AACtC,IAAA,IAAI,gBAAA,CAAiB,aAAA,EAAe,OAAA,CAAQ,KAAK,CAAA,EAAG;AAClD,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,kBACd,gBAAA,EAC6B;AAC7B,EAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAAG;AACjC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,MAAA;AAEJ,EAAA,KAAA,MAAW,WAAW,gBAAA,EAAkB;AACtC,IAAA,IAAI,CAAC,MAAA,IAAU,eAAA,CAAgB,OAAA,CAAQ,KAAA,CAAM,SAAS,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,GAAI,CAAA,EAAG;AAC/E,MAAA,MAAA,GAAS,OAAA;AAAA,IACX;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,qBAAqB,gBAAA,EAAwD;AAC3F,EAAA,OAAO,gBAAA,CAAiB,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,GAAG,CAAA;AAC1C;;;ACjIA,SAAS,iBAAA,CACP,KAAA,EACA,GAAA,EACA,GAAA,EACA,MACA,MAAA,EACM;AAEN,EAAA,IAAI,MAAA,CAAO,cAAc,OAAA,EAAS;AAChC,IAAA,MAAM,UAAU,MAAA,CAAO,aAAA,CAAc,QAAQ,KAAA,EAAO,GAAA,EAAK,KAAK,IAAI,CAAA;AAClE,IAAA,IAAI,YAAY,IAAA,EAAM;AACpB,MAAA;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,QAAA,GAAoC;AAAA,IACxC,OAAO,KAAA,CAAM,IAAA;AAAA,IACb,SAAS,KAAA,CAAM;AAAA,GACjB;AAEA,EAAA,IAAI,MAAA,CAAO,aAAA,CAAc,uBAAA,IAA2B,KAAA,CAAM,gBAAA,EAAkB;AAC1E,IAAA,QAAA,CAAS,kBAAkB,IAAI,KAAA,CAAM,gBAAA;AAAA,EACvC;AAEA,EAAA,IAAI,KAAA,CAAM,iBAAA,IAAqB,KAAA,CAAM,iBAAA,CAAkB,SAAS,CAAA,EAAG;AACjE,IAAA,QAAA,CAAS,mBAAmB,IAAI,KAAA,CAAM,iBAAA;AAAA,EACxC;AAEA,EAAA,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,UAAU,CAAA,CAAE,KAAK,QAAQ,CAAA;AAC5C;AAKA,SAAS,yBAAA,CAA0B,SAAiB,MAAA,EAAgC;AAClF,EAAA,MAAM,OAAA,GAAU,OAAO,aAAA,CAAc,sBAAA;AAErC,EAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,IAAA,OAAO,QAAQ,OAAO,CAAA;AAAA,EACxB;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,yBAAyB,MAAA,EAAgC;AAChE,EAAA,OAAO,OAAO,aAAA,CAAc,qBAAA;AAC9B;AAyCO,SAAS,oBAAA,CACd,UACA,OAAA,EACgB;AAEhB,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3B,IAAA,MAAM,gCAAgC,iDAAiD,CAAA;AAAA,EACzF;AAGA,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,aAAA,CAAc,OAAO,CAAC,CAAA;AAGlD,EAAA,MAAM,gBAAA,GAAmB,eAAA,CAAgB,QAAA,EAAU,MAAA,CAAO,gBAAgB,CAAA;AAG1E,EAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAAG;AACjC,IAAA,MAAM,+BAAA;AAAA,MACJ;AAAA,KACF;AAAA,EACF;AAGA,EAAA,MAAM,cAAA,GAAiB,uBAAuB,MAAM,CAAA;AAGpD,EAAA,MAAM,iBAAA,GAAoB,qBAAqB,gBAAgB,CAAA;AAG/D,EAAA,OAAO,CAAC,GAAA,EAAuB,GAAA,EAAe,IAAA,KAA6B;AAEzE,IAAA,MAAM,UAAA,GAAa,eAAe,GAAG,CAAA;AAGrC,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA,IAAI,CAAC,OAAO,cAAA,EAAgB;AAE1B,QAAA,cAAA;AAAA,UACE,MAAA;AAAA,UACA,MAAA;AAAA,UACA,gBAAA;AAAA,UACA,MAAA;AAAA,UACA,iBAAA;AAAA,UACA,GAAA;AAAA,UACA,GAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,KAAA,GAAQ,yBAAA;AAAA,QACZ,OAAO,aAAA,CAAc,oBAAA;AAAA,QACrB,yBAAyB,MAAM;AAAA,OACjC;AACA,MAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAC/C,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,OAAA,EAAS,aAAA,EAAe,MAAA,EAAO,GAAI,UAAA;AAG3C,IAAA,MAAM,aAAA,GAAgB,aAAa,aAAa,CAAA;AAEhD,IAAA,IAAI,CAAC,aAAA,EAAe;AAClB,MAAA,MAAM,KAAA,GAAQ,gCAAgC,aAAa,CAAA;AAC3D,MAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAC/C,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,cAAA,GAAiB,mBAAA,CAAoB,aAAA,EAAe,gBAAgB,CAAA;AAE1E,IAAA,IAAI,cAAA,EAAgB;AAElB,MAAA,IAAI,OAAO,iBAAA,EAAmB;AAC5B,QAAA,GAAA,CAAI,WAAA,GAAc;AAAA,UAChB,SAAA,EAAW,aAAA;AAAA,UACX,SAAS,cAAA,CAAe,GAAA;AAAA,UACxB;AAAA,SACF;AAAA,MACF;AAGA,MAAA,cAAA,CAAe,cAAA,EAAgB,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAC7C,MAAA;AAAA,IACF;AAGA,IAAA,cAAA;AAAA,MACE,aAAA;AAAA,MACA,MAAA;AAAA,MACA,gBAAA;AAAA,MACA,MAAA;AAAA,MACA,iBAAA;AAAA,MACA,GAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AACF;AASA,SAAS,cAAA,CACP,kBACA,eAAA,EACA,gBAAA,EACA,QACA,iBAAA,EACA,GAAA,EACA,KACA,IAAA,EACM;AACN,EAAA,QAAQ,OAAO,gBAAA;AAAkB,IAC/B,KAAK,MAAA,EAAQ;AAEX,MAAA,MAAM,KAAA,GAAQ,0BAAA;AAAA,QACZ,gBAAA,IAAoB,SAAA;AAAA,QACpB,iBAAA;AAAA,QACA,OAAO,aAAA,CAAc;AAAA,OACvB;AACA,MAAA,KAAA,CAAM,OAAA,GAAU,yBAAA,CAA0B,gBAAA,IAAoB,SAAA,EAAW,MAAM,CAAA;AAC/E,MAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAC/C,MAAA;AAAA,IACF;AAAA,IAEA,KAAK,QAAA,EAAU;AAEb,MAAA,MAAM,aAAA,GAAgB,kBAAkB,gBAAgB,CAAA;AAExD,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,IAAI,MAAA,CAAO,qBAAqB,gBAAA,EAAkB;AAChD,UAAA,GAAA,CAAI,WAAA,GAAc;AAAA,YAChB,SAAA,EAAW,gBAAA;AAAA,YACX,SAAS,aAAA,CAAc,GAAA;AAAA,YACvB,QAAQ,eAAA,IAAmB;AAAA,WAC7B;AAAA,QACF;AACA,QAAA,cAAA,CAAe,aAAA,EAAe,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAAA,MAC9C,CAAA,MAAO;AAEL,QAAA,MAAM,KAAA,GAAQ,0BAAA;AAAA,UACZ,gBAAA,IAAoB,SAAA;AAAA,UACpB,iBAAA;AAAA,UACA,OAAO,aAAA,CAAc;AAAA,SACvB;AACA,QAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAAA,MACjD;AACA,MAAA;AAAA,IACF;AAAA,IAEA,KAAK,SAAA,EAAW;AAEd,MAAA,IAAI,OAAO,cAAA,EAAgB;AACzB,QAAA,IAAI,MAAA,CAAO,qBAAqB,gBAAA,EAAkB;AAChD,UAAA,GAAA,CAAI,WAAA,GAAc;AAAA,YAChB,SAAA,EAAW,gBAAA;AAAA,YACX,OAAA,EAAS,SAAA;AAAA,YACT,QAAQ,eAAA,IAAmB;AAAA,WAC7B;AAAA,QACF;AACA,QAAA,qBAAA,CAAsB,MAAA,CAAO,cAAA,EAAgB,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAAA,MAC7D,CAAA,MAAO;AAEL,QAAA,MAAM,aAAA,GAAgB,kBAAkB,gBAAgB,CAAA;AAExD,QAAA,IAAI,aAAA,EAAe;AACjB,UAAA,IAAI,MAAA,CAAO,qBAAqB,gBAAA,EAAkB;AAChD,YAAA,GAAA,CAAI,WAAA,GAAc;AAAA,cAChB,SAAA,EAAW,gBAAA;AAAA,cACX,SAAS,aAAA,CAAc,GAAA;AAAA,cACvB,QAAQ,eAAA,IAAmB;AAAA,aAC7B;AAAA,UACF;AACA,UAAA,cAAA,CAAe,aAAA,EAAe,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAAA,QAC9C,CAAA,MAAO;AACL,UAAA,MAAM,KAAA,GAAQ,0BAAA;AAAA,YACZ,gBAAA,IAAoB,SAAA;AAAA,YACpB,iBAAA;AAAA,YACA,OAAO,aAAA,CAAc;AAAA,WACvB;AACA,UAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAAA,QACjD;AAAA,MACF;AACA,MAAA;AAAA,IACF;AAAA,IAEA,SAAS;AACP,MAAA,MAAM,KAAA,GAAQ,+BAAA;AAAA,QACZ;AAAA,OACF;AACA,MAAA,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAC/C,MAAA;AAAA,IACF;AAAA;AAEJ;AAKA,SAAS,cAAA,CACP,OAAA,EACA,GAAA,EACA,GAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAK,KAAK,IAAI,CAAA;AAG7C,IAAA,IAAI,kBAAkB,OAAA,EAAS;AAC7B,MAAA,MAAA,CAAO,KAAA,CAAM,CAAC,KAAA,KAAmB;AAC/B,QAAA,IAAA,CAAK,KAAK,CAAA;AAAA,MACZ,CAAC,CAAA;AAAA,IACH;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,IAAA,CAAK,KAAK,CAAA;AAAA,EACZ;AACF;AAKA,SAAS,qBAAA,CACP,OAAA,EACA,GAAA,EACA,GAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,EAAK,GAAA,EAAK,IAAI,CAAA;AAErC,IAAA,IAAI,kBAAkB,OAAA,EAAS;AAC7B,MAAA,KAAK,MAAA,CAAO,KAAA,CAAM,CAAC,KAAA,KAAmB;AACpC,QAAA,IAAA,CAAK,KAAK,CAAA;AAAA,MACZ,CAAC,CAAA;AAAA,IACH;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,IAAA,CAAK,KAAK,CAAA;AAAA,EACZ;AACF","file":"index.js","sourcesContent":["import type { Request, Response, NextFunction, RequestHandler } from 'express';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parsed semantic version components.\n */\nexport interface ParsedVersion {\n readonly major: number;\n readonly minor: number;\n readonly patch: number;\n readonly raw: string;\n}\n\n/**\n * Version range type indicating the matching strategy.\n */\nexport type VersionRangeType = 'caret' | 'tilde' | 'exact';\n\n/**\n * Parsed version range with its type and base version.\n */\nexport interface ParsedVersionRange {\n readonly type: VersionRangeType;\n readonly version: ParsedVersion;\n readonly raw: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Handler Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Express request extended with optional version property.\n * This allows middleware to set the version before reaching the versioning middleware.\n */\nexport interface VersionedRequest extends Request {\n /**\n * The API version extracted from the request.\n * Can be set by upstream middleware or extracted automatically.\n */\n version?: string;\n\n /**\n * Metadata about the matched version (set by the middleware).\n */\n versionInfo?: {\n /** The original version string from the request */\n readonly requested: string;\n /** The handler key that matched */\n readonly matched: string;\n /** The source where the version was found */\n readonly source: VersionSource;\n };\n}\n\n/**\n * Type alias for Express request handler with versioned request.\n */\nexport type VersionedRequestHandler = (\n req: VersionedRequest,\n res: Response,\n next: NextFunction\n) => void | Promise<void>;\n\n/**\n * An object that maps semantic version strings to specific request handlers.\n *\n * Each key must be a valid version string:\n * - Exact: `\"1.0.0\"`, `\"2.1.3\"`\n * - Caret (major): `\"^1\"`, `\"^2.0\"`, `\"^3.1.0\"`\n * - Tilde (minor): `\"~1.2\"`, `\"~2.0.0\"`\n *\n * @example\n * ```ts\n * const handlers: VersionHandlers = {\n * \"^1\": (req, res) => res.send(\"v1.x handler\"),\n * \"~2.1\": (req, res) => res.send(\"v2.1.x handler\"),\n * \"3.0.0\": (req, res) => res.send(\"exact v3.0.0 handler\"),\n * };\n * ```\n */\nexport type VersionHandlers = Record<string, RequestHandler | VersionedRequestHandler>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sources from which the version can be extracted.\n */\nexport type VersionSource = 'header' | 'query' | 'path' | 'custom';\n\n/**\n * Configuration for header-based version extraction.\n */\nexport interface HeaderVersionConfig {\n /**\n * The header name to read the version from.\n * @default 'accept-version'\n */\n readonly name?: string;\n}\n\n/**\n * Configuration for query parameter-based version extraction.\n */\nexport interface QueryVersionConfig {\n /**\n * The query parameter name to read the version from.\n * @default 'v'\n */\n readonly name?: string;\n}\n\n/**\n * Configuration for URL path-based version extraction.\n */\nexport interface PathVersionConfig {\n /**\n * Regular expression to extract version from the URL path.\n * Must contain a capture group for the version.\n * @default /\\/v(\\d+(?:\\.\\d+)?(?:\\.\\d+)?)\\//\n * @example /\\/api\\/v(\\d+)\\// matches /api/v1/users\n */\n readonly pattern?: RegExp;\n}\n\n/**\n * Custom version extractor function.\n * Return the version string or undefined if not found.\n */\nexport type CustomVersionExtractor = (req: VersionedRequest) => string | undefined;\n\n/**\n * Configuration for version extraction.\n */\nexport interface VersionExtractionConfig {\n /**\n * Order of sources to try when extracting the version.\n * The first source that returns a valid version wins.\n * @default ['header', 'query']\n */\n readonly sources?: readonly VersionSource[];\n\n /**\n * Header configuration.\n */\n readonly header?: HeaderVersionConfig;\n\n /**\n * Query parameter configuration.\n */\n readonly query?: QueryVersionConfig;\n\n /**\n * URL path configuration.\n */\n readonly path?: PathVersionConfig;\n\n /**\n * Custom version extractor.\n * Only used if 'custom' is included in sources.\n */\n readonly custom?: CustomVersionExtractor;\n}\n\n/**\n * Configuration for error responses.\n */\nexport interface ErrorResponseConfig {\n /**\n * HTTP status code for missing version errors.\n * @default 422\n */\n readonly missingVersionStatus?: number;\n\n /**\n * HTTP status code for version not found errors.\n * @default 422\n */\n readonly versionNotFoundStatus?: number;\n\n /**\n * Custom message for missing version errors.\n * @default 'API version is required'\n */\n readonly missingVersionMessage?: string;\n\n /**\n * Custom message for version not found errors.\n * Can be a string or a function that receives the requested version.\n * @default 'No handler found for the requested API version'\n */\n readonly versionNotFoundMessage?: string | ((version: string) => string);\n\n /**\n * Whether to include the requested version in error responses.\n * @default true\n */\n readonly includeRequestedVersion?: boolean;\n\n /**\n * Custom error handler. If provided, overrides default error handling.\n * Return true to indicate the error was handled, false to use default handling.\n */\n readonly onError?: (\n error: VersioningError,\n req: VersionedRequest,\n res: Response,\n next: NextFunction\n ) => boolean | undefined;\n}\n\n/**\n * Fallback strategy when no version matches.\n */\nexport type FallbackStrategy = 'latest' | 'none' | 'default';\n\n/**\n * Main configuration options for the versioning middleware.\n */\nexport interface VersioningOptions {\n /**\n * Version extraction configuration.\n */\n readonly extraction?: VersionExtractionConfig;\n\n /**\n * Error response configuration.\n */\n readonly errorResponse?: ErrorResponseConfig;\n\n /**\n * Fallback strategy when no handler matches the requested version.\n * - 'latest': Use the handler for the highest available version\n * - 'none': Return an error (no fallback)\n * - 'default': Use the default handler if provided, otherwise 'latest'\n * @default 'default'\n */\n readonly fallbackStrategy?: FallbackStrategy;\n\n /**\n * Default handler to use when no version matches and fallbackStrategy allows it.\n */\n readonly defaultHandler?: RequestHandler | VersionedRequestHandler;\n\n /**\n * Whether to validate handler keys on initialization.\n * If true, throws an error for invalid version strings.\n * @default true\n */\n readonly validateHandlers?: boolean;\n\n /**\n * Whether to attach version info to the request object.\n * @default true\n */\n readonly attachVersionInfo?: boolean;\n\n /**\n * Whether to require a version in every request.\n * If false, requests without a version will use the fallback strategy.\n * @default true\n */\n readonly requireVersion?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Error codes for versioning errors.\n */\nexport type VersioningErrorCode =\n | 'MISSING_VERSION'\n | 'VERSION_NOT_FOUND'\n | 'INVALID_VERSION_FORMAT'\n | 'INVALID_HANDLER'\n | 'INVALID_CONFIGURATION';\n\n/**\n * Base interface for versioning errors.\n */\nexport interface VersioningErrorDetails {\n readonly code: VersioningErrorCode;\n readonly message: string;\n readonly requestedVersion?: string;\n readonly availableVersions?: readonly string[];\n}\n\n/**\n * Custom error class for versioning-related errors.\n */\nexport class VersioningError extends Error {\n public readonly code: VersioningErrorCode;\n public readonly requestedVersion: string | undefined;\n public readonly availableVersions: readonly string[] | undefined;\n public readonly statusCode: number;\n\n constructor(details: VersioningErrorDetails, statusCode = 422) {\n super(details.message);\n this.name = 'VersioningError';\n this.code = details.code;\n this.requestedVersion = details.requestedVersion;\n this.availableVersions = details.availableVersions;\n this.statusCode = statusCode;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n Error.captureStackTrace(this, VersioningError);\n }\n\n /**\n * Converts the error to a JSON-serializable object.\n */\n toJSON(): VersioningErrorDetails & { statusCode: number } {\n return {\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n ...(this.requestedVersion !== undefined && { requestedVersion: this.requestedVersion }),\n ...(this.availableVersions !== undefined && { availableVersions: this.availableVersions }),\n };\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal Types (for compiled handlers)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Pre-compiled handler with parsed version range for fast matching.\n * @internal\n */\nexport interface CompiledHandler {\n readonly key: string;\n readonly range: ParsedVersionRange;\n readonly handler: RequestHandler | VersionedRequestHandler;\n readonly priority: number;\n}\n\n/**\n * Result of version extraction.\n * @internal\n */\nexport interface ExtractionResult {\n readonly version: string;\n readonly source: VersionSource;\n}\n\n/**\n * Resolved configuration with all defaults applied.\n * @internal\n */\nexport interface ResolvedConfig {\n readonly extraction: Omit<Required<VersionExtractionConfig>, 'custom'> & {\n readonly header: Required<HeaderVersionConfig>;\n readonly query: Required<QueryVersionConfig>;\n readonly path: Required<PathVersionConfig>;\n readonly custom: CustomVersionExtractor | undefined;\n };\n readonly errorResponse: Required<Omit<ErrorResponseConfig, 'onError'>> & {\n readonly onError: ErrorResponseConfig['onError'] | undefined;\n };\n readonly fallbackStrategy: FallbackStrategy;\n readonly defaultHandler: RequestHandler | VersionedRequestHandler | undefined;\n readonly validateHandlers: boolean;\n readonly attachVersionInfo: boolean;\n readonly requireVersion: boolean;\n}\n","import { VersioningError, type VersioningErrorCode } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error Factory Functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a VersioningError for missing version in request.\n */\nexport function createMissingVersionError(\n statusCode = 422,\n message = 'API version is required'\n): VersioningError {\n return new VersioningError(\n {\n code: 'MISSING_VERSION',\n message,\n },\n statusCode\n );\n}\n\n/**\n * Creates a VersioningError for version not found.\n */\nexport function createVersionNotFoundError(\n requestedVersion: string,\n availableVersions: readonly string[],\n statusCode = 422\n): VersioningError {\n return new VersioningError(\n {\n code: 'VERSION_NOT_FOUND',\n message: `No handler found for API version '${requestedVersion}'`,\n requestedVersion,\n availableVersions,\n },\n statusCode\n );\n}\n\n/**\n * Creates a VersioningError for invalid version format.\n */\nexport function createInvalidVersionFormatError(\n version: string,\n statusCode = 400\n): VersioningError {\n return new VersioningError(\n {\n code: 'INVALID_VERSION_FORMAT',\n message: `Invalid version format: '${version}'`,\n requestedVersion: version,\n },\n statusCode\n );\n}\n\n/**\n * Creates a VersioningError for invalid handler configuration.\n */\nexport function createInvalidHandlerError(key: string, reason: string): VersioningError {\n return new VersioningError(\n {\n code: 'INVALID_HANDLER',\n message: `Invalid handler for version '${key}': ${reason}`,\n },\n 500\n );\n}\n\n/**\n * Creates a VersioningError for invalid configuration.\n */\nexport function createInvalidConfigurationError(reason: string): VersioningError {\n return new VersioningError(\n {\n code: 'INVALID_CONFIGURATION',\n message: `Invalid configuration: ${reason}`,\n },\n 500\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error Type Guards\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Type guard to check if an error is a VersioningError.\n */\nexport function isVersioningError(error: unknown): error is VersioningError {\n return error instanceof VersioningError;\n}\n\n/**\n * Type guard to check if an error has a specific error code.\n */\nexport function hasErrorCode(error: unknown, code: VersioningErrorCode): error is VersioningError {\n return isVersioningError(error) && error.code === code;\n}\n","import type { VersioningOptions, ResolvedConfig } from './types.js';\nimport { createInvalidConfigurationError } from './errors.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Default Configuration\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n extraction: {\n sources: ['header', 'query'],\n header: {\n name: 'accept-version',\n },\n query: {\n name: 'v',\n },\n path: {\n pattern: /\\/v(\\d+(?:\\.\\d+)?(?:\\.\\d+)?)\\//,\n },\n custom: undefined,\n },\n errorResponse: {\n missingVersionStatus: 422,\n versionNotFoundStatus: 422,\n missingVersionMessage: 'API version is required',\n versionNotFoundMessage: 'No handler found for the requested API version',\n includeRequestedVersion: true,\n onError: undefined,\n },\n fallbackStrategy: 'default',\n defaultHandler: undefined,\n validateHandlers: true,\n attachVersionInfo: true,\n requireVersion: true,\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Configuration Resolution\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolves user options with default values.\n *\n * @param options - User-provided options\n * @returns Fully resolved configuration\n * @throws VersioningError if configuration is invalid\n */\nexport function resolveConfig(options?: VersioningOptions): ResolvedConfig {\n if (!options) {\n return DEFAULT_CONFIG;\n }\n\n // Validate fallback strategy if provided\n if (options.fallbackStrategy !== undefined) {\n const validStrategies = new Set(['latest', 'none', 'default']);\n if (!validStrategies.has(options.fallbackStrategy)) {\n throw createInvalidConfigurationError(\n `Invalid fallback strategy: '${options.fallbackStrategy}'. Valid strategies: latest, none, default`\n );\n }\n }\n\n // Validate sources if provided\n if (options.extraction?.sources) {\n const validSources = new Set(['header', 'query', 'path', 'custom']);\n for (const source of options.extraction.sources) {\n if (!validSources.has(source)) {\n throw createInvalidConfigurationError(\n `Invalid version source: '${source}'. Valid sources: header, query, path, custom`\n );\n }\n }\n\n // Validate custom extractor is provided when 'custom' source is used\n if (options.extraction.sources.includes('custom') && !options.extraction.custom) {\n throw createInvalidConfigurationError(\n \"Custom extractor function is required when 'custom' source is used\"\n );\n }\n }\n\n // Validate path pattern if provided\n if (options.extraction?.path?.pattern && !(options.extraction.path.pattern instanceof RegExp)) {\n throw createInvalidConfigurationError('Path pattern must be a RegExp instance');\n }\n\n // Validate status codes if provided\n if (options.errorResponse?.missingVersionStatus !== undefined) {\n const status = options.errorResponse.missingVersionStatus;\n if (!Number.isInteger(status) || status < 100 || status > 599) {\n throw createInvalidConfigurationError(\n 'missingVersionStatus must be a valid HTTP status code (100-599)'\n );\n }\n }\n\n if (options.errorResponse?.versionNotFoundStatus !== undefined) {\n const status = options.errorResponse.versionNotFoundStatus;\n if (!Number.isInteger(status) || status < 100 || status > 599) {\n throw createInvalidConfigurationError(\n 'versionNotFoundStatus must be a valid HTTP status code (100-599)'\n );\n }\n }\n\n // Merge configurations\n return {\n extraction: {\n sources: options.extraction?.sources ?? DEFAULT_CONFIG.extraction.sources,\n header: {\n name: options.extraction?.header?.name ?? DEFAULT_CONFIG.extraction.header.name,\n },\n query: {\n name: options.extraction?.query?.name ?? DEFAULT_CONFIG.extraction.query.name,\n },\n path: {\n pattern: options.extraction?.path?.pattern ?? DEFAULT_CONFIG.extraction.path.pattern,\n },\n custom: options.extraction?.custom,\n },\n errorResponse: {\n missingVersionStatus:\n options.errorResponse?.missingVersionStatus ??\n DEFAULT_CONFIG.errorResponse.missingVersionStatus,\n versionNotFoundStatus:\n options.errorResponse?.versionNotFoundStatus ??\n DEFAULT_CONFIG.errorResponse.versionNotFoundStatus,\n missingVersionMessage:\n options.errorResponse?.missingVersionMessage ??\n DEFAULT_CONFIG.errorResponse.missingVersionMessage,\n versionNotFoundMessage:\n options.errorResponse?.versionNotFoundMessage ??\n DEFAULT_CONFIG.errorResponse.versionNotFoundMessage,\n includeRequestedVersion:\n options.errorResponse?.includeRequestedVersion ??\n DEFAULT_CONFIG.errorResponse.includeRequestedVersion,\n onError: options.errorResponse?.onError,\n },\n fallbackStrategy: options.fallbackStrategy ?? DEFAULT_CONFIG.fallbackStrategy,\n defaultHandler: options.defaultHandler ?? DEFAULT_CONFIG.defaultHandler,\n validateHandlers: options.validateHandlers ?? DEFAULT_CONFIG.validateHandlers,\n attachVersionInfo: options.attachVersionInfo ?? DEFAULT_CONFIG.attachVersionInfo,\n requireVersion: options.requireVersion ?? DEFAULT_CONFIG.requireVersion,\n };\n}\n\n/**\n * Creates a frozen copy of the configuration to prevent mutations.\n *\n * @param config - The resolved configuration\n * @returns A frozen configuration object\n */\nexport function freezeConfig(config: ResolvedConfig): Readonly<ResolvedConfig> {\n return Object.freeze({\n ...config,\n extraction: Object.freeze({\n ...config.extraction,\n sources: Object.freeze([...config.extraction.sources]),\n header: Object.freeze({ ...config.extraction.header }),\n query: Object.freeze({ ...config.extraction.query }),\n path: Object.freeze({ ...config.extraction.path }),\n }),\n errorResponse: Object.freeze({ ...config.errorResponse }),\n });\n}\n","import type { VersionedRequest, ResolvedConfig, ExtractionResult } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Extractors\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extracts version from the request header.\n *\n * @param req - The Express request object\n * @param headerName - The header name to read from\n * @returns The version string or undefined\n */\nfunction extractFromHeader(req: VersionedRequest, headerName: string): string | undefined {\n const value = req.headers[headerName.toLowerCase()];\n\n if (typeof value === 'string' && value.trim().length > 0) {\n return value.trim();\n }\n\n // Handle array of headers (take first)\n if (Array.isArray(value) && value.length > 0 && value[0]) {\n return value[0].trim();\n }\n\n return undefined;\n}\n\n/**\n * Extracts version from query parameters.\n *\n * @param req - The Express request object\n * @param paramName - The query parameter name to read from\n * @returns The version string or undefined\n */\nfunction extractFromQuery(req: VersionedRequest, paramName: string): string | undefined {\n const value = req.query[paramName];\n\n if (typeof value === 'string' && value.trim().length > 0) {\n return value.trim();\n }\n\n return undefined;\n}\n\n/**\n * Extracts version from the URL path.\n *\n * @param req - The Express request object\n * @param pattern - The regex pattern to match\n * @returns The version string or undefined\n */\nfunction extractFromPath(req: VersionedRequest, pattern: RegExp): string | undefined {\n const path = req.path || req.url;\n const isStatefulPattern = pattern.global || pattern.sticky;\n\n if (isStatefulPattern) {\n pattern.lastIndex = 0;\n }\n\n const match = pattern.exec(path);\n\n if (isStatefulPattern) {\n pattern.lastIndex = 0;\n }\n\n if (match?.[1]) {\n return match[1].trim();\n }\n\n return undefined;\n}\n\n/**\n * Extracts version from the request object (set by upstream middleware).\n *\n * @param req - The Express request object\n * @returns The version string or undefined\n */\nfunction extractFromRequest(req: VersionedRequest): string | undefined {\n if (typeof req.version === 'string' && req.version.trim().length > 0) {\n return req.version.trim();\n }\n\n return undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main Extractor\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a version extractor function based on configuration.\n *\n * @param config - The resolved configuration\n * @returns A function that extracts version from a request\n */\nexport function createVersionExtractor(\n config: ResolvedConfig\n): (req: VersionedRequest) => ExtractionResult | undefined {\n const { sources, header, query, path, custom } = config.extraction;\n\n return (req: VersionedRequest): ExtractionResult | undefined => {\n // First, always check if version is already set on the request\n const requestVersion = extractFromRequest(req);\n if (requestVersion) {\n return { version: requestVersion, source: 'custom' };\n }\n\n // Then try each configured source in order\n for (const source of sources) {\n let version: string | undefined;\n\n switch (source) {\n case 'header':\n version = extractFromHeader(req, header.name);\n break;\n\n case 'query':\n version = extractFromQuery(req, query.name);\n break;\n\n case 'path':\n version = extractFromPath(req, path.pattern);\n break;\n\n case 'custom':\n version = custom?.(req);\n break;\n }\n\n if (version) {\n return { version, source };\n }\n }\n\n return undefined;\n };\n}\n\n/**\n * Default version extractor that checks header and query.\n * Used when no custom configuration is provided.\n *\n * @param req - The Express request object\n * @returns Extraction result or undefined\n */\nexport function defaultVersionExtractor(req: VersionedRequest): ExtractionResult | undefined {\n // Check req.version first (set by upstream middleware)\n const requestVersion = extractFromRequest(req);\n if (requestVersion) {\n return { version: requestVersion, source: 'custom' };\n }\n\n // Check Accept-Version header\n const headerVersion = extractFromHeader(req, 'accept-version');\n if (headerVersion) {\n return { version: headerVersion, source: 'header' };\n }\n\n // Check query parameter\n const queryVersion = extractFromQuery(req, 'v');\n if (queryVersion) {\n return { version: queryVersion, source: 'query' };\n }\n\n return undefined;\n}\n","import type { ParsedVersion, ParsedVersionRange, VersionRangeType } from './types.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Regular expression to validate and parse semantic version strings.\n * Supports: 1, 1.0, 1.0.0, with optional ^ or ~ prefix\n */\nconst VERSION_REGEX = /^(\\^|~)?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?$/;\n\n/**\n * Gets how many version components were explicitly provided (1, 2, or 3).\n */\nfunction getSpecifiedComponentCount(rawRange: string): 1 | 2 | 3 {\n const withoutPrefix = rawRange.replace(/^[\\^~]/, '');\n const count = withoutPrefix.split('.').length;\n\n if (count <= 1) {\n return 1;\n }\n\n if (count === 2) {\n return 2;\n }\n\n return 3;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Parsing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parses a version string into its components.\n *\n * @param version - The version string to parse (e.g., \"1.2.3\", \"1.2\", \"1\")\n * @returns The parsed version or null if invalid\n *\n * @example\n * ```ts\n * parseVersion(\"1.2.3\") // { major: 1, minor: 2, patch: 3, raw: \"1.2.3\" }\n * parseVersion(\"1.2\") // { major: 1, minor: 2, patch: 0, raw: \"1.2\" }\n * parseVersion(\"1\") // { major: 1, minor: 0, patch: 0, raw: \"1\" }\n * parseVersion(\"abc\") // null\n * ```\n */\nexport function parseVersion(version: string): ParsedVersion | null {\n const trimmed = version.trim();\n const match = VERSION_REGEX.exec(trimmed);\n\n if (!match) {\n return null;\n }\n\n const [, , majorStr, minorStr, patchStr] = match;\n\n const major = parseInt(majorStr ?? '0', 10);\n const minor = parseInt(minorStr ?? '0', 10);\n const patch = parseInt(patchStr ?? '0', 10);\n\n // Validate parsed numbers\n if (isNaN(major) || isNaN(minor) || isNaN(patch)) {\n return null;\n }\n\n // Validate non-negative\n if (major < 0 || minor < 0 || patch < 0) {\n return null;\n }\n\n return {\n major,\n minor,\n patch,\n raw: trimmed,\n };\n}\n\n/**\n * Parses a version range string (with optional ^ or ~ prefix).\n *\n * @param range - The version range string to parse\n * @returns The parsed version range or null if invalid\n *\n * @example\n * ```ts\n * parseVersionRange(\"^1.2.3\") // { type: 'caret', version: {...}, raw: \"^1.2.3\" }\n * parseVersionRange(\"~1.2\") // { type: 'tilde', version: {...}, raw: \"~1.2\" }\n * parseVersionRange(\"1.0.0\") // { type: 'exact', version: {...}, raw: \"1.0.0\" }\n * ```\n */\nexport function parseVersionRange(range: string): ParsedVersionRange | null {\n const trimmed = range.trim();\n const match = VERSION_REGEX.exec(trimmed);\n\n if (!match) {\n return null;\n }\n\n const [, prefix, majorStr, minorStr, patchStr] = match;\n\n const major = parseInt(majorStr ?? '0', 10);\n const minor = parseInt(minorStr ?? '0', 10);\n const patch = parseInt(patchStr ?? '0', 10);\n\n if (isNaN(major) || isNaN(minor) || isNaN(patch)) {\n return null;\n }\n\n let type: VersionRangeType;\n if (prefix === '^') {\n type = 'caret';\n } else if (prefix === '~') {\n type = 'tilde';\n } else {\n type = 'exact';\n }\n\n return {\n type,\n version: { major, minor, patch, raw: trimmed.replace(/^[\\^~]/, '') },\n raw: trimmed,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Matching\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Checks if a client version satisfies a version range.\n *\n * Matching rules:\n * - Caret (^): Matches if major version is equal and client version >= range version\n * - ^1.2.3 matches 1.2.3, 1.2.4, 1.3.0, but not 2.0.0 or 1.2.2\n * - Tilde (~): Matches if major.minor is equal and client version >= range version\n * - ~1.2.3 matches 1.2.3, 1.2.4, but not 1.3.0 or 1.2.2\n * - Exact: Matches only if versions are exactly equal\n * - 1.2.3 matches only 1.2.3\n *\n * @param clientVersion - The parsed client version\n * @param range - The parsed version range to match against\n * @returns true if the client version satisfies the range\n *\n * @example\n * ```ts\n * const client = parseVersion(\"1.5.0\");\n * const range = parseVersionRange(\"^1.2.0\");\n * versionSatisfies(client, range); // true\n * ```\n */\nexport function versionSatisfies(clientVersion: ParsedVersion, range: ParsedVersionRange): boolean {\n const { major: cMajor, minor: cMinor, patch: cPatch } = clientVersion;\n const { major: rMajor, minor: rMinor, patch: rPatch } = range.version;\n\n switch (range.type) {\n case 'caret': {\n // ^1.2.3 matches >=1.2.3 <2.0.0\n if (cMajor !== rMajor) {\n return false;\n }\n\n // For major >= 1, caret allows all compatible minors/patches.\n if (rMajor > 0) {\n if (cMinor < rMinor) {\n return false;\n }\n\n if (cMinor === rMinor && cPatch < rPatch) {\n return false;\n }\n\n return true;\n }\n\n // For major 0, compatibility is stricter per semver.\n // ^0.2.3 := >=0.2.3 <0.3.0\n if (rMinor > 0) {\n return cMinor === rMinor && cPatch >= rPatch;\n }\n\n // ^0 := >=0.0.0 <1.0.0\n // ^0.0 := >=0.0.0 <0.1.0\n // ^0.0.3 := >=0.0.3 <0.0.4\n const componentCount = getSpecifiedComponentCount(range.raw);\n\n if (componentCount === 1) {\n return true;\n }\n\n if (componentCount === 2) {\n return cMinor === 0;\n }\n\n return cMinor === 0 && cPatch === rPatch;\n }\n\n case 'tilde': {\n // ~1.2.3 matches 1.2.x where x >= 3\n if (cMajor !== rMajor || cMinor !== rMinor) {\n return false;\n }\n // Client patch must be >= range patch\n return cPatch >= rPatch;\n }\n\n case 'exact': {\n // Must match exactly\n return cMajor === rMajor && cMinor === rMinor && cPatch === rPatch;\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version Comparison\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Compares two parsed versions.\n *\n * @param a - First version\n * @param b - Second version\n * @returns -1 if a < b, 0 if a === b, 1 if a > b\n */\nexport function compareVersions(a: ParsedVersion, b: ParsedVersion): -1 | 0 | 1 {\n if (a.major !== b.major) {\n return a.major > b.major ? 1 : -1;\n }\n if (a.minor !== b.minor) {\n return a.minor > b.minor ? 1 : -1;\n }\n if (a.patch !== b.patch) {\n return a.patch > b.patch ? 1 : -1;\n }\n return 0;\n}\n\n/**\n * Finds the latest version from a list of version strings.\n *\n * @param versions - Array of version strings (may include ^ or ~ prefixes)\n * @returns The latest version string (without prefix) or null if array is empty\n *\n * @example\n * ```ts\n * findLatestVersion([\"^1.0.0\", \"~2.1.0\", \"3.0.0\"]) // \"3.0.0\"\n * findLatestVersion([\"1.0.0\", \"1.2.0\", \"1.1.0\"]) // \"1.2.0\"\n * findLatestVersion([]) // null\n * ```\n */\nexport function findLatestVersion(versions: readonly string[]): string | null {\n if (versions.length === 0) {\n return null;\n }\n\n let latest: ParsedVersion | null = null;\n let latestRaw = '';\n\n for (const version of versions) {\n const range = parseVersionRange(version);\n if (!range) {\n continue;\n }\n\n if (!latest || compareVersions(range.version, latest) > 0) {\n latest = range.version;\n latestRaw = range.version.raw;\n }\n }\n\n return latestRaw || null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Validation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Validates if a string is a valid version string.\n *\n * @param version - The version string to validate\n * @returns true if valid, false otherwise\n */\nexport function isValidVersion(version: string): boolean {\n return parseVersion(version) !== null;\n}\n\n/**\n * Validates if a string is a valid version range.\n *\n * @param range - The version range string to validate\n * @returns true if valid, false otherwise\n */\nexport function isValidVersionRange(range: string): boolean {\n return parseVersionRange(range) !== null;\n}\n\n/**\n * Normalizes a version string to full semver format.\n *\n * @param version - The version string to normalize\n * @returns The normalized version string or null if invalid\n *\n * @example\n * ```ts\n * normalizeVersion(\"1\") // \"1.0.0\"\n * normalizeVersion(\"1.2\") // \"1.2.0\"\n * normalizeVersion(\"1.2.3\") // \"1.2.3\"\n * normalizeVersion(\"^1.2\") // \"1.2.0\" (prefix removed)\n * ```\n */\nexport function normalizeVersion(version: string): string | null {\n const range = parseVersionRange(version);\n if (!range) {\n return null;\n }\n\n const { major, minor, patch } = range.version;\n return `${major.toString()}.${minor.toString()}.${patch.toString()}`;\n}\n","import type {\n VersionHandlers,\n CompiledHandler,\n ParsedVersion,\n VersionedRequestHandler,\n} from './types.js';\nimport type { RequestHandler } from 'express';\nimport { parseVersionRange, versionSatisfies, compareVersions } from './version-parser.js';\nimport { createInvalidHandlerError } from './errors.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Priority Calculation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Calculates the priority of a version range for matching.\n * Higher priority = more specific = matched first.\n *\n * Priority rules:\n * 1. Exact versions have highest priority (1000 + version value)\n * 2. Tilde ranges have medium priority (500 + version value)\n * 3. Caret ranges have lowest priority (version value only)\n *\n * Within each category, higher versions have higher priority.\n */\nfunction calculatePriority(handler: CompiledHandler): number {\n const { range } = handler;\n const { major, minor, patch } = range.version;\n\n // Base version value (supports versions up to 999.999.999)\n const versionValue = major * 1_000_000 + minor * 1_000 + patch;\n\n switch (range.type) {\n case 'exact':\n return 1_000_000_000 + versionValue;\n case 'tilde':\n return 500_000_000 + versionValue;\n case 'caret':\n return versionValue;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Handler Compilation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Compiles version handlers into an optimized format for fast matching.\n *\n * This function:\n * 1. Parses all version ranges\n * 2. Validates handlers\n * 3. Calculates priorities\n * 4. Sorts by priority (most specific first)\n *\n * @param handlers - The version handlers object\n * @param validate - Whether to validate handlers\n * @returns Array of compiled handlers sorted by priority\n * @throws VersioningError if validation is enabled and a handler is invalid\n */\nexport function compileHandlers(handlers: VersionHandlers, validate = true): CompiledHandler[] {\n const compiled: CompiledHandler[] = [];\n\n for (const [key, handler] of Object.entries(handlers)) {\n // Validate handler is a function\n if (validate && typeof handler !== 'function') {\n throw createInvalidHandlerError(key, 'handler must be a function');\n }\n\n // Parse the version range\n const range = parseVersionRange(key);\n\n if (!range) {\n if (validate) {\n throw createInvalidHandlerError(\n key,\n 'invalid version format. Expected: \"1.0.0\", \"^1.0\", \"~1.2\", etc.'\n );\n }\n continue;\n }\n\n const compiledHandler: CompiledHandler = {\n key,\n range,\n handler: handler as RequestHandler | VersionedRequestHandler,\n priority: 0, // Will be calculated after\n };\n\n // Calculate priority\n (compiledHandler as { priority: number }).priority = calculatePriority(compiledHandler);\n\n compiled.push(compiledHandler);\n }\n\n // Sort by priority (highest first)\n compiled.sort((a, b) => b.priority - a.priority);\n\n return compiled;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Handler Matching\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Finds the best matching handler for a given client version.\n *\n * The matching algorithm:\n * 1. Iterates through handlers in priority order (most specific first)\n * 2. Returns the first handler that matches the client version\n * 3. Returns undefined if no handler matches\n *\n * @param clientVersion - The parsed client version\n * @param compiledHandlers - Array of compiled handlers\n * @returns The matching compiled handler or undefined\n */\nexport function findMatchingHandler(\n clientVersion: ParsedVersion,\n compiledHandlers: readonly CompiledHandler[]\n): CompiledHandler | undefined {\n for (const handler of compiledHandlers) {\n if (versionSatisfies(clientVersion, handler.range)) {\n return handler;\n }\n }\n\n return undefined;\n}\n\n/**\n * Finds the handler for the latest available version.\n *\n * @param compiledHandlers - Array of compiled handlers\n * @returns The handler for the latest version or undefined\n */\nexport function findLatestHandler(\n compiledHandlers: readonly CompiledHandler[]\n): CompiledHandler | undefined {\n if (compiledHandlers.length === 0) {\n return undefined;\n }\n\n let latest: CompiledHandler | undefined;\n\n for (const handler of compiledHandlers) {\n if (!latest || compareVersions(handler.range.version, latest.range.version) > 0) {\n latest = handler;\n }\n }\n\n return latest;\n}\n\n/**\n * Gets all available version keys from compiled handlers.\n *\n * @param compiledHandlers - Array of compiled handlers\n * @returns Array of version keys\n */\nexport function getAvailableVersions(compiledHandlers: readonly CompiledHandler[]): string[] {\n return compiledHandlers.map((h) => h.key);\n}\n","import type { RequestHandler, Response, NextFunction } from 'express';\nimport type {\n VersionHandlers,\n VersioningOptions,\n VersionedRequest,\n CompiledHandler,\n ResolvedConfig,\n VersionSource,\n} from './types.js';\nimport type { VersioningError } from './types.js';\nimport { resolveConfig, freezeConfig } from './config.js';\nimport { createVersionExtractor } from './version-extractor.js';\nimport { parseVersion } from './version-parser.js';\nimport {\n compileHandlers,\n findMatchingHandler,\n findLatestHandler,\n getAvailableVersions,\n} from './handler-compiler.js';\nimport {\n createMissingVersionError,\n createVersionNotFoundError,\n createInvalidVersionFormatError,\n createInvalidConfigurationError,\n} from './errors.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error Response Helper\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Sends an error response to the client.\n */\nfunction sendErrorResponse(\n error: VersioningError,\n req: VersionedRequest,\n res: Response,\n next: NextFunction,\n config: ResolvedConfig\n): void {\n // Check for custom error handler\n if (config.errorResponse.onError) {\n const handled = config.errorResponse.onError(error, req, res, next);\n if (handled === true) {\n return;\n }\n }\n\n // Default error response\n const response: Record<string, unknown> = {\n error: error.code,\n message: error.message,\n };\n\n if (config.errorResponse.includeRequestedVersion && error.requestedVersion) {\n response['requestedVersion'] = error.requestedVersion;\n }\n\n if (error.availableVersions && error.availableVersions.length > 0) {\n response['availableVersions'] = error.availableVersions;\n }\n\n res.status(error.statusCode).json(response);\n}\n\n/**\n * Gets the version not found message based on configuration.\n */\nfunction getVersionNotFoundMessage(version: string, config: ResolvedConfig): string {\n const message = config.errorResponse.versionNotFoundMessage;\n\n if (typeof message === 'function') {\n return message(version);\n }\n\n return message;\n}\n\n/**\n * Gets the missing version message based on configuration.\n */\nfunction getMissingVersionMessage(config: ResolvedConfig): string {\n return config.errorResponse.missingVersionMessage;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Middleware Factory\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Creates a versioning middleware for Express.\n *\n * This middleware inspects incoming requests for a semantic version and routes\n * them to the appropriate handler based on version matching rules.\n *\n * @param handlers - Object mapping version strings to request handlers\n * @param options - Configuration options for the middleware\n * @returns Express middleware function\n *\n * @example\n * ```ts\n * // Basic usage\n * app.use('/api', versioningMiddleware({\n * '^1': v1Handler,\n * '^2': v2Handler,\n * '3.0.0': v3Handler,\n * }));\n *\n * // With options\n * app.use('/api', versioningMiddleware(\n * {\n * '^1': v1Handler,\n * '^2': v2Handler,\n * },\n * {\n * extraction: {\n * sources: ['header', 'query', 'path'],\n * header: { name: 'x-api-version' },\n * },\n * fallbackStrategy: 'latest',\n * }\n * ));\n * ```\n */\nexport function versioningMiddleware(\n handlers: VersionHandlers,\n options?: VersioningOptions\n): RequestHandler {\n // Validate handlers argument\n if (Array.isArray(handlers)) {\n throw createInvalidConfigurationError(\"'handlers' must be a non-null, non-array object\");\n }\n\n // Resolve and freeze configuration\n const config = freezeConfig(resolveConfig(options));\n\n // Compile handlers (validates and sorts by priority)\n const compiledHandlers = compileHandlers(handlers, config.validateHandlers);\n\n // Check if we have any valid handlers\n if (compiledHandlers.length === 0) {\n throw createInvalidConfigurationError(\n 'No valid handlers provided. At least one handler is required.'\n );\n }\n\n // Create version extractor\n const extractVersion = createVersionExtractor(config);\n\n // Get available versions for error messages\n const availableVersions = getAvailableVersions(compiledHandlers);\n\n // Return the middleware function\n return (req: VersionedRequest, res: Response, next: NextFunction): void => {\n // Extract version from request\n const extraction = extractVersion(req);\n\n // Handle missing version\n if (!extraction) {\n if (!config.requireVersion) {\n // Use fallback strategy\n handleFallback(\n undefined,\n undefined,\n compiledHandlers,\n config,\n availableVersions,\n req,\n res,\n next\n );\n return;\n }\n\n const error = createMissingVersionError(\n config.errorResponse.missingVersionStatus,\n getMissingVersionMessage(config)\n );\n sendErrorResponse(error, req, res, next, config);\n return;\n }\n\n const { version: versionString, source } = extraction;\n\n // Parse the client version\n const clientVersion = parseVersion(versionString);\n\n if (!clientVersion) {\n const error = createInvalidVersionFormatError(versionString);\n sendErrorResponse(error, req, res, next, config);\n return;\n }\n\n // Find matching handler\n const matchedHandler = findMatchingHandler(clientVersion, compiledHandlers);\n\n if (matchedHandler) {\n // Attach version info if configured\n if (config.attachVersionInfo) {\n req.versionInfo = {\n requested: versionString,\n matched: matchedHandler.key,\n source,\n };\n }\n\n // Execute the handler\n executeHandler(matchedHandler, req, res, next);\n return;\n }\n\n // No match found, use fallback strategy\n handleFallback(\n versionString,\n source,\n compiledHandlers,\n config,\n availableVersions,\n req,\n res,\n next\n );\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helper Functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Handles the fallback strategy when no version matches.\n */\nfunction handleFallback(\n requestedVersion: string | undefined,\n requestedSource: VersionSource | undefined,\n compiledHandlers: readonly CompiledHandler[],\n config: ResolvedConfig,\n availableVersions: readonly string[],\n req: VersionedRequest,\n res: Response,\n next: NextFunction\n): void {\n switch (config.fallbackStrategy) {\n case 'none': {\n // Return error, no fallback\n const error = createVersionNotFoundError(\n requestedVersion ?? 'unknown',\n availableVersions,\n config.errorResponse.versionNotFoundStatus\n );\n error.message = getVersionNotFoundMessage(requestedVersion ?? 'unknown', config);\n sendErrorResponse(error, req, res, next, config);\n break;\n }\n\n case 'latest': {\n // Use latest version handler\n const latestHandler = findLatestHandler(compiledHandlers);\n\n if (latestHandler) {\n if (config.attachVersionInfo && requestedVersion) {\n req.versionInfo = {\n requested: requestedVersion,\n matched: latestHandler.key,\n source: requestedSource ?? 'custom',\n };\n }\n executeHandler(latestHandler, req, res, next);\n } else {\n // This shouldn't happen if we validated handlers\n const error = createVersionNotFoundError(\n requestedVersion ?? 'unknown',\n availableVersions,\n config.errorResponse.versionNotFoundStatus\n );\n sendErrorResponse(error, req, res, next, config);\n }\n break;\n }\n\n case 'default': {\n // Use default handler if provided, otherwise fall back to latest\n if (config.defaultHandler) {\n if (config.attachVersionInfo && requestedVersion) {\n req.versionInfo = {\n requested: requestedVersion,\n matched: 'default',\n source: requestedSource ?? 'custom',\n };\n }\n executeRequestHandler(config.defaultHandler, req, res, next);\n } else {\n // Fall back to latest\n const latestHandler = findLatestHandler(compiledHandlers);\n\n if (latestHandler) {\n if (config.attachVersionInfo && requestedVersion) {\n req.versionInfo = {\n requested: requestedVersion,\n matched: latestHandler.key,\n source: requestedSource ?? 'custom',\n };\n }\n executeHandler(latestHandler, req, res, next);\n } else {\n const error = createVersionNotFoundError(\n requestedVersion ?? 'unknown',\n availableVersions,\n config.errorResponse.versionNotFoundStatus\n );\n sendErrorResponse(error, req, res, next, config);\n }\n }\n break;\n }\n\n default: {\n const error = createInvalidConfigurationError(\n 'Unsupported fallback strategy. Valid strategies: latest, none, default'\n );\n sendErrorResponse(error, req, res, next, config);\n break;\n }\n }\n}\n\n/**\n * Executes a compiled handler safely.\n */\nfunction executeHandler(\n handler: CompiledHandler,\n req: VersionedRequest,\n res: Response,\n next: NextFunction\n): void {\n try {\n const result = handler.handler(req, res, next);\n\n // Handle async handlers\n if (result instanceof Promise) {\n result.catch((error: unknown) => {\n next(error);\n });\n }\n } catch (error) {\n next(error);\n }\n}\n\n/**\n * Executes a request handler safely (sync + async).\n */\nfunction executeRequestHandler(\n handler: (req: VersionedRequest, res: Response, next: NextFunction) => unknown,\n req: VersionedRequest,\n res: Response,\n next: NextFunction\n): void {\n try {\n const result = handler(req, res, next);\n\n if (result instanceof Promise) {\n void result.catch((error: unknown) => {\n next(error);\n });\n }\n } catch (error) {\n next(error);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,41 +1,99 @@
1
1
  {
2
2
  "name": "express-version-api",
3
- "description": "Middleware for versioning routes/APIs in Express.js using ECMAScript 6. Note: This library is intended for personal use and experimentation, and is not recommended for production environments or widespread adoption.",
4
- "version": "1.0.1",
5
- "author": "Braian Quintian",
3
+ "version": "2.0.4",
4
+ "description": "Middleware for versioning routes/APIs in Express.js using semantic versioning like ^1.0 or ~1.2.",
5
+ "author": "Braian Quintian <bquintian.developer@gmail.com>",
6
6
  "license": "MIT",
7
- "homepage": "https://github.com/Braian-Quintian/express-version-routes?tab=readme-ov-file",
7
+ "homepage": "https://github.com/Braian-Quintian/express-version-api#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/Braian-Quintian/express-version-api"
10
+ "url": "git+https://github.com/Braian-Quintian/express-version-api.git"
11
11
  },
12
12
  "bugs": {
13
- "url": "https://github.com/Braian-Quintian/express-version-routes/issues"
14
- },
15
- "main": "dist/index.js",
16
- "engines": {
17
- "node": ">=20.0.0"
18
- },
19
- "scripts": {
20
- "build": "npx tsc",
21
- "test": "jest --verbose"
13
+ "url": "https://github.com/Braian-Quintian/express-version-api/issues"
22
14
  },
23
15
  "keywords": [
24
- "routes",
16
+ "express",
17
+ "versioning",
25
18
  "api",
26
- "version",
27
- "versioning"
19
+ "routes",
20
+ "middleware",
21
+ "semver",
22
+ "api-versioning",
23
+ "rest-api",
24
+ "typescript"
28
25
  ],
26
+ "type": "module",
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "require": "./dist/index.cjs",
35
+ "default": "./dist/index.js"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "sideEffects": false,
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "packageManager": "npm@10.8.0",
29
44
  "files": [
30
- "dist"
45
+ "dist",
46
+ "README.md",
47
+ "LICENSE",
48
+ "CHANGELOG.md"
31
49
  ],
50
+ "scripts": {
51
+ "clean": "rm -rf dist coverage",
52
+ "typecheck": "tsc --noEmit -p tsconfig.esm.json",
53
+ "build": "tsup",
54
+ "build:watch": "tsup --watch",
55
+ "lint": "eslint .",
56
+ "lint:fix": "eslint . --fix",
57
+ "lint-staged": "lint-staged",
58
+ "format": "prettier --write .",
59
+ "format:check": "prettier --check . --ignore-path .prettierignore",
60
+ "test": "jest",
61
+ "test:watch": "jest --watch",
62
+ "test:coverage": "jest --coverage",
63
+ "test:types": "tsd --files \"test-types/**/*.test-d.ts\"",
64
+ "validate": "npm run typecheck && npm run lint && npm run format:check && npm run test && npm run build && npm run test:types",
65
+ "prepublishOnly": "npm run validate",
66
+ "prepare": "husky || true"
67
+ },
68
+ "tsd": {
69
+ "directory": "test-types"
70
+ },
71
+ "peerDependencies": {
72
+ "express": ">=4.0.0 || >=5.0.0"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "express": {
76
+ "optional": false
77
+ }
78
+ },
32
79
  "devDependencies": {
33
- "@types/express": "^4.17.21",
34
- "@types/jest": "^29.5.12",
35
- "@types/supertest": "^6.0.2",
36
- "express": "^4.19.2",
37
- "jest": "^29.7.0",
38
- "supertest": "^7.0.0",
39
- "ts-jest": "^29.1.2"
80
+ "@eslint/js": "^9.30.1",
81
+ "@types/express": "^5.0.3",
82
+ "@types/jest": "^30.0.0",
83
+ "@types/node": "^22.15.0",
84
+ "@types/supertest": "^6.0.3",
85
+ "eslint": "^9.30.1",
86
+ "eslint-config-prettier": "^10.1.0",
87
+ "express": "^5.1.0",
88
+ "husky": "^9.1.0",
89
+ "jest": "^30.2.0",
90
+ "lint-staged": "^16.1.0",
91
+ "prettier": "^3.5.0",
92
+ "supertest": "^7.2.2",
93
+ "ts-jest": "^29.4.6",
94
+ "tsd": "^0.33.0",
95
+ "tsup": "^8.5.0",
96
+ "typescript": "^5.8.3",
97
+ "typescript-eslint": "^8.35.1"
40
98
  }
41
99
  }
package/dist/lib.d.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * Interface for version handlers.
3
- * Each key is a version string and the value is a function to handle requests for that version.
4
- */
5
- interface VersionHandlers {
6
- [key: string]: (req: any, res: any, next: any) => void;
7
- }
8
- /**
9
- * Main middleware that handles version-based routing logic.
10
- * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
11
- * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
12
- * @returns {((req: any, res: any, next: any) => void)} - The middleware function.
13
- */
14
- declare const versioningMiddleware: (versionHandlers: VersionHandlers, defaultHandler?: (req: any, res: any, next: any) => void) => ((req: any, res: any, next: any) => void);
15
- export default versioningMiddleware;
package/dist/lib.js DELETED
@@ -1,147 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /**
4
- * Extracts the version from the request object.
5
- * @param {any} req - The request object.
6
- * @returns {string | boolean} - The version string if it exists, otherwise false.
7
- */
8
- const extractVersionFromRequest = (req) => {
9
- return req
10
- ? req.version
11
- ? String(req.version)
12
- : req.headers["accept-version"]
13
- : false;
14
- };
15
- /**
16
- * Executes the corresponding version handler if there is a version match.
17
- * @param {string} handlerKey - The key of the handler to execute.
18
- * @param {string} clientVersion - The version from the client's request.
19
- * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
20
- * @param {any} req - The request object.
21
- * @param {any} res - The response object.
22
- * @param {any} next - The next middleware function.
23
- * @returns {boolean} - True if a matching version handler was executed, otherwise false.
24
- */
25
- const executeVersionHandler = (handlerKey, clientVersion, versionHandlers, req, res, next) => {
26
- let tempKey;
27
- let versionArray = clientVersion.split(".");
28
- let tempVersion;
29
- if (handlerKey[0] === "~") {
30
- // Handling versions using '~'
31
- tempKey = handlerKey.substring(1).split(".").slice(0, 2).join(".");
32
- versionArray[1] = versionArray[1] || "0";
33
- tempVersion = versionArray.slice(0, 2).join(".");
34
- }
35
- else if (handlerKey[0] === "^") {
36
- // Handling versions using '^'
37
- tempKey = handlerKey.substring(1).split(".").slice(0, 1).join(".");
38
- tempVersion = versionArray.slice(0, 1).join(".");
39
- }
40
- else {
41
- // Standard version handling
42
- tempKey = handlerKey;
43
- versionArray[1] = versionArray[1] || "0";
44
- versionArray[2] = versionArray[2] || "0";
45
- tempVersion = versionArray.join(".");
46
- }
47
- if (tempKey === tempVersion && versionHandlers[handlerKey]) {
48
- // Call the corresponding version handler if there is a match
49
- versionHandlers[handlerKey].call(this, req, res, next);
50
- return true;
51
- }
52
- return false;
53
- };
54
- /**
55
- * Executes the default handler if no matching version is found.
56
- * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
57
- * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
58
- * @param {string[]} keys - The keys of all version handlers.
59
- * @param {any} req - The request object.
60
- * @param {any} res - The response object.
61
- * @param {any} next - The next middleware function.
62
- */
63
- const executeDefaultHandler = (defaultHandler, versionHandlers, keys, req, res, next) => {
64
- // Check if there is a defaultHandler defined and execute it if it exists
65
- if (defaultHandler) {
66
- defaultHandler(req, res, next);
67
- }
68
- else {
69
- // If there is no defaultHandler, determine the most recent version
70
- const latestVersionKey = findLatestVersion(keys);
71
- // If the latest version is found and there is a handler for that version, execute it
72
- if (latestVersionKey && versionHandlers[latestVersionKey]) {
73
- versionHandlers[latestVersionKey](req, res, next);
74
- }
75
- else {
76
- // If no valid version is found to handle the request, respond with a 422 error
77
- res.status(422).send("Unprocessable Entity: No valid handler found for this request.");
78
- }
79
- }
80
- };
81
- /**
82
- * Main middleware that handles version-based routing logic.
83
- * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
84
- * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
85
- * @returns {((req: any, res: any, next: any) => void)} - The middleware function.
86
- */
87
- const versioningMiddleware = (versionHandlers, defaultHandler) => {
88
- if (!versionHandlers ||
89
- typeof versionHandlers !== "object" ||
90
- Array.isArray(versionHandlers)) {
91
- throw new Error("Invalid argument: 'versionHandlers' must be a non-array object.");
92
- }
93
- else {
94
- return (req, res, next) => {
95
- const clientVersion = extractVersionFromRequest(req);
96
- const keys = Object.keys(versionHandlers);
97
- if (!clientVersion) {
98
- // Execute the default handler if no version is provided
99
- return res.status(422).send("Unprocessable Entity: No version provided.");
100
- }
101
- for (let i = 0; i < keys.length; i++) {
102
- const handlerKey = keys[i];
103
- if (executeVersionHandler(handlerKey, clientVersion, versionHandlers, req, res, next)) {
104
- return;
105
- }
106
- }
107
- // If no matching version is found, execute the default handler
108
- executeDefaultHandler(defaultHandler, versionHandlers, keys, req, res, next);
109
- };
110
- }
111
- };
112
- /**
113
- * Finds the latest version among an array of versions.
114
- * @param {string[]} versions - The array of version strings.
115
- * @returns {string | null} - The latest version string, or null if the array is empty.
116
- */
117
- const findLatestVersion = (versions) => {
118
- // Find the latest version among an array of versions
119
- if (!versions || versions.length === 0) {
120
- return null;
121
- }
122
- const processedVersions = versions.map((version) => {
123
- // Process versions by removing special characters and padding with zeros
124
- const versionArr = version.replace(/[\^~]/g, "").split(".");
125
- for (let i = 0; i < 2; i++) {
126
- versionArr[i] = versionArr[i] || "0";
127
- }
128
- return versionArr;
129
- });
130
- // Sort processed versions to find the latest one
131
- processedVersions.sort((v1Arr, v2Arr) => {
132
- for (let i = 0; i < 2; i++) {
133
- if (Number(v1Arr[i]) > Number(v2Arr[i])) {
134
- return 1;
135
- }
136
- else if (Number(v1Arr[i]) < Number(v2Arr[i])) {
137
- return -1;
138
- }
139
- }
140
- return 0;
141
- });
142
- return processedVersions[processedVersions.length - 1].join(".");
143
- };
144
- // Exporta la función para CommonJS
145
- module.exports = versioningMiddleware;
146
- // Exporta la función como default para ES6 modules
147
- exports.default = versioningMiddleware;