@spfn/core 0.2.0-beta.67 → 0.2.0-beta.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -116
- package/dist/authz/index.js +398 -3
- package/dist/authz/index.js.map +1 -1
- package/dist/codegen/index.d.ts +114 -8
- package/dist/codegen/index.js +162 -3
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/contract/index.d.ts +329 -0
- package/dist/contract/index.js +558 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/{define-middleware-DuXD8Hvu.d.ts → define-middleware-B9bFuXVU.d.ts} +1 -1
- package/dist/errors/index.js +398 -3
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +3 -3
- package/dist/event/sse/client.d.ts +2 -2
- package/dist/event/sse/index.d.ts +4 -4
- package/dist/event/sse/index.js +9 -0
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +2 -2
- package/dist/event/ws/index.d.ts +3 -3
- package/dist/middleware/index.d.ts +108 -11
- package/dist/middleware/index.js +769 -632
- package/dist/middleware/index.js.map +1 -1
- package/dist/route/index.d.ts +8 -552
- package/dist/route/index.js +66 -3
- package/dist/route/index.js.map +1 -1
- package/dist/router-HDVa6gfc.d.ts +676 -0
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -1
- package/dist/{token-manager-jKD_EsSE.d.ts → token-manager-vZeqBbtA.d.ts} +7 -0
- package/dist/{types-DVjf37yO.d.ts → types-CF-37KAG.d.ts} +1 -1
- package/dist/{types-BFB72jbM.d.ts → types-D9uMxeQS.d.ts} +1 -1
- package/package.json +11 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/contract/collect.ts","../../src/contract/stable-json.ts","../../src/contract/compare.ts","../../src/contract/snapshot.ts","../../src/contract/usage.ts","../../src/contract/check.ts"],"names":["REQUEST_SECTIONS","usageDir","readdirSync","join","readFileSync"],"mappings":";;;;;AAmBO,IAAM,uBAAA,GAAN,cAAsC,KAAA,CAC7C;AAAA,EACI,YAAY,OAAA,EACZ;AACI,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AAAA,EAChB;AACJ;AAEA,SAAS,SAAS,KAAA,EAClB;AACI,EAAA,OAAO,UAAU,IAAA,IACV,OAAO,UAAU,QAAA,IACjB,QAAA,IAAY,SACZ,SAAA,IAAa,KAAA;AACxB;AAEA,SAAS,WAAW,KAAA,EACpB;AACI,EAAA,OAAO,KAAA,KAAU,IAAA,IACV,OAAO,KAAA,KAAU,YACjB,SAAA,IAAa,KAAA;AACxB;AAQA,SAAS,aAAa,MAAA,EACtB;AACI,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAC5C;AAQA,IAAM,mBAAmB,CAAC,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,WAAW,SAAS,CAAA;AAEzE,SAAS,kBAAkB,KAAA,EAC3B;AACI,EAAA,MAAM,UAA2B,EAAC;AAElC,EAAA,IAAI,CAAC,KAAA,EACL;AACI,IAAA,OAAO,OAAA;AAAA,EACX;AAEA,EAAA,KAAA,MAAW,WAAW,gBAAA,EACtB;AACI,IAAA,MAAM,MAAA,GAAS,MAAM,OAAO,CAAA;AAC5B,IAAA,IAAI,MAAA,EACJ;AACI,MAAA,OAAA,CAAQ,OAAO,CAAA,GAAI,YAAA,CAAa,MAAM,CAAA;AAAA,IAC1C;AAAA,EACJ;AAEA,EAAA,OAAO,OAAA;AACX;AAUA,SAAS,WAAA,CAAY,MAAA,EAAqB,KAAA,EAAiB,KAAA,EAC3D;AACI,EAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA,EACxD;AACI,IAAA,IAAI,QAAA,CAAS,KAAK,CAAA,EAClB;AACI,MAAA,WAAA,CAAY,OAAO,CAAC,GAAG,KAAA,EAAO,IAAI,GAAG,KAAK,CAAA;AAC1C,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,CAAC,UAAA,CAAW,KAAK,CAAA,EACrB;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,QAAA,GAAW,KAAA;AACjB,IAAA,IAAI,CAAC,SAAS,QAAA,EACd;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,YAAA,CAAa,MAAM,QAAA,EAAU,CAAC,GAAG,KAAA,EAAO,IAAI,GAAG,KAAK,CAAA;AAAA,EACxD;AAEA,EAAA,KAAA,MAAW,aAAA,IAAiB,MAAA,CAAO,eAAA,IAAmB,EAAC,EACvD;AACI,IAAA,WAAA,CAAY,eAAe,CAAC,GAAG,KAAA,EAAO,WAAW,GAAG,KAAK,CAAA;AAAA,EAC7D;AACJ;AAWA,SAAS,gBAAA,CAAiB,OAAe,QAAA,EACzC;AACI,EAAA,MAAM,QAAQ,QAAA,CAAS,KAAA;AACvB,EAAA,MAAM,cAAc,QAAA,CAAS,WAAA;AAC7B,EAAA,MAAM,UAAU,KAAA,EAAO,QAAA,GAAW,OAAA,GAAU,WAAA,EAAa,WAAW,aAAA,GAAgB,MAAA;AAEpF,EAAA,IAAI,CAAC,OAAA,EACL;AACI,IAAA;AAAA,EACJ;AAEA,EAAA,MAAM,IAAI,uBAAA;AAAA,IACN,CAAA,kBAAA,EAAqB,KAAK,CAAA,WAAA,EAAc,OAAO,CAAA,2NAAA;AAAA,GAGnD;AACJ;AAEA,SAAS,YAAA,CAAa,IAAA,EAAc,QAAA,EAAyB,KAAA,EAAiB,KAAA,EAC9E;AACI,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA;AAC5B,EAAA,MAAM,WAAW,QAAA,CAAS,QAAA;AAE1B,EAAA,gBAAA,CAAiB,OAAO,QAAQ,CAAA;AAEhC,EAAA,IAAI,CAAC,QAAA,CAAS,MAAA,IAAU,CAAC,SAAS,IAAA,EAClC;AACI,IAAA,MAAM,IAAI,uBAAA;AAAA,MACN,qBAAqB,KAAK,CAAA,6FAAA;AAAA,KAE9B;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,SAAS,KAAA,EACd;AACI,IAAA,MAAM,IAAI,uBAAA;AAAA,MACN,qBAAqB,KAAK,CAAA,4FAAA;AAAA,KAE9B;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,SAAS,QAAA,EACd;AACI,IAAA,MAAM,IAAI,uBAAA;AAAA,MACN,qBAAqB,KAAK,CAAA,8EAAA;AAAA,KAE9B;AAAA,EACJ;AAEA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA;AAC/B,EAAA,IAAI,QAAA,EACJ;AACI,IAAA,MAAM,IAAI,uBAAA;AAAA,MACN,yCAAyC,IAAI,CAAA,GAAA,EAAM,QAAA,CAAS,KAAK,QAAQ,KAAK,CAAA,mFAAA;AAAA,KAElF;AAAA,EACJ;AAEA,EAAA,KAAA,CAAM,IAAI,IAAA,EAAM;AAAA,IACZ,KAAA,EAAO,KAAA;AAAA,IACP,SAAA,EAAW;AAAA,MACP,IAAA;AAAA,MACA,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,MAAM,QAAA,CAAS,IAAA;AAAA,MACf,OAAO,QAAA,CAAS,KAAA;AAAA,MAChB,IAAA,EAAM,SAAS,IAAA,IAAQ,MAAA;AAAA,MACvB,eAAA,EAAiB,SAAS,eAAA,IAAmB,KAAA;AAAA,MAC7C,GAAI,SAAS,YAAA,GAAe,EAAE,cAAc,QAAA,CAAS,YAAA,KAAiB,EAAC;AAAA,MACvE,GAAI,SAAS,SAAA,GAAY,EAAE,WAAW,QAAA,CAAS,SAAA,KAAc,EAAC;AAAA,MAC9D,OAAA,EAAS,iBAAA,CAAkB,QAAA,CAAS,KAAK,CAAA;AAAA,MACzC,WAAA,EAAa,iBAAA,CAAkB,QAAA,CAAS,WAAW,CAAA;AAAA,MACnD,QAAA,EAAU,YAAA,CAAa,QAAA,CAAS,QAAQ;AAAA;AAC5C,GACH,CAAA;AACL;AAOO,SAAS,wBAAwB,MAAA,EACxC;AACI,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAmB;AACrC,EAAA,WAAA,CAAY,MAAA,EAAQ,CAAC,QAAQ,CAAA,EAAG,KAAK,CAAA;AAErC,EAAA,MAAM,UAAA,GAAa,CAAC,GAAG,KAAA,CAAM,MAAA,EAAQ,CAAA,CAChC,GAAA,CAAI,CAAA,KAAA,KAAS,KAAA,CAAM,SAAS,CAAA,CAC5B,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,IAAA,GAAO,CAAA,CAAE,IAAA,GAAO,EAAA,GAAK,CAAA,CAAE,IAAA,GAAO,CAAA,CAAE,IAAA,GAAO,CAAA,GAAI,CAAE,CAAA;AAEpE,EAAA,OAAO;AAAA,IACH,eAAA,EAAiB,CAAA;AAAA,IACjB,GAAI,OAAO,gBAAA,GAAmB,EAAE,iBAAiB,MAAA,CAAO,gBAAA,KAAqB,EAAC;AAAA,IAC9E,mBAAA,EAAqB,cAAA;AAAA,IACrB;AAAA,GACJ;AACJ;ACpNO,SAAS,aAAa,KAAA,EAC7B;AACI,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EACvB;AACI,IAAA,OAAO,KAAA,CAAM,IAAI,YAAY,CAAA;AAAA,EACjC;AAEA,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,EACvC;AACI,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,MAAM,MAAA,GAAS,KAAA;AACf,EAAA,MAAM,SAAkC,EAAC;AAEzC,EAAA,KAAA,MAAW,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,MAAK,EAC3C;AACI,IAAA,IAAI,MAAA,CAAO,GAAG,CAAA,KAAM,MAAA,EACpB;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,MAAA,CAAO,GAAG,CAAA,GAAI,YAAA,CAAa,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,MAAA;AACX;AAGO,SAAS,gBAAgB,KAAA,EAChC;AACI,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,YAAA,CAAa,KAAK,CAAC,CAAA;AAC7C;AAGO,SAAS,sBAAsB,KAAA,EACtC;AACI,EAAA,OAAO,CAAA,EAAG,KAAK,SAAA,CAAU,YAAA,CAAa,KAAK,CAAA,EAAG,IAAA,EAAM,CAAC,CAAC;AAAA,CAAA;AAC1D;AAGO,SAAS,aAAa,KAAA,EAC7B;AACI,EAAA,OAAO,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,eAAA,CAAgB,KAAK,CAAA,EAAG,MAAM,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AACnF;;;ACXA,IAAMA,oBAAmB,CAAC,QAAA,EAAU,SAAS,MAAA,EAAQ,UAAA,EAAY,WAAW,SAAS,CAAA;AAWrF,SAAS,cAAc,MAAA,EACvB;AACI,EAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,EAAO,GAAG,MAAK,GAAI,MAAA;AAEjD,EAAA,OAAO,gBAAgB,IAAI,CAAA;AAC/B;AAEA,SAAS,aAAa,MAAA,EACtB;AACI,EAAA,MAAM,aAAa,MAAA,CAAO,UAAA;AAE1B,EAAA,IAAI,CAAC,UAAA,IAAc,OAAO,UAAA,KAAe,QAAA,EACzC;AACI,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,OAAO,UAAA;AACX;AAEA,SAAS,WAAW,MAAA,EACpB;AACI,EAAA,MAAM,WAAW,MAAA,CAAO,QAAA;AAExB,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAC3B;AACI,IAAA,2BAAW,GAAA,EAAI;AAAA,EACnB;AAEA,EAAA,OAAO,IAAI,IAAI,QAAA,CAAS,MAAA,CAAO,CAAC,IAAA,KAAyB,OAAO,IAAA,KAAS,QAAQ,CAAC,CAAA;AACtF;AAQA,SAAS,qBAAqB,MAAA,EAC9B;AACI,EAAA,IAAI,MAAA,CAAO,SAAS,QAAA,EACpB;AACI,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,OAAO,UAAA,CAAW,MAAM,CAAA,CAAE,IAAA,GAAO,CAAA;AACrC;AASA,SAAS,MAAA,CACL,GAAA,EACA,IAAA,EACA,QAAA,EACA,MAAA,EAEJ;AACI,EAAA,GAAA,CAAI,UAAA,CAAW,KAAK,EAAE,IAAA,EAAM,WAAW,GAAA,CAAI,SAAA,EAAW,QAAA,EAAU,MAAA,EAAQ,CAAA;AAC5E;AAQA,SAAS,aAAA,CACL,MAAA,EACA,KAAA,EACA,QAAA,EACA,GAAA,EAEJ;AACI,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,KAAA,EAChB;AACI,IAAA;AAAA,EACJ;AAEA,EAAA,IAAI,MAAA,IAAU,CAAC,KAAA,EACf;AACI,IAAA,IAAI,GAAA,CAAI,SAAS,UAAA,EACjB;AACI,MAAA,MAAA,CAAO,GAAA,EAAK,wBAAA,EAA0B,QAAA,EAAU,8DAA8D,CAAA;AAAA,IAClH;AAGA,IAAA;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,UAAU,KAAA,EACf;AACI,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,IAAa,oBAAA,CAAqB,KAAK,CAAA,EACxD;AACI,MAAA,MAAA;AAAA,QACI,GAAA;AAAA,QACA,8BAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACJ;AAAA,IACJ;AAGA,IAAA;AAAA,EACJ;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA;AACb,EAAA,MAAM,EAAA,GAAK,KAAA;AAEX,EAAA,IAAI,aAAA,CAAc,IAAI,CAAA,KAAM,aAAA,CAAc,EAAE,CAAA,EAC5C;AACI,IAAA,MAAA;AAAA,MACI,GAAA;AAAA,MACA,GAAA,CAAI,IAAA,KAAS,SAAA,GAAY,sBAAA,GAAyB,uBAAA;AAAA,MAClD,QAAA;AAAA,MACA,8BAA8B,aAAA,CAAc,IAAI,CAAC,CAAA,QAAA,EAAM,aAAA,CAAc,EAAE,CAAC,CAAA;AAAA,KAC5E;AAEA,IAAA;AAAA,EACJ;AAEA,EAAA,iBAAA,CAAkB,IAAA,EAAM,EAAA,EAAI,QAAA,EAAU,GAAG,CAAA;AACzC,EAAA,aAAA;AAAA,IACI,IAAA,CAAK,KAAA;AAAA,IACL,EAAA,CAAG,KAAA;AAAA,IACH,GAAG,QAAQ,CAAA,EAAA,CAAA;AAAA,IACX;AAAA,GACJ;AACJ;AAEA,SAAS,iBAAA,CAAkB,IAAA,EAAkB,EAAA,EAAgB,QAAA,EAAkB,GAAA,EAC/E;AACI,EAAA,MAAM,gBAAA,GAAmB,aAAa,IAAI,CAAA;AAC1C,EAAA,MAAM,eAAA,GAAkB,aAAa,EAAE,CAAA;AACvC,EAAA,MAAM,cAAA,GAAiB,WAAW,IAAI,CAAA;AACtC,EAAA,MAAM,aAAA,GAAgB,WAAW,EAAE,CAAA;AAEnC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,YAAY,KAAK,MAAA,CAAO,OAAA,CAAQ,gBAAgB,CAAA,EAClE;AACI,IAAA,MAAM,KAAA,GAAQ,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACjC,IAAA,MAAM,WAAA,GAAc,gBAAgB,IAAI,CAAA;AAExC,IAAA,aAAA,CAAc,YAAA,EAAc,WAAA,EAAa,KAAA,EAAO,GAAG,CAAA;AAEnD,IAAA,IAAI,CAAC,WAAA,EACL;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,WAAA,GAAc,cAAA,CAAe,GAAA,CAAI,IAAI,CAAA;AAC3C,IAAA,MAAM,UAAA,GAAa,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA;AAEzC,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,SAAA,IAAa,CAAC,eAAe,UAAA,EAC9C;AACI,MAAA,MAAA,CAAO,GAAA,EAAK,+BAAA,EAAiC,KAAA,EAAO,8CAA8C,CAAA;AAAA,IACtG;AAEA,IAAA,IAAI,GAAA,CAAI,IAAA,KAAS,UAAA,IAAc,WAAA,IAAe,CAAC,UAAA,EAC/C;AACI,MAAA,MAAA,CAAO,GAAA,EAAK,gCAAA,EAAkC,KAAA,EAAO,wDAAwD,CAAA;AAAA,IACjH;AAAA,EACJ;AAIA,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,IAAA,CAAK,eAAe,CAAA,EAC9C;AACI,IAAA,IAAI,gBAAA,CAAiB,IAAI,CAAA,EACzB;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,IAAI,IAAA,KAAS,SAAA,IAAa,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA,EACpD;AACI,MAAA,MAAA;AAAA,QACI,GAAA;AAAA,QACA,8BAAA;AAAA,QACA,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAAA,QACnB;AAAA,OACJ;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,SAAS,sBAAA,CACL,MAAA,EACA,KAAA,EACA,MAAA,EACA,GAAA,EAEJ;AACI,EAAA,KAAA,MAAW,WAAWA,iBAAAA,EACtB;AACI,IAAA,aAAA,CAAc,MAAA,CAAO,OAAO,CAAA,EAAG,KAAA,CAAM,OAAO,CAAA,EAAG,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,EAAI,GAAG,CAAA;AAAA,EAC9E;AACJ;AAGO,SAAS,gBAAA,CAAiB,QAA2B,KAAA,EAC5D;AACI,EAAA,MAAM,aAAkC,EAAC;AAEzC,EAAA,IAAI,MAAA,CAAO,IAAA,KAAS,KAAA,CAAM,IAAA,EAC1B;AACI,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACZ,IAAA,EAAM,wBAAA;AAAA,MACN,WAAW,MAAA,CAAO,IAAA;AAAA,MAClB,QAAQ,CAAA,WAAA,EAAc,MAAA,CAAO,IAAI,CAAA,QAAA,EAAM,MAAM,IAAI,CAAA,mCAAA;AAAA,KACpD,CAAA;AAAA,EACL;AAEA,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,KAAA,CAAM,MAAA,EAC5B;AACI,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACZ,IAAA,EAAM,0BAAA;AAAA,MACN,WAAW,MAAA,CAAO,IAAA;AAAA,MAClB,QAAQ,CAAA,eAAA,EAAkB,MAAA,CAAO,MAAM,CAAA,QAAA,EAAM,MAAM,MAAM,CAAA,mCAAA;AAAA,KAC5D,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,aAA6B,EAAE,SAAA,EAAW,OAAO,IAAA,EAAM,IAAA,EAAM,WAAW,UAAA,EAAW;AACzF,EAAA,sBAAA,CAAuB,MAAA,CAAO,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS,WAAW,UAAU,CAAA;AAC3E,EAAA,sBAAA,CAAuB,MAAA,CAAO,WAAA,EAAa,KAAA,CAAM,WAAA,EAAa,eAAe,UAAU,CAAA;AAEvF,EAAA,aAAA;AAAA,IACI,MAAA,CAAO,QAAA;AAAA,IACP,KAAA,CAAM,QAAA;AAAA,IACN,UAAA;AAAA,IACA,EAAE,SAAA,EAAW,MAAA,CAAO,IAAA,EAAM,IAAA,EAAM,YAAY,UAAA;AAAW,GAC3D;AAEA,EAAA,OAAO,UAAA;AACX;AAQO,SAAS,gBAAA,CAAiB,QAA0B,KAAA,EAC3D;AACI,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,KAAA,CAAM,UAAA,CAAW,GAAA,CAAI,CAAA,SAAA,KAAa,CAAC,SAAA,CAAU,IAAA,EAAM,SAAS,CAAC,CAAC,CAAA;AACtF,EAAA,MAAM,aAAkC,EAAC;AACzC,EAAA,MAAM,oBAA8B,EAAC;AAErC,EAAA,KAAA,MAAW,SAAA,IAAa,OAAO,UAAA,EAC/B;AACI,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,SAAA,CAAU,IAAI,CAAA;AAEtC,IAAA,IAAI,CAAC,GAAA,EACL;AACI,MAAA,iBAAA,CAAkB,IAAA,CAAK,UAAU,IAAI,CAAA;AACrC,MAAA;AAAA,IACJ;AAEA,IAAA,UAAA,CAAW,IAAA,CAAK,GAAG,gBAAA,CAAiB,SAAA,EAAW,GAAG,CAAC,CAAA;AAAA,EACvD;AAEA,EAAA,OAAO,EAAE,YAAY,iBAAA,EAAkB;AAC3C;AC5SO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAC3C;AAAA,EACI,YAAY,OAAA,EACZ;AACI,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EAChB;AACJ;AAEO,IAAM,gBAAA,GAAmB;AACzB,IAAM,gBAAA,GAAmB;AACzB,IAAM,aAAA,GAAgB;AAEtB,SAAS,YAAY,YAAA,EAC5B;AACI,EAAA,OAAO,IAAA,CAAK,cAAc,gBAAgB,CAAA;AAC9C;AAEO,SAAS,YAAY,YAAA,EAC5B;AACI,EAAA,OAAO,IAAA,CAAK,cAAc,gBAAgB,CAAA;AAC9C;AAEO,SAAS,SAAS,YAAA,EACzB;AACI,EAAA,OAAO,IAAA,CAAK,cAAc,aAAa,CAAA;AAC3C;AAQO,SAAS,eAAA,CAAgB,GAAW,CAAA,EAC3C;AACI,EAAA,MAAM,CAAC,KAAA,EAAO,IAAI,CAAA,GAAI,aAAa,CAAC,CAAA;AACpC,EAAA,MAAM,CAAC,KAAA,EAAO,IAAI,CAAA,GAAI,aAAa,CAAC,CAAA;AAEpC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EACvB;AACI,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,CAAA,EACxB;AACI,MAAA,OAAO,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA;AAAA,IAC7B;AAAA,EACJ;AAEA,EAAA,IAAI,SAAS,IAAA,EACb;AACI,IAAA,OAAO,CAAA;AAAA,EACX;AAEA,EAAA,IAAI,SAAS,MAAA,EACb;AACI,IAAA,OAAO,CAAA;AAAA,EACX;AAEA,EAAA,IAAI,SAAS,MAAA,EACb;AACI,IAAA,OAAO,EAAA;AAAA,EACX;AAEA,EAAA,OAAO,iBAAA,CAAkB,MAAM,IAAI,CAAA;AACvC;AAEA,SAAS,aAAa,OAAA,EACtB;AACI,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,IAAI,CAAA,GAAI,OAAA,CAAQ,MAAM,GAAG,CAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAA,IAAA,KAAQ,MAAA,CAAO,QAAA,CAAS,IAAA,EAAM,EAAE,CAAC,CAAA;AAEnE,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,KAAA,CAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,CAAC,MAAA,CAAO,SAAA,CAAU,IAAI,CAAA,IAAK,IAAA,GAAO,CAAC,CAAA,EAChF;AACI,IAAA,MAAM,IAAI,qBAAA,CAAsB,CAAA,CAAA,EAAI,OAAO,CAAA,gDAAA,CAAkD,CAAA;AAAA,EACjG;AAEA,EAAA,OAAO,CAAC,OAAO,IAAA,CAAK,MAAA,GAAS,IAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,GAAI,MAAS,CAAA;AAC/D;AAEA,SAAS,iBAAA,CAAkB,GAAW,CAAA,EACtC;AACI,EAAA,MAAM,MAAA,GAAS,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AAE1B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,OAAO,MAAA,EAAQ,MAAA,CAAO,MAAM,CAAA,EAAG,CAAA,EAAA,EAC5D;AACI,IAAA,MAAM,IAAA,GAAO,OAAO,CAAC,CAAA;AACrB,IAAA,MAAM,KAAA,GAAQ,OAAO,CAAC,CAAA;AAEtB,IAAA,IAAI,SAAS,MAAA,EACb;AACI,MAAA,OAAO,EAAA;AAAA,IACX;AAEA,IAAA,IAAI,UAAU,MAAA,EACd;AACI,MAAA,OAAO,CAAA;AAAA,IACX;AAEA,IAAA,IAAI,SAAS,KAAA,EACb;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AACrC,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA;AAEvC,IAAA,IAAI,eAAe,YAAA,EACnB;AACI,MAAA,OAAO,MAAA,CAAO,IAAI,CAAA,GAAI,MAAA,CAAO,KAAK,CAAA;AAAA,IACtC;AAEA,IAAA,IAAI,gBAAgB,YAAA,EACpB;AACI,MAAA,OAAO,cAAc,EAAA,GAAK,CAAA;AAAA,IAC9B;AAEA,IAAA,OAAO,IAAA,GAAO,QAAQ,EAAA,GAAK,CAAA;AAAA,EAC/B;AAEA,EAAA,OAAO,CAAA;AACX;AASO,SAAS,cAAc,YAAA,EAC9B;AACI,EAAA,MAAM,GAAA,GAAM,YAAY,YAAY,CAAA;AAEpC,EAAA,IAAI,CAAC,UAAA,CAAW,GAAG,CAAA,EACnB;AACI,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,OAAO,YAAY,GAAG,CAAA,CACjB,MAAA,CAAO,CAAA,IAAA,KAAQ,KAAK,QAAA,CAAS,OAAO,CAAC,CAAA,CACrC,IAAI,CAAA,IAAA,MAAS,EAAE,OAAA,EAAS,IAAA,CAAK,MAAM,CAAA,EAAG,CAAC,OAAA,CAAQ,MAAM,GAAG,IAAA,EAAM,IAAA,CAAK,GAAA,EAAK,IAAI,GAAE,CAAE,CAAA,CAChF,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM,eAAA,CAAgB,EAAE,OAAA,EAAS,CAAA,CAAE,OAAO,CAAC,CAAA;AAC7D;AAGO,SAAS,eAAe,YAAA,EAC/B;AACI,EAAA,MAAM,SAAA,GAAY,cAAc,YAAY,CAAA;AAE5C,EAAA,OAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA;AACzC;AAQO,SAAS,aAAa,IAAA,EAC7B;AACI,EAAA,IAAI,MAAA;AAEJ,EAAA,IACA;AACI,IAAA,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,OAAO,CAAC,CAAA;AAAA,EACnD,SACO,KAAA,EACP;AACI,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAErE,IAAA,MAAM,IAAI,qBAAA,CAAsB,CAAA,EAAG,IAAI,CAAA,oBAAA,EAAuB,OAAO,CAAA,CAAE,CAAA;AAAA,EAC3E;AAEA,EAAA,IAAI,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,IAAY,OAAO,OAAO,MAAA,KAAW,QAAA,IAAY,CAAC,MAAA,CAAO,QAAA,EACvF;AACI,IAAA,MAAM,IAAI,qBAAA,CAAsB,CAAA,EAAG,IAAI,CAAA,kEAAA,CAAoE,CAAA;AAAA,EAC/G;AAEA,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,MAAA,CAAO,QAAQ,CAAA;AAE3C,EAAA,IAAI,MAAA,KAAW,OAAO,MAAA,EACtB;AACI,IAAA,MAAM,IAAI,qBAAA;AAAA,MACN,GAAG,IAAI,CAAA,6CAAA,EAAgD,MAAA,CAAO,MAAM,+BAA+B,MAAM,CAAA;AAAA,KAC7G;AAAA,EACJ;AAEA,EAAA,OAAO,EAAE,SAAS,MAAA,CAAO,OAAA,EAAS,QAAQ,MAAA,CAAO,MAAA,EAAQ,QAAA,EAAU,MAAA,CAAO,QAAA,EAA6B;AAC3G;AAaO,SAAS,aAAA,CAAc,cAAsB,QAAA,EACpD;AACI,EAAA,MAAM,UAAU,QAAA,CAAS,eAAA;AAEzB,EAAA,IAAI,CAAC,OAAA,EACL;AACI,IAAA,MAAM,IAAI,qBAAA;AAAA,MACN;AAAA,KAEJ;AAAA,EACJ;AACA,EAAA,eAAA,CAAgB,SAAS,OAAO,CAAA;AAEhC,EAAA,MAAM,GAAA,GAAM,YAAY,YAAY,CAAA;AACpC,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG,OAAO,CAAA,KAAA,CAAO,CAAA;AAExC,EAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EACnB;AACI,IAAA,MAAM,IAAI,qBAAA;AAAA,MACN,GAAG,IAAI,CAAA,mGAAA;AAAA,KACX;AAAA,EACJ;AAMA,EAAA,MAAM,MAAA,GAAS,eAAe,YAAY,CAAA;AAE1C,EAAA,IAAI,UAAU,eAAA,CAAgB,OAAA,EAAS,MAAA,CAAO,OAAO,KAAK,CAAA,EAC1D;AACI,IAAA,MAAM,IAAI,qBAAA;AAAA,MACN,CAAA,EAAG,OAAO,CAAA,gCAAA,EAAmC,MAAA,CAAO,OAAO,CAAA,oGAAA;AAAA,KAE/D;AAAA,EACJ;AAEA,EAAA,SAAA,CAAU,GAAA,EAAK,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAElC,EAAA,MAAM,WAA6B,EAAE,OAAA,EAAS,QAAQ,YAAA,CAAa,QAAQ,GAAG,QAAA,EAAS;AACvF,EAAA,aAAA,CAAc,IAAA,EAAM,qBAAA,CAAsB,QAAQ,CAAA,EAAG,OAAO,CAAA;AAE5D,EAAA,OAAO,IAAA;AACX;AAGO,SAAS,oBAAoB,YAAA,EACpC;AACI,EAAA,MAAM,IAAA,GAAO,YAAY,YAAY,CAAA;AAErC,EAAA,IACA;AACI,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,OAAO,CAAC,CAAA;AAAA,EACjD,SACO,KAAA,EACP;AACI,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAErE,IAAA,MAAM,IAAI,qBAAA;AAAA,MACN,CAAA,EAAG,IAAI,CAAA,oBAAA,EAAuB,OAAO,CAAA,8CAAA;AAAA,KACzC;AAAA,EACJ;AACJ;AAGO,SAAS,oBAAA,CAAqB,cAAsB,QAAA,EAC3D;AACI,EAAA,MAAM,IAAA,GAAO,YAAY,YAAY,CAAA;AACrC,EAAA,MAAM,OAAA,GAAU,sBAAsB,QAAQ,CAAA;AAC9C,EAAA,MAAM,WAAW,UAAA,CAAW,IAAI,IAAI,YAAA,CAAa,IAAA,EAAM,OAAO,CAAA,GAAI,MAAA;AAElE,EAAA,IAAI,aAAa,OAAA,EACjB;AACI,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,SAAA,CAAU,YAAA,EAAc,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAC3C,EAAA,aAAA,CAAc,IAAA,EAAM,SAAS,OAAO,CAAA;AAEpC,EAAA,OAAO,IAAA;AACX;AC9QA,SAAS,WAAA,CAAY,MAAc,GAAA,EACnC;AACI,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAE7B,EAAA,IAAI,OAAO,MAAA,CAAO,QAAA,KAAa,YAAY,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EACtE;AACI,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EAC3D;AAEA,EAAA,IAAI,OAAO,MAAA,CAAO,UAAA,KAAe,YAAY,MAAA,CAAO,UAAA,CAAW,WAAW,CAAA,EAC1E;AACI,IAAA,MAAM,IAAI,MAAM,yCAAyC,CAAA;AAAA,EAC7D;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,UAAU,CAAA,IAAK,MAAA,CAAO,UAAA,CAAW,IAAA,CAAK,CAAA,IAAA,KAAQ,OAAO,IAAA,KAAS,QAAQ,CAAA,EAChG;AACI,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACtE;AAEA,EAAA,OAAO;AAAA,IACH,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB;AAAA,GACJ;AACJ;AAQO,SAAS,iBAAiBC,SAAAA,EACjC;AACI,EAAA,IAAI,OAAA;AAEJ,EAAA,IACA;AACI,IAAA,IAAI,CAAC,QAAA,CAASA,SAAQ,CAAA,CAAE,aAAY,EACpC;AACI,MAAA,OAAO,EAAE,SAAA,EAAW,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAGA,SAAQ,CAAA,mBAAA,CAAA,EAAsB;AAAA,IACxE;AAEA,IAAA,OAAA,GAAUC,WAAAA,CAAYD,SAAQ,CAAA,CAAE,MAAA,CAAO,CAAA,IAAA,KAAQ,KAAK,QAAA,CAAS,OAAO,CAAC,CAAA,CAAE,IAAA,EAAK;AAAA,EAChF,CAAA,CAAA,MAEA;AACI,IAAA,OAAO,EAAE,SAAA,EAAW,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAGA,SAAQ,CAAA,eAAA,CAAA,EAAkB;AAAA,EACpE;AAEA,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EACvB;AACI,IAAA,OAAO,EAAE,SAAA,EAAW,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAGA,SAAQ,CAAA,oBAAA,CAAA,EAAuB;AAAA,EACzE;AAEA,EAAA,MAAM,UAAyB,EAAC;AAEhC,EAAA,KAAA,MAAW,SAAS,OAAA,EACpB;AACI,IAAA,MAAM,IAAA,GAAOE,IAAAA,CAAKF,SAAAA,EAAU,KAAK,CAAA;AAEjC,IAAA,IACA;AACI,MAAA,OAAA,CAAQ,KAAK,WAAA,CAAY,IAAA,EAAMG,aAAa,IAAA,EAAM,OAAO,CAAC,CAAC,CAAA;AAAA,IAC/D,SACO,KAAA,EACP;AACI,MAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAErE,MAAA,OAAO,EAAE,WAAW,KAAA,EAAO,MAAA,EAAQ,GAAG,IAAI,CAAA,oBAAA,EAAuB,OAAO,CAAA,CAAA,EAAG;AAAA,IAC/E;AAAA,EACJ;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,IAAA,EAAM,OAAA,EAAQ;AACtC;AAGO,SAAS,SAAA,CAAU,WAAmB,OAAA,EAC7C;AACI,EAAA,OAAO,QAAQ,MAAA,CAAO,CAAA,MAAA,KAAU,OAAO,UAAA,CAAW,QAAA,CAAS,SAAS,CAAC,CAAA;AACzE;;;ACrFO,SAAS,aAAA,CAAc,cAAsB,OAAA,EACpD;AACI,EAAA,MAAM,QAAA,GAAW,eAAe,YAAY,CAAA;AAE5C,EAAA,IAAI,CAAC,QAAA,EACL;AACI,IAAA,OAAO;AAAA,MACH,YAAY,EAAC;AAAA,MACb,QAAA,EAAU;AAAA,QACN;AAAA;AAEJ,KACJ;AAAA,EACJ;AAEA,EAAA,IAAI,QAAA;AAEJ,EAAA,IACA;AACI,IAAA,QAAA,GAAW,YAAA,CAAa,QAAA,CAAS,IAAI,CAAA,CAAE,QAAA;AAAA,EAC3C,SACO,KAAA,EACP;AACI,IAAA,OAAO;AAAA,MACH,iBAAiB,QAAA,CAAS,OAAA;AAAA,MAC1B,UAAU,EAAC;AAAA,MACX,YAAY,CAAC;AAAA,QACT,IAAA,EAAM,0BAAA;AAAA,QACN,QAAQ,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,OAChE;AAAA,KACL;AAAA,EACJ;AAEA,EAAA,MAAM,EAAE,UAAA,EAAY,iBAAA,EAAkB,GAAI,gBAAA,CAAiB,UAAU,OAAO,CAAA;AAE5E,EAAA,OAAO;AAAA,IACH,iBAAiB,QAAA,CAAS,OAAA;AAAA,IAC1B,UAAU,EAAC;AAAA,IACX,UAAA,EAAY,CAAC,GAAG,UAAA,EAAY,GAAG,aAAA,CAAc,YAAA,EAAc,iBAAiB,CAAC;AAAA,GACjF;AACJ;AAQA,SAAS,aAAA,CAAc,cAAsB,iBAAA,EAC7C;AACI,EAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,EACjC;AACI,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,QAAA,CAAS,YAAY,CAAC,CAAA;AAErD,EAAA,IAAI,CAAC,MAAM,SAAA,EACX;AACI,IAAA,OAAO,CAAC;AAAA,MACJ,IAAA,EAAM,mBAAA;AAAA,MACN,MAAA,EACI,GAAG,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAC,CAAA,qEAAA,EACzB,MAAM,MAAM,CAAA,6EAAA;AAAA,KACzB,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,aAAkC,EAAC;AAEzC,EAAA,KAAA,MAAW,aAAa,iBAAA,EACxB;AACI,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,SAAA,EAAW,KAAA,CAAM,OAAO,CAAA;AAElD,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EACvB;AACI,MAAA;AAAA,IACJ;AAEA,IAAA,UAAA,CAAW,IAAA,CAAK;AAAA,MACZ,IAAA,EAAM,oBAAA;AAAA,MACN,SAAA;AAAA,MACA,MAAA,EACI,kBAAA,GACE,OAAA,CAAQ,GAAA,CAAI,YAAU,CAAA,EAAG,MAAA,CAAO,QAAQ,CAAA,CAAA,EAAI,MAAA,CAAO,UAAU,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI;AAAA,KACnF,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,UAAA;AACX;AAGO,SAAS,iBAAiB,UAAA,EACjC;AACI,EAAA,OAAO,UAAA,CACF,GAAA,CAAI,CAAC,SAAA,KACN;AACI,IAAA,MAAM,KAAA,GAAQ,CAAC,SAAA,CAAU,SAAA,EAAW,SAAA,CAAU,QAAQ,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAEhF,IAAA,OAAO,CAAA,KAAA,EAAQ,SAAA,CAAU,IAAI,CAAA,CAAA,EAAI,KAAA,GAAQ,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,EAAE,CAAA,EAAA,EAAK,SAAA,CAAU,MAAM,CAAA,CAAA;AAAA,EAClF,CAAC,CAAA,CACA,IAAA,CAAK,IAAI,CAAA;AAClB","file":"index.js","sourcesContent":["/**\n * Contract Collection\n *\n * Walks a loaded router and turns every route carrying `.contract()` into a\n * contract operation.\n *\n * The router is loaded and walked rather than parsed from source. Real routes\n * build their schemas from imported values — `EmailSchema`, `FileSchema()`,\n * `KEY_DEVICE_NAME_MAX_LENGTH` — which a source parser cannot resolve. Loading\n * costs a module import and no infrastructure: route modules have no\n * import-time side effects, so nothing here opens a database connection.\n */\n\nimport type { RouteDef } from '../route/route-builder';\nimport type { RouteInput } from '../route/route-input';\nimport type { Router } from '../route/router';\nimport type { ContractDocument, ContractOperation, ContractRequest, JsonSchema } from './types';\n\n/** Thrown when the router cannot produce a contract at all. */\nexport class ContractCollectionError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractCollectionError';\n }\n}\n\nfunction isRouter(value: unknown): value is Router<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'routes' in value\n && '_routes' in value;\n}\n\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'handler' in value;\n}\n\n/**\n * Strip TypeBox's symbol-keyed metadata and hand back plain JSON.\n *\n * The symbols carry no information the wire shape depends on, and they cannot\n * survive the round trip to a committed file.\n */\nfunction toJsonSchema(schema: unknown): JsonSchema\n{\n return JSON.parse(JSON.stringify(schema)) as JsonSchema;\n}\n\n/**\n * `formData` is absent on purpose — a contracted route carrying it is refused\n * before it gets here (see `assertNoFormData`). Emitting its schema alongside\n * the JSON sections described a multipart route as if it were JSON-shaped, and a\n * generated client built from that reads as correct right up to the request.\n */\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'headers', 'cookies'] as const;\n\nfunction toContractRequest(input: RouteInput | undefined): ContractRequest\n{\n const request: ContractRequest = {};\n\n if (!input)\n {\n return request;\n }\n\n for (const section of REQUEST_SECTIONS)\n {\n const schema = input[section];\n if (schema)\n {\n request[section] = toJsonSchema(schema);\n }\n }\n\n return request;\n}\n\ninterface Found\n{\n operation: ContractOperation;\n\n /** Where the route was found, for a collision message. */\n trail: string;\n}\n\nfunction visitRouter(router: Router<any>, trail: string[], found: Map<string, Found>): void\n{\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n visitRouter(entry, [...trail, name], found);\n continue;\n }\n\n if (!isRouteDef(entry))\n {\n continue;\n }\n\n const routeDef = entry as RouteDef<any>;\n if (!routeDef.contract)\n {\n continue;\n }\n\n addOperation(name, routeDef, [...trail, name], found);\n }\n\n for (const packageRouter of router._packageRouters ?? [])\n {\n visitRouter(packageRouter, [...trail, '(package)'], found);\n }\n}\n\n/**\n * Refuse a contracted route that takes multipart form data.\n *\n * Multipart is a transport-format problem, not a type problem: the contract's\n * grammar describes JSON values, and a file part has no spelling in it. The\n * refusal is loud rather than a quiet omission — a contract that silently\n * dropped the section would still claim to describe the operation, and an app\n * generated from it would break against the running server instead of at build.\n */\nfunction assertNoFormData(where: string, routeDef: RouteDef<any>): void\n{\n const input = routeDef.input as RouteInput | undefined;\n const interceptor = routeDef.interceptor as RouteInput | undefined;\n const section = input?.formData ? 'input' : interceptor?.formData ? 'interceptor' : undefined;\n\n if (!section)\n {\n return;\n }\n\n throw new ContractCollectionError(\n `Contracted route \"${where}\" declares ${section}.formData, which a contract cannot describe. `\n + 'The contract grammar covers JSON values, and multipart carries file parts that have no spelling in it. '\n + 'Drop .contract() from this route, or move the operation to a JSON body.',\n );\n}\n\nfunction addOperation(name: string, routeDef: RouteDef<any>, trail: string[], found: Map<string, Found>): void\n{\n const where = trail.join('.');\n const contract = routeDef.contract!;\n\n assertNoFormData(where, routeDef);\n\n if (!routeDef.method || !routeDef.path)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no method or path. `\n + 'A contract describes an operation on the wire, so both are required.',\n );\n }\n\n if (!contract.since)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" has no \"since\" version. `\n + 'The version an operation first appeared in is part of the promise.',\n );\n }\n\n if (!contract.response)\n {\n throw new ContractCollectionError(\n `Contracted route \"${where}\" declares no response schema. `\n + 'An operation with no body declares Type.Null().',\n );\n }\n\n const existing = found.get(name);\n if (existing)\n {\n throw new ContractCollectionError(\n `Two contracted routes are both named \"${name}\" (${existing.trail} and ${where}). `\n + 'An operation is identified by its name across versions, so names must be unique.',\n );\n }\n\n found.set(name, {\n trail: where,\n operation: {\n name,\n method: routeDef.method,\n path: routeDef.path,\n since: contract.since,\n auth: contract.auth ?? 'none',\n requiresSession: contract.requiresSession ?? false,\n ...(contract.deprecatedIn ? { deprecatedIn: contract.deprecatedIn } : {}),\n ...(contract.removedIn ? { removedIn: contract.removedIn } : {}),\n request: toContractRequest(routeDef.input),\n interceptor: toContractRequest(routeDef.interceptor),\n response: toJsonSchema(contract.response),\n },\n });\n}\n\n/**\n * Build the contract document from a loaded router.\n *\n * Routes without `.contract()` are skipped — they are not part of the promise.\n */\nexport function collectContractDocument(router: Router<any>): ContractDocument\n{\n const found = new Map<string, Found>();\n visitRouter(router, ['router'], found);\n\n const operations = [...found.values()]\n .map(entry => entry.operation)\n .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n return {\n documentVersion: 1,\n ...(router._contractVersion ? { contractVersion: router._contractVersion } : {}),\n compatibilityPolicy: 'perOperation',\n operations,\n };\n}\n","/**\n * Deterministic JSON\n *\n * The generated contract is committed and compared against what the generator\n * produces. If key order moved between runs, \"the committed file differs from\n * the generated one\" would flicker and the check would train people to ignore\n * it. So every emitted document is written with its object keys sorted, and the\n * digest that pins a released snapshot is taken over the same ordering with no\n * whitespace — formatting choices never move a digest.\n */\n\nimport { createHash } from 'node:crypto';\n\n/** Recursively sort object keys and drop `undefined` members. */\nexport function canonicalize(value: unknown): unknown\n{\n if (Array.isArray(value))\n {\n return value.map(canonicalize);\n }\n\n if (value === null || typeof value !== 'object')\n {\n return value;\n }\n\n const source = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n\n for (const key of Object.keys(source).sort())\n {\n if (source[key] === undefined)\n {\n continue;\n }\n\n sorted[key] = canonicalize(source[key]);\n }\n\n return sorted;\n}\n\n/** Compact canonical encoding — what a digest is taken over. */\nexport function stableStringify(value: unknown): string\n{\n return JSON.stringify(canonicalize(value));\n}\n\n/** Indented canonical encoding — what gets written to disk and reviewed. */\nexport function stableStringifyPretty(value: unknown): string\n{\n return `${JSON.stringify(canonicalize(value), null, 4)}\\n`;\n}\n\n/** SHA-256 of the compact canonical encoding, lowercase hex. */\nexport function stableDigest(value: unknown): string\n{\n return createHash('sha256').update(stableStringify(value), 'utf8').digest('hex');\n}\n","/**\n * Backward-compatibility comparison\n *\n * Compares the contract a build produces against the newest released snapshot.\n *\n * The rule that shapes everything here: **optional runs in opposite directions\n * on the two sides.** A request is safe when the server grows more tolerant —\n * a new optional field, a dropped field, a requirement relaxed. A response is\n * safe when the server grows more certain — a new field, a value that used to\n * be optional now always present. Collapsing both into one rule would\n * necessarily get one of them backwards.\n *\n * | change | request | response |\n * |-------------------------|---------|----------|\n * | field added (optional) | pass | pass |\n * | field added (required) | refuse | pass |\n * | field removed | pass | refuse |\n * | required → optional | pass | refuse |\n * | optional → required | refuse | pass |\n * | type changed | refuse | refuse |\n */\n\nimport { stableStringify } from './stable-json';\nimport type {\n ContractDocument,\n ContractOperation,\n ContractRequest,\n ContractViolation,\n JsonSchema,\n} from './types';\n\n/** Which side of the wire a schema sits on. The direction optional runs. */\ntype Side = 'request' | 'response';\n\nexport interface DocumentComparison\n{\n violations: ContractViolation[];\n\n /**\n * Operations present in the baseline and gone from the current contract.\n *\n * Removal is not decided here: whether an operation may go depends on\n * whether any released app still calls it, which is a separate check.\n */\n removedOperations: string[];\n}\n\nconst REQUEST_SECTIONS = ['params', 'query', 'body', 'formData', 'headers', 'cookies'] as const;\n\n/**\n * The schema's own keywords, with the structural ones removed.\n *\n * Everything left — `type`, `format`, `enum`, `anyOf`, `minLength` — is\n * compared verbatim, so a narrowed constraint counts as a type change. That\n * refuses more than the table's \"type changed\" row strictly requires, and it\n * refuses in the recoverable direction: a build that stops is fixed by cutting\n * a new contract version, while a break that passes reaches a shipped app.\n */\nfunction leafSignature(schema: JsonSchema): string\n{\n const { properties, required, items, ...rest } = schema;\n\n return stableStringify(rest);\n}\n\nfunction propertiesOf(schema: JsonSchema): Record<string, JsonSchema>\n{\n const properties = schema.properties;\n\n if (!properties || typeof properties !== 'object')\n {\n return {};\n }\n\n return properties as Record<string, JsonSchema>;\n}\n\nfunction requiredOf(schema: JsonSchema): Set<string>\n{\n const required = schema.required;\n\n if (!Array.isArray(required))\n {\n return new Set();\n }\n\n return new Set(required.filter((name): name is string => typeof name === 'string'));\n}\n\n/**\n * Would a client that sends nothing here now be refused?\n *\n * Only this level counts. A required field nested inside an optional object is\n * required *if* that object is sent, and a client that never sends it is fine.\n */\nfunction requiresClientToSend(schema: JsonSchema): boolean\n{\n if (schema.type !== 'object')\n {\n return true;\n }\n\n return requiredOf(schema).size > 0;\n}\n\ninterface CompareContext\n{\n operation: string;\n side: Side;\n violations: ContractViolation[];\n}\n\nfunction report(\n ctx: CompareContext,\n kind: ContractViolation['kind'],\n location: string,\n detail: string,\n): void\n{\n ctx.violations.push({ kind, operation: ctx.operation, location, detail });\n}\n\n/**\n * Compare one schema position across versions.\n *\n * `before`/`after` may be absent: a whole request section can appear or vanish,\n * and so can a nested field.\n */\nfunction compareSchema(\n before: JsonSchema | undefined,\n after: JsonSchema | undefined,\n location: string,\n ctx: CompareContext,\n): void\n{\n if (!before && !after)\n {\n return;\n }\n\n if (before && !after)\n {\n if (ctx.side === 'response')\n {\n report(ctx, 'response.field-removed', location, 'the response no longer carries this, and old clients read it');\n }\n\n // A request the server stopped reading is a request old clients may still send.\n return;\n }\n\n if (!before && after)\n {\n if (ctx.side === 'request' && requiresClientToSend(after))\n {\n report(\n ctx,\n 'request.required-field-added',\n location,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n\n // A response that carries more is a response old clients ignore the rest of.\n return;\n }\n\n const from = before!;\n const to = after!;\n\n if (leafSignature(from) !== leafSignature(to))\n {\n report(\n ctx,\n ctx.side === 'request' ? 'request.type-changed' : 'response.type-changed',\n location,\n `the declared type changed: ${leafSignature(from)} → ${leafSignature(to)}`,\n );\n\n return;\n }\n\n compareProperties(from, to, location, ctx);\n compareSchema(\n from.items as JsonSchema | undefined,\n to.items as JsonSchema | undefined,\n `${location}[]`,\n ctx,\n );\n}\n\nfunction compareProperties(from: JsonSchema, to: JsonSchema, location: string, ctx: CompareContext): void\n{\n const beforeProperties = propertiesOf(from);\n const afterProperties = propertiesOf(to);\n const beforeRequired = requiredOf(from);\n const afterRequired = requiredOf(to);\n\n for (const [name, beforeSchema] of Object.entries(beforeProperties))\n {\n const where = `${location}.${name}`;\n const afterSchema = afterProperties[name];\n\n compareSchema(beforeSchema, afterSchema, where, ctx);\n\n if (!afterSchema)\n {\n continue;\n }\n\n const wasRequired = beforeRequired.has(name);\n const isRequired = afterRequired.has(name);\n\n if (ctx.side === 'request' && !wasRequired && isRequired)\n {\n report(ctx, 'request.field-became-required', where, 'clients that never sent this are now refused');\n }\n\n if (ctx.side === 'response' && wasRequired && !isRequired)\n {\n report(ctx, 'response.field-became-optional', where, 'clients that counted on this always arriving now break');\n }\n }\n\n // An added field is judged at its own level only. Optional means an old\n // client that omits it still passes, whatever the field contains.\n for (const name of Object.keys(afterProperties))\n {\n if (beforeProperties[name])\n {\n continue;\n }\n\n if (ctx.side === 'request' && afterRequired.has(name))\n {\n report(\n ctx,\n 'request.required-field-added',\n `${location}.${name}`,\n 'this is new and mandatory, so every already-released client is refused',\n );\n }\n }\n}\n\nfunction compareRequestSections(\n before: ContractRequest,\n after: ContractRequest,\n prefix: string,\n ctx: CompareContext,\n): void\n{\n for (const section of REQUEST_SECTIONS)\n {\n compareSchema(before[section], after[section], `${prefix}.${section}`, ctx);\n }\n}\n\n/** Compare one operation that exists on both sides. */\nexport function compareOperation(before: ContractOperation, after: ContractOperation): ContractViolation[]\n{\n const violations: ContractViolation[] = [];\n\n if (before.path !== after.path)\n {\n violations.push({\n kind: 'operation.path-changed',\n operation: before.name,\n detail: `path moved ${before.path} → ${after.path}; released clients call the old one`,\n });\n }\n\n if (before.method !== after.method)\n {\n violations.push({\n kind: 'operation.method-changed',\n operation: before.name,\n detail: `method changed ${before.method} → ${after.method}; released clients send the old one`,\n });\n }\n\n const requestCtx: CompareContext = { operation: before.name, side: 'request', violations };\n compareRequestSections(before.request, after.request, 'request', requestCtx);\n compareRequestSections(before.interceptor, after.interceptor, 'interceptor', requestCtx);\n\n compareSchema(\n before.response,\n after.response,\n 'response',\n { operation: before.name, side: 'response', violations },\n );\n\n return violations;\n}\n\n/**\n * Compare a released contract against the one this build produced.\n *\n * Operations that are new in `after` need no check — nothing has been promised\n * about them yet.\n */\nexport function compareDocuments(before: ContractDocument, after: ContractDocument): DocumentComparison\n{\n const current = new Map(after.operations.map(operation => [operation.name, operation]));\n const violations: ContractViolation[] = [];\n const removedOperations: string[] = [];\n\n for (const operation of before.operations)\n {\n const now = current.get(operation.name);\n\n if (!now)\n {\n removedOperations.push(operation.name);\n continue;\n }\n\n violations.push(...compareOperation(operation, now));\n }\n\n return { violations, removedOperations };\n}\n","/**\n * Released snapshots\n *\n * `contracts/released/<version>.json` is what a version actually promised. It is\n * written once, at release, and never touched again — the gate compares against\n * the newest one.\n *\n * Comparing against the newest one alone is only sound if no release is missing\n * a snapshot: compatibility is transitive through the chain, and a gap in the\n * chain silently widens what passes. That is why cutting a release writes a\n * snapshot rather than offering to.\n *\n * Each snapshot carries the SHA-256 of its own document, so a hand-edited\n * snapshot fails the gate instead of quietly moving the baseline.\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { stableDigest, stableStringifyPretty } from './stable-json';\nimport type { ContractDocument, ContractSnapshot } from './types';\n\nexport class ContractSnapshotError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractSnapshotError';\n }\n}\n\nexport const CURRENT_FILENAME = 'current.json';\nexport const RELEASED_DIRNAME = 'released';\nexport const USAGE_DIRNAME = 'usage';\n\nexport function currentPath(contractsDir: string): string\n{\n return join(contractsDir, CURRENT_FILENAME);\n}\n\nexport function releasedDir(contractsDir: string): string\n{\n return join(contractsDir, RELEASED_DIRNAME);\n}\n\nexport function usageDir(contractsDir: string): string\n{\n return join(contractsDir, USAGE_DIRNAME);\n}\n\n/**\n * Order two versions the way a release line runs.\n *\n * Numeric identifiers compare numerically (so 1.10.0 follows 1.9.0), and a\n * pre-release sorts before the release it leads to.\n */\nexport function compareVersions(a: string, b: string): number\n{\n const [aCore, aPre] = splitVersion(a);\n const [bCore, bPre] = splitVersion(b);\n\n for (let i = 0; i < 3; i++)\n {\n if (aCore[i] !== bCore[i])\n {\n return aCore[i] - bCore[i];\n }\n }\n\n if (aPre === bPre)\n {\n return 0;\n }\n\n if (aPre === undefined)\n {\n return 1;\n }\n\n if (bPre === undefined)\n {\n return -1;\n }\n\n return comparePreRelease(aPre, bPre);\n}\n\nfunction splitVersion(version: string): [number[], string | undefined]\n{\n const [core, ...rest] = version.split('-');\n const parts = core.split('.').map(part => Number.parseInt(part, 10));\n\n if (parts.length !== 3 || parts.some(part => !Number.isInteger(part) || part < 0))\n {\n throw new ContractSnapshotError(`\"${version}\" is not a version of the form major.minor.patch`);\n }\n\n return [parts, rest.length > 0 ? rest.join('-') : undefined];\n}\n\nfunction comparePreRelease(a: string, b: string): number\n{\n const aParts = a.split('.');\n const bParts = b.split('.');\n\n for (let i = 0; i < Math.max(aParts.length, bParts.length); i++)\n {\n const left = aParts[i];\n const right = bParts[i];\n\n if (left === undefined)\n {\n return -1;\n }\n\n if (right === undefined)\n {\n return 1;\n }\n\n if (left === right)\n {\n continue;\n }\n\n const leftNumeric = /^\\d+$/.test(left);\n const rightNumeric = /^\\d+$/.test(right);\n\n if (leftNumeric && rightNumeric)\n {\n return Number(left) - Number(right);\n }\n\n if (leftNumeric !== rightNumeric)\n {\n return leftNumeric ? -1 : 1;\n }\n\n return left < right ? -1 : 1;\n }\n\n return 0;\n}\n\nexport interface SnapshotFile\n{\n version: string;\n file: string;\n}\n\n/** Every released snapshot, oldest first. */\nexport function listSnapshots(contractsDir: string): SnapshotFile[]\n{\n const dir = releasedDir(contractsDir);\n\n if (!existsSync(dir))\n {\n return [];\n }\n\n return readdirSync(dir)\n .filter(name => name.endsWith('.json'))\n .map(name => ({ version: name.slice(0, -'.json'.length), file: join(dir, name) }))\n .sort((a, b) => compareVersions(a.version, b.version));\n}\n\n/** The snapshot the gate compares against, or undefined when nothing is released yet. */\nexport function newestSnapshot(contractsDir: string): SnapshotFile | undefined\n{\n const snapshots = listSnapshots(contractsDir);\n\n return snapshots[snapshots.length - 1];\n}\n\n/**\n * Read a snapshot and check its digest.\n *\n * A mismatch means the file was edited after release. The baseline is the whole\n * point of the gate, so an edited baseline is refused rather than trusted.\n */\nexport function readSnapshot(file: string): ContractSnapshot\n{\n let parsed: Partial<ContractSnapshot>;\n\n try\n {\n parsed = JSON.parse(readFileSync(file, 'utf-8')) as Partial<ContractSnapshot>;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(`${file} could not be read: ${message}`);\n }\n\n if (typeof parsed.version !== 'string' || typeof parsed.sha256 !== 'string' || !parsed.document)\n {\n throw new ContractSnapshotError(`${file} is not a contract snapshot: expected version, sha256 and document`);\n }\n\n const digest = stableDigest(parsed.document);\n\n if (digest !== parsed.sha256)\n {\n throw new ContractSnapshotError(\n `${file} was edited after release: it records sha256 ${parsed.sha256} but its document hashes to ${digest}`,\n );\n }\n\n return { version: parsed.version, sha256: parsed.sha256, document: parsed.document as ContractDocument };\n}\n\n/**\n * Write the snapshot for a release.\n *\n * The version comes from the document, which took it from `.contractVersion()`\n * on the router. The filename follows the declaration rather than being told\n * separately what to say — a second place to state the version is a second place\n * for it to be wrong.\n *\n * Refuses to overwrite: a published version's promise does not change, a\n * mistake becomes a new version.\n */\nexport function writeSnapshot(contractsDir: string, document: ContractDocument): string\n{\n const version = document.contractVersion;\n\n if (!version)\n {\n throw new ContractSnapshotError(\n 'The contract declares no version, so a release cannot be named. '\n + 'Add .contractVersion(\"x.y.z\") to the router before cutting one.',\n );\n }\n compareVersions(version, version);\n\n const dir = releasedDir(contractsDir);\n const file = join(dir, `${version}.json`);\n\n if (existsSync(file))\n {\n throw new ContractSnapshotError(\n `${file} already exists. A released version's contract is never rewritten — cut a new version instead.`,\n );\n }\n\n // Releases have to reach the newest snapshot in order. The gate compares\n // against the newest one alone, which is only sound while the chain has no\n // gaps — a snapshot filled in behind the newest one is never compared to\n // anything, and quietly widens what passes.\n const newest = newestSnapshot(contractsDir);\n\n if (newest && compareVersions(version, newest.version) <= 0)\n {\n throw new ContractSnapshotError(\n `${version} is not newer than the released ${newest.version}. `\n + 'The gate compares against the newest snapshot, so filling one in behind it would never be checked.',\n );\n }\n\n mkdirSync(dir, { recursive: true });\n\n const snapshot: ContractSnapshot = { version, sha256: stableDigest(document), document };\n writeFileSync(file, stableStringifyPretty(snapshot), 'utf-8');\n\n return file;\n}\n\n/** Read `contracts/current.json`. */\nexport function readCurrentDocument(contractsDir: string): ContractDocument\n{\n const file = currentPath(contractsDir);\n\n try\n {\n return JSON.parse(readFileSync(file, 'utf-8')) as ContractDocument;\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n throw new ContractSnapshotError(\n `${file} could not be read: ${message}. Run the @spfn/core:contract generator first.`,\n );\n }\n}\n\n/** Write `contracts/current.json`. Returns true when the file changed. */\nexport function writeCurrentDocument(contractsDir: string, document: ContractDocument): boolean\n{\n const file = currentPath(contractsDir);\n const content = stableStringifyPretty(document);\n const existing = existsSync(file) ? readFileSync(file, 'utf-8') : undefined;\n\n if (existing === content)\n {\n return false;\n }\n\n mkdirSync(contractsDir, { recursive: true });\n writeFileSync(file, content, 'utf-8');\n\n return true;\n}\n","/**\n * Usage files — who still calls an operation\n *\n * A released app is compiled and shipped; the server cannot ask it what it\n * calls. So each released client writes down the operations it uses, and those\n * files are what a removal is judged against:\n * `contracts/usage/<platform>-<appVersion>.json`.\n *\n * The one rule this file exists to hold: **an unreadable file and \"nobody calls\n * it\" are not the same answer.** An empty scan result reading as a pass is how\n * a removal check quietly stops checking anything. Every failure to read is a\n * refusal that names the file.\n */\n\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** One released client's declared call list. */\nexport interface UsageRecord\n{\n platform: string;\n appVersion: string;\n operations: string[];\n\n /** File this came from, for messages. */\n file: string;\n}\n\nexport type UsageReadResult =\n | { decidable: true; records: UsageRecord[] }\n | { decidable: false; reason: string };\n\nfunction parseRecord(file: string, raw: string): UsageRecord\n{\n const parsed = JSON.parse(raw) as Partial<UsageRecord>;\n\n if (typeof parsed.platform !== 'string' || parsed.platform.length === 0)\n {\n throw new Error('\"platform\" must be a non-empty string');\n }\n\n if (typeof parsed.appVersion !== 'string' || parsed.appVersion.length === 0)\n {\n throw new Error('\"appVersion\" must be a non-empty string');\n }\n\n if (!Array.isArray(parsed.operations) || parsed.operations.some(name => typeof name !== 'string'))\n {\n throw new Error('\"operations\" must be an array of operation names');\n }\n\n return {\n platform: parsed.platform,\n appVersion: parsed.appVersion,\n operations: parsed.operations,\n file,\n };\n}\n\n/**\n * Read every usage file under `usageDir`.\n *\n * Returns undecidable — never an empty pass — when the directory is missing,\n * holds no usage file, or holds one that cannot be read.\n */\nexport function readUsageRecords(usageDir: string): UsageReadResult\n{\n let entries: string[];\n\n try\n {\n if (!statSync(usageDir).isDirectory())\n {\n return { decidable: false, reason: `${usageDir} is not a directory` };\n }\n\n entries = readdirSync(usageDir).filter(name => name.endsWith('.json')).sort();\n }\n catch\n {\n return { decidable: false, reason: `${usageDir} does not exist` };\n }\n\n if (entries.length === 0)\n {\n return { decidable: false, reason: `${usageDir} holds no usage file` };\n }\n\n const records: UsageRecord[] = [];\n\n for (const entry of entries)\n {\n const file = join(usageDir, entry);\n\n try\n {\n records.push(parseRecord(file, readFileSync(file, 'utf-8')));\n }\n catch (error)\n {\n const message = error instanceof Error ? error.message : String(error);\n\n return { decidable: false, reason: `${file} could not be read: ${message}` };\n }\n }\n\n return { decidable: true, records };\n}\n\n/** Which released clients still call `operation`. */\nexport function callersOf(operation: string, records: UsageRecord[]): UsageRecord[]\n{\n return records.filter(record => record.operations.includes(operation));\n}\n","/**\n * The gate\n *\n * Puts the pieces together: read the newest released snapshot, compare this\n * build's contract against it, and decide removals against what released\n * clients still call.\n *\n * Nothing released yet is a pass — with a warning. \"This is the first contract\"\n * and \"the release that should have written a snapshot didn't\" produce the same\n * empty directory, and only a person can tell them apart.\n */\n\nimport { compareDocuments } from './compare';\nimport { newestSnapshot, readSnapshot, usageDir } from './snapshot';\nimport { callersOf, readUsageRecords } from './usage';\nimport type { ContractDocument, ContractViolation } from './types';\n\nexport interface ContractCheckResult\n{\n /** Version compared against, absent when nothing is released yet. */\n baselineVersion?: string;\n\n violations: ContractViolation[];\n\n /** Things a person should look at that do not stop the build. */\n warnings: string[];\n}\n\nexport function checkContract(contractsDir: string, current: ContractDocument): ContractCheckResult\n{\n const baseline = newestSnapshot(contractsDir);\n\n if (!baseline)\n {\n return {\n violations: [],\n warnings: [\n 'No released contract snapshot found, so nothing was compared. '\n + 'This is expected for a first contract, and a mistake if a release forgot to write one.',\n ],\n };\n }\n\n let previous: ContractDocument;\n\n try\n {\n previous = readSnapshot(baseline.file).document;\n }\n catch (error)\n {\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [{\n kind: 'snapshot.digest-mismatch',\n detail: error instanceof Error ? error.message : String(error),\n }],\n };\n }\n\n const { violations, removedOperations } = compareDocuments(previous, current);\n\n return {\n baselineVersion: baseline.version,\n warnings: [],\n violations: [...violations, ...judgeRemovals(contractsDir, removedOperations)],\n };\n}\n\n/**\n * Decide whether removed operations may go.\n *\n * Only reached when something was actually removed — an app that removes\n * nothing never needs a usage file to exist.\n */\nfunction judgeRemovals(contractsDir: string, removedOperations: string[]): ContractViolation[]\n{\n if (removedOperations.length === 0)\n {\n return [];\n }\n\n const usage = readUsageRecords(usageDir(contractsDir));\n\n if (!usage.decidable)\n {\n return [{\n kind: 'usage.undecidable',\n detail:\n `${removedOperations.join(', ')} would be removed, but no released client's call list could be read `\n + `(${usage.reason}). Not knowing who calls an operation is not the same as knowing nobody does.`,\n }];\n }\n\n const violations: ContractViolation[] = [];\n\n for (const operation of removedOperations)\n {\n const callers = callersOf(operation, usage.records);\n\n if (callers.length === 0)\n {\n continue;\n }\n\n violations.push({\n kind: 'usage.still-called',\n operation,\n detail:\n 'still called by '\n + callers.map(caller => `${caller.platform} ${caller.appVersion}`).join(', '),\n });\n }\n\n return violations;\n}\n\n/** Render violations as the message a failing build prints. */\nexport function formatViolations(violations: ContractViolation[]): string\n{\n return violations\n .map((violation) =>\n {\n const where = [violation.operation, violation.location].filter(Boolean).join(' ');\n\n return ` - [${violation.kind}]${where ? ` ${where}` : ''}: ${violation.detail}`;\n })\n .join('\\n');\n}\n"]}
|
|
@@ -164,4 +164,4 @@ declare function defineMiddlewareFactory<TName extends string, TArgs extends unk
|
|
|
164
164
|
*/
|
|
165
165
|
type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> = T[number]['_name'];
|
|
166
166
|
|
|
167
|
-
export { type ExtractMiddlewareNames as E, type
|
|
167
|
+
export { type ExtractMiddlewareNames as E, type NamedMiddlewareFactory as N, defineMiddlewareFactory as a, type NamedMiddleware as b, defineMiddleware as d };
|
package/dist/errors/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { format } from 'util';
|
|
2
|
+
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __export = (target, all) => {
|
|
3
5
|
for (var name in all)
|
|
@@ -68,7 +70,8 @@ var ErrorRegistry = class _ErrorRegistry {
|
|
|
68
70
|
if (!ErrorClass) {
|
|
69
71
|
throw new Error(`Unknown error type: ${data.__type}`);
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
const { __type: _type, error: _envelope, ...fields } = data;
|
|
74
|
+
return new ErrorClass(fields);
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
77
|
* Try to deserialize error, return null if type unknown
|
|
@@ -112,7 +115,394 @@ __export(http_errors_exports, {
|
|
|
112
115
|
ValidationError: () => ValidationError
|
|
113
116
|
});
|
|
114
117
|
|
|
118
|
+
// src/logger/types.ts
|
|
119
|
+
var LOG_LEVEL_PRIORITY = {
|
|
120
|
+
debug: 0,
|
|
121
|
+
info: 1,
|
|
122
|
+
warn: 2,
|
|
123
|
+
error: 3,
|
|
124
|
+
fatal: 4
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/logger/formatters.ts
|
|
128
|
+
var SENSITIVE_KEYS = [
|
|
129
|
+
"password",
|
|
130
|
+
"passwd",
|
|
131
|
+
"pwd",
|
|
132
|
+
"secret",
|
|
133
|
+
"token",
|
|
134
|
+
"apikey",
|
|
135
|
+
"api_key",
|
|
136
|
+
"accesstoken",
|
|
137
|
+
"access_token",
|
|
138
|
+
"refreshtoken",
|
|
139
|
+
"refresh_token",
|
|
140
|
+
"authorization",
|
|
141
|
+
"auth",
|
|
142
|
+
"cookie",
|
|
143
|
+
"session",
|
|
144
|
+
"sessionid",
|
|
145
|
+
"session_id",
|
|
146
|
+
"privatekey",
|
|
147
|
+
"private_key",
|
|
148
|
+
"creditcard",
|
|
149
|
+
"credit_card",
|
|
150
|
+
"cardnumber",
|
|
151
|
+
"card_number",
|
|
152
|
+
"cvv",
|
|
153
|
+
"ssn",
|
|
154
|
+
"pin"
|
|
155
|
+
];
|
|
156
|
+
var MASKED_VALUE = "***MASKED***";
|
|
157
|
+
function isSensitiveKey(key) {
|
|
158
|
+
const lowerKey = key.toLowerCase();
|
|
159
|
+
return SENSITIVE_KEYS.some((sensitive) => lowerKey.includes(sensitive));
|
|
160
|
+
}
|
|
161
|
+
function maskSensitiveData(data, seen = /* @__PURE__ */ new WeakSet()) {
|
|
162
|
+
if (data === null || data === void 0) {
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
165
|
+
if (typeof data !== "object") {
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
if (seen.has(data)) {
|
|
169
|
+
return "[Circular]";
|
|
170
|
+
}
|
|
171
|
+
seen.add(data);
|
|
172
|
+
if (Array.isArray(data)) {
|
|
173
|
+
return data.map((item) => maskSensitiveData(item, seen));
|
|
174
|
+
}
|
|
175
|
+
const masked = {};
|
|
176
|
+
for (const [key, value] of Object.entries(data)) {
|
|
177
|
+
if (isSensitiveKey(key)) {
|
|
178
|
+
masked[key] = MASKED_VALUE;
|
|
179
|
+
} else if (typeof value === "object" && value !== null) {
|
|
180
|
+
masked[key] = maskSensitiveData(value, seen);
|
|
181
|
+
} else {
|
|
182
|
+
masked[key] = value;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return masked;
|
|
186
|
+
}
|
|
187
|
+
var COLORS = {
|
|
188
|
+
reset: "\x1B[0m",
|
|
189
|
+
bright: "\x1B[1m",
|
|
190
|
+
dim: "\x1B[2m",
|
|
191
|
+
// 로그 레벨 컬러
|
|
192
|
+
debug: "\x1B[36m",
|
|
193
|
+
// cyan
|
|
194
|
+
info: "\x1B[32m",
|
|
195
|
+
// green
|
|
196
|
+
warn: "\x1B[33m",
|
|
197
|
+
// yellow
|
|
198
|
+
error: "\x1B[31m",
|
|
199
|
+
// red
|
|
200
|
+
fatal: "\x1B[35m",
|
|
201
|
+
// magenta
|
|
202
|
+
// 추가 컬러
|
|
203
|
+
gray: "\x1B[90m"
|
|
204
|
+
};
|
|
205
|
+
function formatTimestampHuman(date) {
|
|
206
|
+
const year = date.getFullYear();
|
|
207
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
208
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
209
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
210
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
211
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
212
|
+
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
213
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
|
|
214
|
+
}
|
|
215
|
+
function formatError(error) {
|
|
216
|
+
const lines = [];
|
|
217
|
+
lines.push(`${error.name}: ${error.message}`);
|
|
218
|
+
if (error.stack) {
|
|
219
|
+
const stackLines = error.stack.split("\n").slice(1);
|
|
220
|
+
lines.push(...stackLines);
|
|
221
|
+
}
|
|
222
|
+
if (error.cause instanceof Error) {
|
|
223
|
+
lines.push(`Caused by: ${formatError(error.cause)}`);
|
|
224
|
+
} else if (error.cause !== void 0) {
|
|
225
|
+
lines.push(`Caused by: ${String(error.cause)}`);
|
|
226
|
+
}
|
|
227
|
+
return lines.join("\n");
|
|
228
|
+
}
|
|
229
|
+
function formatConsole(metadata, colorize = true) {
|
|
230
|
+
const parts = [];
|
|
231
|
+
const timestamp = formatTimestampHuman(metadata.timestamp);
|
|
232
|
+
if (colorize) {
|
|
233
|
+
parts.push(`${COLORS.gray}[${timestamp}]${COLORS.reset}`);
|
|
234
|
+
} else {
|
|
235
|
+
parts.push(`[${timestamp}]`);
|
|
236
|
+
}
|
|
237
|
+
const pid = process.pid;
|
|
238
|
+
if (colorize) {
|
|
239
|
+
parts.push(`${COLORS.dim}[pid=${pid}]${COLORS.reset}`);
|
|
240
|
+
} else {
|
|
241
|
+
parts.push(`[pid=${pid}]`);
|
|
242
|
+
}
|
|
243
|
+
if (metadata.module) {
|
|
244
|
+
if (colorize) {
|
|
245
|
+
parts.push(`${COLORS.dim}[module=${metadata.module}]${COLORS.reset}`);
|
|
246
|
+
} else {
|
|
247
|
+
parts.push(`[module=${metadata.module}]`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (metadata.context && Object.keys(metadata.context).length > 0) {
|
|
251
|
+
Object.entries(metadata.context).forEach(([key, value]) => {
|
|
252
|
+
let valueStr;
|
|
253
|
+
if (typeof value === "string") {
|
|
254
|
+
valueStr = value;
|
|
255
|
+
} else if (typeof value === "object" && value !== null) {
|
|
256
|
+
try {
|
|
257
|
+
valueStr = JSON.stringify(value);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
valueStr = "[circular]";
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
valueStr = String(value);
|
|
263
|
+
}
|
|
264
|
+
if (colorize) {
|
|
265
|
+
parts.push(`${COLORS.dim}[${key}=${valueStr}]${COLORS.reset}`);
|
|
266
|
+
} else {
|
|
267
|
+
parts.push(`[${key}=${valueStr}]`);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const levelStr = metadata.level.toUpperCase();
|
|
272
|
+
if (colorize) {
|
|
273
|
+
const color = COLORS[metadata.level];
|
|
274
|
+
parts.push(`${color}(${levelStr})${COLORS.reset}:`);
|
|
275
|
+
} else {
|
|
276
|
+
parts.push(`(${levelStr}):`);
|
|
277
|
+
}
|
|
278
|
+
if (colorize) {
|
|
279
|
+
parts.push(`${COLORS.bright}${metadata.message}${COLORS.reset}`);
|
|
280
|
+
} else {
|
|
281
|
+
parts.push(metadata.message);
|
|
282
|
+
}
|
|
283
|
+
let output = parts.join(" ");
|
|
284
|
+
if (metadata.error) {
|
|
285
|
+
output += "\n" + formatError(metadata.error);
|
|
286
|
+
}
|
|
287
|
+
return output;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/logger/logger.ts
|
|
291
|
+
var FORMAT_PATTERN = /%[sdifjoOc%]/;
|
|
292
|
+
var Logger = class _Logger {
|
|
293
|
+
config;
|
|
294
|
+
module;
|
|
295
|
+
constructor(config) {
|
|
296
|
+
this.config = config;
|
|
297
|
+
this.module = config.module;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Convert unknown error to Error object
|
|
301
|
+
*/
|
|
302
|
+
toError(error) {
|
|
303
|
+
if (error instanceof Error) return error;
|
|
304
|
+
if (typeof error === "string") return new Error(error);
|
|
305
|
+
if (typeof error === "object" && error !== null) {
|
|
306
|
+
return new Error(JSON.stringify(error));
|
|
307
|
+
}
|
|
308
|
+
return new Error(String(error));
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Check if value is a context object (not an error)
|
|
312
|
+
*/
|
|
313
|
+
isContext(value) {
|
|
314
|
+
if (typeof value !== "object" || value === null) return false;
|
|
315
|
+
if (value instanceof Error) return false;
|
|
316
|
+
const hasStack = "stack" in value && typeof value.stack === "string";
|
|
317
|
+
if (hasStack) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get current log level
|
|
324
|
+
*/
|
|
325
|
+
get level() {
|
|
326
|
+
return this.config.level;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Create child logger (per module)
|
|
330
|
+
*/
|
|
331
|
+
child(module) {
|
|
332
|
+
return new _Logger({
|
|
333
|
+
...this.config,
|
|
334
|
+
module
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Common log method with error/context detection
|
|
339
|
+
*/
|
|
340
|
+
logWithLevel(level, message, errorOrContext, context) {
|
|
341
|
+
if (errorOrContext !== void 0 && FORMAT_PATTERN.test(message)) {
|
|
342
|
+
this.log(level, format(message, errorOrContext), void 0, context);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (errorOrContext instanceof Error) {
|
|
346
|
+
this.log(level, message, errorOrContext, context);
|
|
347
|
+
} else if (errorOrContext !== void 0 && typeof errorOrContext === "object" && !this.isContext(errorOrContext)) {
|
|
348
|
+
this.log(level, message, this.toError(errorOrContext), context);
|
|
349
|
+
} else if (typeof errorOrContext === "string" || typeof errorOrContext === "number" || typeof errorOrContext === "boolean") {
|
|
350
|
+
this.log(level, message, this.toError(errorOrContext), context);
|
|
351
|
+
} else {
|
|
352
|
+
this.log(level, message, void 0, errorOrContext);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
debug(message, errorOrContext, context) {
|
|
356
|
+
this.logWithLevel("debug", message, errorOrContext, context);
|
|
357
|
+
}
|
|
358
|
+
info(message, errorOrContext, context) {
|
|
359
|
+
this.logWithLevel("info", message, errorOrContext, context);
|
|
360
|
+
}
|
|
361
|
+
warn(message, errorOrContext, context) {
|
|
362
|
+
this.logWithLevel("warn", message, errorOrContext, context);
|
|
363
|
+
}
|
|
364
|
+
error(message, errorOrContext, context) {
|
|
365
|
+
this.logWithLevel("error", message, errorOrContext, context);
|
|
366
|
+
}
|
|
367
|
+
fatal(message, errorOrContext, context) {
|
|
368
|
+
this.logWithLevel("fatal", message, errorOrContext, context);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Log processing (internal)
|
|
372
|
+
*/
|
|
373
|
+
log(level, message, error, context) {
|
|
374
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.config.level]) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const metadata = {
|
|
378
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
379
|
+
level,
|
|
380
|
+
message,
|
|
381
|
+
module: this.module,
|
|
382
|
+
error,
|
|
383
|
+
// Mask sensitive information in context to prevent credential leaks
|
|
384
|
+
context: context ? maskSensitiveData(context) : void 0
|
|
385
|
+
};
|
|
386
|
+
this.processTransports(metadata);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Process Transports
|
|
390
|
+
*/
|
|
391
|
+
processTransports(metadata) {
|
|
392
|
+
const promises = this.config.transports.filter((transport) => transport.enabled).map((transport) => this.safeTransportLog(transport, metadata));
|
|
393
|
+
Promise.all(promises).catch((error) => {
|
|
394
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
395
|
+
process.stderr.write(`[Logger] Transport error: ${errorMessage}
|
|
396
|
+
`);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Transport log (error-safe)
|
|
401
|
+
*/
|
|
402
|
+
async safeTransportLog(transport, metadata) {
|
|
403
|
+
try {
|
|
404
|
+
await transport.log(metadata);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
407
|
+
process.stderr.write(`[Logger] Transport "${transport.name}" failed: ${errorMessage}
|
|
408
|
+
`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Close all Transports
|
|
413
|
+
*/
|
|
414
|
+
async close() {
|
|
415
|
+
const closePromises = this.config.transports.filter((transport) => transport.close).map((transport) => transport.close());
|
|
416
|
+
await Promise.all(closePromises);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// src/logger/transports/console.ts
|
|
421
|
+
var ConsoleTransport = class {
|
|
422
|
+
name = "console";
|
|
423
|
+
level;
|
|
424
|
+
enabled;
|
|
425
|
+
colorize;
|
|
426
|
+
constructor(config) {
|
|
427
|
+
this.level = config.level;
|
|
428
|
+
this.enabled = config.enabled;
|
|
429
|
+
this.colorize = config.colorize ?? true;
|
|
430
|
+
}
|
|
431
|
+
async log(metadata) {
|
|
432
|
+
if (!this.enabled) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level]) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const message = formatConsole(metadata, this.colorize);
|
|
439
|
+
if (metadata.level === "warn" || metadata.level === "error" || metadata.level === "fatal") {
|
|
440
|
+
console.error(message);
|
|
441
|
+
} else {
|
|
442
|
+
console.log(message);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/logger/config.ts
|
|
448
|
+
function getConsoleConfig() {
|
|
449
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
450
|
+
return {
|
|
451
|
+
level: "debug",
|
|
452
|
+
enabled: true,
|
|
453
|
+
colorize: !isProduction
|
|
454
|
+
// Dev: colored output, Production: plain text
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function validateEnvironment() {
|
|
458
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
459
|
+
if (!nodeEnv) {
|
|
460
|
+
process.stderr.write(
|
|
461
|
+
"[Logger] Warning: NODE_ENV is not set. Defaulting to test environment.\n"
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function validateConfig() {
|
|
466
|
+
validateEnvironment();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/logger/factory.ts
|
|
470
|
+
function initializeTransports() {
|
|
471
|
+
const transports = [];
|
|
472
|
+
const consoleConfig = getConsoleConfig();
|
|
473
|
+
transports.push(new ConsoleTransport(consoleConfig));
|
|
474
|
+
return transports;
|
|
475
|
+
}
|
|
476
|
+
function getLogLevel() {
|
|
477
|
+
const envLevel = process.env.SPFN_LOG_LEVEL || process.env.NEXT_PUBLIC_SPFN_LOG_LEVEL || "info";
|
|
478
|
+
if (envLevel in LOG_LEVEL_PRIORITY) {
|
|
479
|
+
return envLevel;
|
|
480
|
+
}
|
|
481
|
+
process.stderr.write(
|
|
482
|
+
`[Logger] Invalid log level "${envLevel}", defaulting to "info"
|
|
483
|
+
`
|
|
484
|
+
);
|
|
485
|
+
return "info";
|
|
486
|
+
}
|
|
487
|
+
function initializeLogger() {
|
|
488
|
+
validateConfig();
|
|
489
|
+
return new Logger({
|
|
490
|
+
level: getLogLevel(),
|
|
491
|
+
transports: initializeTransports()
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
var logger = initializeLogger();
|
|
495
|
+
|
|
115
496
|
// src/errors/serializable-error.ts
|
|
497
|
+
var RESERVED_RESPONSE_KEYS = /* @__PURE__ */ new Set(["__type", "message", "error"]);
|
|
498
|
+
var AUTHORING_ENVIRONMENTS = /* @__PURE__ */ new Set(["local", "development", "test"]);
|
|
499
|
+
function refuseReservedKey(className, key) {
|
|
500
|
+
const detail = `${className} declares the reserved field "${key}". Reserved response keys: ${[...RESERVED_RESPONSE_KEYS].join(", ")}. Rename the field.`;
|
|
501
|
+
if (AUTHORING_ENVIRONMENTS.has(process.env.NODE_ENV ?? "local")) {
|
|
502
|
+
throw new Error(detail);
|
|
503
|
+
}
|
|
504
|
+
logger.error(`[SerializableError] ${detail} The field is omitted from the response.`);
|
|
505
|
+
}
|
|
116
506
|
var SerializableError = class extends Error {
|
|
117
507
|
/**
|
|
118
508
|
* Serialize error to JSON-compatible object
|
|
@@ -128,9 +518,14 @@ var SerializableError = class extends Error {
|
|
|
128
518
|
message: this.message
|
|
129
519
|
};
|
|
130
520
|
for (const key of Object.keys(this)) {
|
|
131
|
-
if (key
|
|
132
|
-
|
|
521
|
+
if (key === "name" || key === "stack" || key === "statusCode") {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (RESERVED_RESPONSE_KEYS.has(key)) {
|
|
525
|
+
refuseReservedKey(this.constructor.name, key);
|
|
526
|
+
continue;
|
|
133
527
|
}
|
|
528
|
+
json[key] = this[key];
|
|
134
529
|
}
|
|
135
530
|
return json;
|
|
136
531
|
}
|