@spfn/core 0.2.0-beta.52 → 0.2.0-beta.54
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/dist/{boss-Cxqc-Oiw.d.ts → boss-CEik0yq-.d.ts} +7 -0
- package/dist/cache/index.js +10 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/config/index.d.ts +285 -0
- package/dist/config/index.js +60 -1
- package/dist/config/index.js.map +1 -1
- package/dist/db/index.d.ts +57 -0
- package/dist/db/index.js +55 -8
- package/dist/db/index.js.map +1 -1
- package/dist/define-middleware-DuXD8Hvu.d.ts +167 -0
- package/dist/env/index.js +4 -3
- package/dist/env/index.js.map +1 -1
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.js +20 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +3 -3
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +2 -2
- package/dist/event/sse/index.d.ts +4 -4
- package/dist/event/sse/index.js +41 -16
- 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/event/ws/index.js +75 -16
- package/dist/event/ws/index.js.map +1 -1
- package/dist/job/index.d.ts +2 -2
- package/dist/job/index.js +4 -4
- package/dist/job/index.js.map +1 -1
- package/dist/middleware/index.d.ts +171 -2
- package/dist/middleware/index.js +1186 -10
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +10 -0
- package/dist/nextjs/server.js +115 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/route/index.d.ts +3 -165
- package/dist/server/index.d.ts +31 -5
- package/dist/server/index.js +172 -36
- package/dist/server/index.js.map +1 -1
- package/dist/{token-manager-C2Ag5-s8.d.ts → token-manager-jKD_EsSE.d.ts} +0 -1
- package/dist/{types-VpVQIsyB.d.ts → types-BFB72jbM.d.ts} +1 -1
- package/dist/{types-CKsmzaB8.d.ts → types-DVjf37yO.d.ts} +37 -1
- package/package.json +6 -6
package/dist/errors/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/errors/error-registry.ts","../../src/errors/http-errors.ts","../../src/errors/serializable-error.ts","../../src/errors/database-errors.ts","../../src/errors/error-utils.ts","../../src/errors/index.ts"],"names":[],"mappings":";;;;;;;AAmDO,IAAM,aAAA,GAAN,MAAM,cAAA,CACb;AAAA,EACY,MAAA,uBAAa,GAAA,EAA0C;AAAA,EAE/D,YAAY,aAAA,EACZ;AACI,IAAA,IAAI,aAAA,EACJ;AACI,MAAA,KAAA,MAAW,SAAS,aAAA,EACpB;AACI,QAAA,IAAI,iBAAiB,cAAA,EACrB;AACI,UAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AAAA,QACrB,CAAA,MAAA,IACS,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAC5B;AACI,UAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AAAA,QACrB,CAAA,MAEA;AACI,UAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AAAA,QACrB;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,OAAO,KAAA,EACP;AACI,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EACvB;AACI,MAAA,KAAA,MAAW,cAAc,KAAA,EACzB;AACI,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,IAAA,EAAM,UAAU,CAAA;AAAA,MAC/C;AAAA,IACJ,CAAA,MAEA;AACI,MAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,KAAK,CAAA;AAAA,IACrC;AAEA,IAAA,OAAO,IAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,QAAA,EACP;AACI,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,UAAU,CAAA,IAAK,SAAS,MAAA,EAC1C;AACI,MAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,EAAM,UAAU,CAAA;AAAA,IACpC;AAEA,IAAA,OAAO,IAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,IAAA,EACJ;AACI,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAY,IAAA,EACZ;AACI,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,KAAK,MAAM,CAAA;AAE9C,IAAA,IAAI,CAAC,UAAA,EACL;AACI,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,IAAA,CAAK,MAAM,CAAA,CAAE,CAAA;AAAA,IACxD;AAGA,IAAA,OAAO,IAAI,WAAW,IAAI,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,IAAA,EACf;AACI,IAAA,IAAI,CAAC,KAAK,MAAA,IAAU,CAAC,KAAK,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,EACzC;AACI,MAAA,OAAO,IAAA;AAAA,IACX;AAEA,IAAA,IACA;AACI,MAAA,OAAO,IAAA,CAAK,YAAY,IAAW,CAAA;AAAA,IACvC,CAAA,CAAA,MAEA;AACI,MAAA,OAAO,IAAA;AAAA,IACX;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAA,GACA;AACI,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA;AAAA,EACxC;AACJ;;;AChMA,IAAA,mBAAA,GAAA;AAAA,QAAA,CAAA,mBAAA,EAAA;AAAA,EAAA,eAAA,EAAA,MAAA,eAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,cAAA,EAAA,MAAA,cAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,mBAAA,EAAA,MAAA,mBAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,uBAAA,EAAA,MAAA,uBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,iBAAA,EAAA,MAAA,iBAAA;AAAA,EAAA,wBAAA,EAAA,MAAA,wBAAA;AAAA,EAAA,yBAAA,EAAA,MAAA,yBAAA;AAAA,EAAA,eAAA,EAAA,MAAA;AAAA,CAAA,CAAA;;;AC8CO,IAAe,iBAAA,GAAf,cAAyC,KAAA,CAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcI,MAAA,GACA;AACI,IAAA,MAAM,IAAA,GAAwB;AAAA,MAC1B,MAAA,EAAQ,KAAK,WAAA,CAAY,IAAA;AAAA,MACzB,SAAS,IAAA,CAAK;AAAA,KAClB;AAGA,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,EAClC;AAEI,MAAA,IAAI,QAAQ,MAAA,IAAU,GAAA,KAAQ,aAAa,GAAA,KAAQ,OAAA,IAAW,QAAQ,YAAA,EACtE;AACI,QAAA,IAAA,CAAK,GAAG,CAAA,GAAK,IAAA,CAA4C,GAAG,CAAA;AAAA,MAChE;AAAA,IACJ;AAEA,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;;;ADlEO,IAAM,SAAA,GAAN,cAAwB,iBAAA,CAC/B;AAAA,EACoB,UAAA;AAAA,EACT,OAAA;AAAA,EAEP,YAAY,IAAA,EAKZ;AACI,IAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAElB,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AACZ,IAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AAEvB,IAAA,IAAI,KAAK,OAAA,EACT;AACI,MAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AAAA,IACxB;AAEA,IAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,EAClD;AACJ;AAOO,IAAM,eAAA,GAAN,cAA8B,SAAA,CACrC;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,aAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EAChB;AACJ;AAQO,IAAM,eAAA,GAAN,cAA8B,SAAA,CACrC;AAAA,EACI,MAAA;AAAA,EAEA,YAAY,IAAA,EAKZ;AACI,IAAA,KAAA,CAAM;AAAA,MACF,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAEZ,IAAA,IAAI,KAAK,MAAA,EACT;AACI,MAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AAAA,IACvB;AAAA,EACJ;AACJ;AAOO,IAAM,iBAAA,GAAN,cAAgC,SAAA,CACvC;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,yBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EAChB;AACJ;AAOO,IAAM,cAAA,GAAN,cAA6B,SAAA,CACpC;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,kBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EAChB;AACJ;AAOO,IAAM,aAAA,GAAN,cAA4B,SAAA,CACnC;AAAA,EACI,QAAA;AAAA,EAEA,WAAA,CAAY,IAAA,GAA+E,EAAC,EAC5F;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,oBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAEZ,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA,IAAA,CAAK,WAAW,IAAA,CAAK,QAAA;AAAA,IACzB;AAAA,EACJ;AACJ;AAOO,IAAM,aAAA,GAAN,cAA4B,SAAA,CACnC;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,mBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EAChB;AACJ;AAOO,IAAM,SAAA,GAAN,cAAwB,SAAA,CAC/B;AAAA,EACI,QAAA;AAAA,EAEA,WAAA,CAAY,IAAA,GAA+E,EAAC,EAC5F;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,8BAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AAEZ,IAAA,IAAI,KAAK,QAAA,EACT;AACI,MAAA,IAAA,CAAK,WAAW,IAAA,CAAK,QAAA;AAAA,IACzB;AAAA,EACJ;AACJ;AAOO,IAAM,oBAAA,GAAN,cAAmC,SAAA,CAC1C;AAAA,EACI,UAAA;AAAA,EAEA,WAAA,CAAY,IAAA,GAIR,EAAC,EACL;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,mBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AAEZ,IAAA,IAAI,KAAK,UAAA,EACT;AACI,MAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AAAA,IAC3B;AAAA,EACJ;AACJ;AAOO,IAAM,mBAAA,GAAN,cAAkC,SAAA,CACzC;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,uBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EAChB;AACJ;AAOO,IAAM,yBAAA,GAAN,cAAwC,SAAA,CAC/C;AAAA,EACI,SAAA;AAAA,EACA,cAAA;AAAA,EAEA,WAAA,CAAY,IAAA,GAKR,EAAC,EACL;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,wBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,2BAAA;AAEZ,IAAA,IAAI,KAAK,SAAA,EACT;AACI,MAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AAAA,IAC1B;AAEA,IAAA,IAAI,KAAK,cAAA,EACT;AACI,MAAA,IAAA,CAAK,iBAAiB,IAAA,CAAK,cAAA;AAAA,IAC/B;AAAA,EACJ;AACJ;AAOO,IAAM,wBAAA,GAAN,cAAuC,SAAA,CAC9C;AAAA,EACI,WAAA,CAAY,IAAA,GAA4D,EAAC,EACzE;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,sBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,0BAAA;AAAA,EAChB;AACJ;AAOO,IAAM,uBAAA,GAAN,cAAsC,SAAA,CAC7C;AAAA,EACI,UAAA;AAAA,EAEA,WAAA,CAAY,IAAA,GAIR,EAAC,EACL;AACI,IAAA,KAAA,CAAM;AAAA,MACF,OAAA,EAAS,KAAK,OAAA,IAAW,qBAAA;AAAA,MACzB,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AAED,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AAEZ,IAAA,IAAI,KAAK,UAAA,EACT;AACI,MAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AAAA,IAC3B;AAAA,EACJ;AACJ;;;AE3UA,IAAA,uBAAA,GAAA;AAAA,QAAA,CAAA,uBAAA,EAAA;AAAA,EAAA,eAAA,EAAA,MAAA,eAAA;AAAA,EAAA,wBAAA,EAAA,MAAA,wBAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,mBAAA,EAAA,MAAA,mBAAA;AAAA,EAAA,mBAAA,EAAA,MAAA,mBAAA;AAAA,EAAA,UAAA,EAAA,MAAA,UAAA;AAAA,EAAA,gBAAA,EAAA,MAAA;AAAA,CAAA,CAAA;AAeO,IAAM,aAAA,GAAN,cACK,iBAAA,CACZ;AAAA,EACoB,UAAA;AAAA,EACA,OAAA;AAAA,EAEhB,YAAY,IAAA,EAKZ;AACI,IAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,KAAK,UAAA,IAAc,GAAA;AACrC,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpB,IAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,EAClD;AACJ;AAOO,IAAM,eAAA,GAAN,cAA8B,aAAA,CACrC;AAAA,EACI,YAAY,IAAA,EACZ;AACI,IAAA,KAAA,CAAM,EAAE,SAAS,IAAA,CAAK,OAAA,EAAS,YAAY,GAAA,EAAK,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AACvE,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EAChB;AACJ;AAOO,IAAM,UAAA,GAAN,cAAyB,aAAA,CAChC;AAAA,EACI,YAAY,IAAA,EAKZ;AACI,IAAA,KAAA,CAAM;AAAA,MACF,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,UAAA,EAAY,KAAK,UAAA,IAAc,GAAA;AAAA,MAC/B,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AAAA,EAChB;AACJ;AAOO,IAAM,mBAAA,GAAN,cAAkC,UAAA,CACzC;AAAA,EACoB,QAAA;AAAA,EACA,EAAA;AAAA,EAEhB,YAAY,IAAA,EACZ;AACI,IAAA,KAAA,CAAM;AAAA,MACF,SAAS,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,SAAA,EAAY,KAAK,EAAE,CAAA,UAAA,CAAA;AAAA,MAC5C,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,EAAE,QAAA,EAAU,KAAK,QAAA,EAAU,EAAA,EAAI,KAAK,EAAA;AAAG,KACnD,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AACZ,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,QAAA;AACrB,IAAA,IAAA,CAAK,KAAK,IAAA,CAAK,EAAA;AAAA,EACnB;AACJ;AAQO,IAAM,wBAAA,GAAN,cAAuC,UAAA,CAC9C;AAAA,EACI,YAAY,IAAA,EACZ;AACI,IAAA,KAAA,CAAM,EAAE,SAAS,IAAA,CAAK,OAAA,EAAS,YAAY,GAAA,EAAK,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AACvE,IAAA,IAAA,CAAK,IAAA,GAAO,0BAAA;AAAA,EAChB;AACJ;AAOO,IAAM,gBAAA,GAAN,cAA+B,aAAA,CACtC;AAAA,EACI,YAAY,IAAA,EAKZ;AACI,IAAA,KAAA,CAAM;AAAA,MACF,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,UAAA,EAAY,KAAK,UAAA,IAAc,GAAA;AAAA,MAC/B,SAAS,IAAA,CAAK;AAAA,KACjB,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EAChB;AACJ;AAOO,IAAM,aAAA,GAAN,cAA4B,gBAAA,CACnC;AAAA,EACI,YAAY,IAAA,EACZ;AACI,IAAA,KAAA,CAAM,EAAE,SAAS,IAAA,CAAK,OAAA,EAAS,YAAY,GAAA,EAAK,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AACvE,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EAChB;AACJ;AAOO,IAAM,mBAAA,GAAN,cAAkC,UAAA,CACzC;AAAA,EACoB,KAAA;AAAA,EACA,KAAA;AAAA,EAEhB,YAAY,IAAA,EACZ;AACI,IAAA,KAAA,CAAM;AAAA,MACF,SAAS,CAAA,EAAG,IAAA,CAAK,KAAK,CAAA,EAAA,EAAK,KAAK,KAAK,CAAA,gBAAA,CAAA;AAAA,MACrC,UAAA,EAAY,GAAA;AAAA,MACZ,SAAS,EAAE,KAAA,EAAO,KAAK,KAAA,EAAO,KAAA,EAAO,KAAK,KAAA;AAAM,KACnD,CAAA;AACD,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AACZ,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK,KAAA;AAClB,IAAA,IAAA,CAAK,QAAQ,IAAA,CAAK,KAAA;AAAA,EACtB;AACJ;;;AC1JO,SAAS,gBAAgB,KAAA,EAChC;AACI,EAAA,OAAO,KAAA,YAAiB,aAAA;AAC5B;AAKO,SAAS,YAAY,KAAA,EAC5B;AACI,EAAA,OAAO,KAAA,YAAiB,SAAA;AAC5B;AAKO,SAAS,cAAc,KAAA,EAC9B;AACI,EAAA,OACI,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,YAAA,IAAgB,KAAA,IAChB,OAAQ,KAAA,CAAc,UAAA,KAAe,QAAA;AAE7C;;;ACyCO,IAAM,aAAA,GAAgB,IAAI,aAAA;AAGjC,aAAA,CAAc,MAAA,CAAO;AAAA,EACjB,SAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,iBAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,oBAAA;AAAA,EACA,yBAAA;AAAA,EACA,wBAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACJ,CAAC,CAAA;AAGD,aAAA,CAAc,MAAA,CAAO;AAAA,EACjB,aAAA;AAAA,EACA,eAAA;AAAA,EACA,UAAA;AAAA,EACA,mBAAA;AAAA,EACA,wBAAA;AAAA,EACA,gBAAA;AAAA,EACA,aAAA;AAAA,EACA;AACJ,CAAC,CAAA","file":"index.js","sourcesContent":["/**\n * Error Registry\n *\n * Central registry for serializable error types.\n * Enables automatic error deserialization on the client side.\n */\n\nimport type { SerializableError } from './serializable-error';\n\n/**\n * Error constructor type\n */\nexport type SerializableErrorConstructor = new (data: any) => SerializableError;\n\n/**\n * Input type for ErrorRegistry constructor\n */\nexport type ErrorRegistryInput =\n | SerializableErrorConstructor\n | SerializableErrorConstructor[]\n | ErrorRegistry;\n\n/**\n * Error registry for serialization/deserialization\n *\n * @example\n * ```typescript\n * // Create registry with initial errors\n * const registry = new ErrorRegistry([\n * errorRegistry, // Another ErrorRegistry\n * PaymentFailedError, // Single error class\n * [RefundError, OrderError], // Array of error classes\n * ]);\n *\n * // Or create empty and append\n * const registry = new ErrorRegistry();\n * registry\n * .append(ValidationError)\n * .append([PaymentFailedError, RefundError]);\n *\n * // Deserialize from JSON\n * const error = registry.deserialize({\n * __type: 'PaymentFailedError',\n * message: 'Payment failed',\n * transactionId: 'tx_123',\n * reason: 'insufficient_funds'\n * });\n *\n * // error instanceof PaymentFailedError === true\n * ```\n */\nexport class ErrorRegistry\n{\n private errors = new Map<string, SerializableErrorConstructor>();\n\n constructor(initialErrors?: ErrorRegistryInput[])\n {\n if (initialErrors)\n {\n for (const input of initialErrors)\n {\n if (input instanceof ErrorRegistry)\n {\n this.concat(input);\n }\n else if (Array.isArray(input))\n {\n this.append(input);\n }\n else\n {\n this.append(input);\n }\n }\n }\n }\n\n /**\n * Append error class(es) to the registry\n *\n * @param ErrorClass - Single error constructor\n * @returns This registry for chaining\n */\n append(ErrorClass: SerializableErrorConstructor): this;\n\n /**\n * Append error class(es) to the registry\n *\n * @param ErrorClasses - Array of error constructors\n * @returns This registry for chaining\n */\n append(ErrorClasses: SerializableErrorConstructor[]): this;\n\n /**\n * Append error class(es) to the registry\n *\n * @param input - Error constructor or array of constructors\n * @returns This registry for chaining\n */\n append(input: SerializableErrorConstructor | SerializableErrorConstructor[]): this\n {\n if (Array.isArray(input))\n {\n for (const ErrorClass of input)\n {\n this.errors.set(ErrorClass.name, ErrorClass);\n }\n }\n else\n {\n this.errors.set(input.name, input);\n }\n\n return this;\n }\n\n /**\n * Concatenate another ErrorRegistry into this one\n *\n * @param registry - Another ErrorRegistry to merge\n * @returns This registry for chaining\n */\n concat(registry: ErrorRegistry): this\n {\n for (const [name, ErrorClass] of registry.errors)\n {\n this.errors.set(name, ErrorClass);\n }\n\n return this;\n }\n\n /**\n * Check if error type is registered\n *\n * @param name - Error class name\n */\n has(name: string): boolean\n {\n return this.errors.has(name);\n }\n\n /**\n * Deserialize error from JSON data\n *\n * @param data - Serialized error data with __type field\n * @returns Deserialized error instance\n * @throws Error if error type is not registered\n */\n deserialize(data: { __type: string; [key: string]: any }): SerializableError\n {\n const ErrorClass = this.errors.get(data.__type);\n\n if (!ErrorClass)\n {\n throw new Error(`Unknown error type: ${data.__type}`);\n }\n\n // Pass entire data object to constructor\n return new ErrorClass(data);\n }\n\n /**\n * Try to deserialize error, return null if type unknown\n *\n * @param data - Serialized error data\n * @returns Deserialized error or null\n */\n tryDeserialize(data: { __type?: string; [key: string]: any }): SerializableError | null\n {\n if (!data.__type || !this.has(data.__type))\n {\n return null;\n }\n\n try\n {\n return this.deserialize(data as any);\n }\n catch\n {\n return null;\n }\n }\n\n /**\n * Get all registered error types\n */\n getRegisteredTypes(): string[]\n {\n return Array.from(this.errors.keys());\n }\n}\n","/**\n * HTTP Error Classes\n *\n * Standard HTTP error classes for API responses\n * All errors are serializable for type-safe client-side error handling\n */\n\nimport { SerializableError } from './serializable-error';\n\n/**\n * Base HTTP Error\n *\n * Base class for all HTTP-related errors\n */\nexport class HttpError extends SerializableError\n{\n public readonly statusCode: number;\n public details?: Record<string, unknown>;\n\n constructor(data: {\n message: string;\n statusCode: number;\n details?: Record<string, unknown>;\n })\n {\n super(data.message);\n\n this.name = 'HttpError';\n this.statusCode = data.statusCode;\n\n if (data.details)\n {\n this.details = data.details;\n }\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Bad Request Error (400)\n *\n * Generic bad request - malformed syntax, invalid parameters, etc.\n */\nexport class BadRequestError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Bad request',\n statusCode: 400,\n details: data.details,\n });\n\n this.name = 'BadRequestError';\n }\n}\n\n/**\n * Validation Error (400)\n *\n * Input validation failure (request params, query, body)\n * Used by define-route system for automatic validation\n */\nexport class ValidationError extends HttpError\n{\n fields?: Array<{ path: string; message: string; value?: any }>;\n\n constructor(data: {\n message: string;\n fields?: Array<{ path: string; message: string; value?: any }>;\n details?: Record<string, any>;\n })\n {\n super({\n message: data.message,\n statusCode: 400,\n details: data.details,\n });\n\n this.name = 'ValidationError';\n\n if (data.fields)\n {\n this.fields = data.fields;\n }\n }\n}\n\n/**\n * Unauthorized Error (401)\n *\n * Authentication required or authentication failed\n */\nexport class UnauthorizedError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Authentication required',\n statusCode: 401,\n details: data.details,\n });\n\n this.name = 'UnauthorizedError';\n }\n}\n\n/**\n * Forbidden Error (403)\n *\n * Authenticated but lacks permission to access resource\n */\nexport class ForbiddenError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Access forbidden',\n statusCode: 403,\n details: data.details,\n });\n\n this.name = 'ForbiddenError';\n }\n}\n\n/**\n * Not Found Error (404)\n *\n * Requested resource does not exist\n */\nexport class NotFoundError extends HttpError\n{\n resource?: string;\n\n constructor(data: { message?: string; resource?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource not found',\n statusCode: 404,\n details: data.details,\n });\n\n this.name = 'NotFoundError';\n\n if (data.resource)\n {\n this.resource = data.resource;\n }\n }\n}\n\n/**\n * Conflict Error (409)\n *\n * Generic conflict - resource state conflict, concurrent modification, etc.\n */\nexport class ConflictError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource conflict',\n statusCode: 409,\n details: data.details,\n });\n\n this.name = 'ConflictError';\n }\n}\n\n/**\n * Gone Error (410)\n *\n * Resource permanently deleted and no longer available\n */\nexport class GoneError extends HttpError\n{\n resource?: string;\n\n constructor(data: { message?: string; resource?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource permanently deleted',\n statusCode: 410,\n details: data.details,\n });\n\n this.name = 'GoneError';\n\n if (data.resource)\n {\n this.resource = data.resource;\n }\n }\n}\n\n/**\n * Too Many Requests Error (429)\n *\n * Rate limit exceeded\n */\nexport class TooManyRequestsError extends HttpError\n{\n retryAfter?: number;\n\n constructor(data: {\n message?: string;\n retryAfter?: number;\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Too many requests',\n statusCode: 429,\n details: data.details,\n });\n\n this.name = 'TooManyRequestsError';\n\n if (data.retryAfter)\n {\n this.retryAfter = data.retryAfter;\n }\n }\n}\n\n/**\n * Internal Server Error (500)\n *\n * Generic server error when no specific error type applies\n */\nexport class InternalServerError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Internal server error',\n statusCode: 500,\n details: data.details,\n });\n\n this.name = 'InternalServerError';\n }\n}\n\n/**\n * Unsupported Media Type Error (415)\n *\n * Media type not supported - invalid file type, content type, etc.\n */\nexport class UnsupportedMediaTypeError extends HttpError\n{\n mediaType?: string;\n supportedTypes?: string[];\n\n constructor(data: {\n message?: string;\n mediaType?: string;\n supportedTypes?: string[];\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Unsupported media type',\n statusCode: 415,\n details: data.details,\n });\n\n this.name = 'UnsupportedMediaTypeError';\n\n if (data.mediaType)\n {\n this.mediaType = data.mediaType;\n }\n\n if (data.supportedTypes)\n {\n this.supportedTypes = data.supportedTypes;\n }\n }\n}\n\n/**\n * Unprocessable Entity Error (422)\n *\n * Request is well-formed but contains semantic errors\n */\nexport class UnprocessableEntityError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Unprocessable entity',\n statusCode: 422,\n details: data.details,\n });\n\n this.name = 'UnprocessableEntityError';\n }\n}\n\n/**\n * Service Unavailable Error (503)\n *\n * Service temporarily unavailable (maintenance, overload, etc.)\n */\nexport class ServiceUnavailableError extends HttpError\n{\n retryAfter?: number;\n\n constructor(data: {\n message?: string;\n retryAfter?: number;\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Service unavailable',\n statusCode: 503,\n details: data.details,\n });\n\n this.name = 'ServiceUnavailableError';\n\n if (data.retryAfter)\n {\n this.retryAfter = data.retryAfter;\n }\n }\n}\n","/**\n * Serializable Error Base Class\n *\n * Base class for errors that can be serialized/deserialized across HTTP boundary.\n * Automatically serializes public fields for transmission to client.\n */\n\n/**\n * Serialized error format for JSON transmission\n */\nexport interface SerializedError\n{\n __type: string;\n message: string;\n [key: string]: unknown;\n}\n\n/**\n * Base class for all serializable errors\n *\n * Features:\n * - Auto-serialization of public fields via toJSON()\n * - Constructor accepts object with all fields\n * - Type-safe error handling with instanceof\n *\n * @example\n * ```typescript\n * class PaymentFailedError extends SerializableError\n * {\n * readonly statusCode = 402;\n * transactionId!: string;\n * reason!: 'insufficient_funds' | 'card_declined';\n *\n * constructor(data: {\n * message: string;\n * transactionId: string;\n * reason: 'insufficient_funds' | 'card_declined';\n * })\n * {\n * super(data.message);\n * this.name = 'PaymentFailedError';\n * Object.assign(this, data);\n * }\n * }\n * ```\n */\nexport abstract class SerializableError extends Error\n{\n /**\n * HTTP status code for this error type\n */\n abstract readonly statusCode: number;\n\n /**\n * Serialize error to JSON-compatible object\n *\n * Automatically includes:\n * - __type: Constructor name for deserialization\n * - message: Error message\n * - All public instance properties (except name, stack)\n */\n toJSON(): SerializedError\n {\n const json: SerializedError = {\n __type: this.constructor.name,\n message: this.message,\n };\n\n // Extract all public instance properties\n for (const key of Object.keys(this))\n {\n // Skip Error built-ins and statusCode (inferred from type)\n if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== 'statusCode')\n {\n json[key] = (this as unknown as Record<string, unknown>)[key];\n }\n }\n\n return json;\n }\n}\n","/**\n * Database Error Classes\n *\n * Type-safe error handling with custom error class hierarchy\n * Mapped to HTTP status codes for API responses\n * All errors extend SerializableError for consistent JSON serialization\n */\n\nimport { SerializableError } from './serializable-error';\n\n/**\n * Base Database Error\n *\n * Base class for all database-related errors\n */\nexport class DatabaseError<TDetails extends Record<string, unknown> = Record<string, unknown>>\n extends SerializableError\n{\n public readonly statusCode: number;\n public readonly details?: TDetails;\n\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: TDetails;\n })\n {\n super(data.message);\n this.name = 'DatabaseError';\n this.statusCode = data.statusCode ?? 500;\n this.details = data.details;\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Connection Error (503 Service Unavailable)\n *\n * Database connection failure, connection pool exhaustion, etc.\n */\nexport class ConnectionError extends DatabaseError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 503, details: data.details });\n this.name = 'ConnectionError';\n }\n}\n\n/**\n * Query Error (500 Internal Server Error)\n *\n * SQL query execution failure, syntax errors, etc.\n */\nexport class QueryError extends DatabaseError\n{\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: Record<string, unknown>;\n })\n {\n super({\n message: data.message,\n statusCode: data.statusCode ?? 500,\n details: data.details,\n });\n this.name = 'QueryError';\n }\n}\n\n/**\n * Entity Not Found Error (404 Not Found)\n *\n * Database entity does not exist\n */\nexport class EntityNotFoundError extends QueryError\n{\n public readonly resource: string;\n public readonly id: string | number;\n\n constructor(data: { resource: string; id: string | number })\n {\n super({\n message: `${data.resource} with id ${data.id} not found`,\n statusCode: 404,\n details: { resource: data.resource, id: data.id },\n });\n this.name = 'EntityNotFoundError';\n this.resource = data.resource;\n this.id = data.id;\n }\n}\n\n/**\n * Constraint Violation Error (400 Bad Request)\n *\n * Database constraint violation (NOT NULL, CHECK, FOREIGN KEY, etc.)\n * This is different from HTTP ValidationError which validates request input\n */\nexport class ConstraintViolationError extends QueryError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 400, details: data.details });\n this.name = 'ConstraintViolationError';\n }\n}\n\n/**\n * Transaction Error (500 Internal Server Error)\n *\n * Transaction start/commit/rollback failure\n */\nexport class TransactionError extends DatabaseError\n{\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: Record<string, unknown>;\n })\n {\n super({\n message: data.message,\n statusCode: data.statusCode ?? 500,\n details: data.details,\n });\n this.name = 'TransactionError';\n }\n}\n\n/**\n * Deadlock Error (409 Conflict)\n *\n * Database deadlock detected\n */\nexport class DeadlockError extends TransactionError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 409, details: data.details });\n this.name = 'DeadlockError';\n }\n}\n\n/**\n * Duplicate Entry Error (409 Conflict)\n *\n * Unique constraint violation (e.g., duplicate email)\n */\nexport class DuplicateEntryError extends QueryError\n{\n public readonly field: string;\n public readonly value: string | number;\n\n constructor(data: { field: string; value: string | number })\n {\n super({\n message: `${data.field} '${data.value}' already exists`,\n statusCode: 409,\n details: { field: data.field, value: data.value },\n });\n this.name = 'DuplicateEntryError';\n this.field = data.field;\n this.value = data.value;\n }\n}\n","/**\n * Error Utility Functions\n *\n * Generic error type checking utilities\n */\n\nimport { DatabaseError } from './database-errors';\nimport { HttpError } from './http-errors';\n\n/**\n * Check if error is a DatabaseError\n */\nexport function isDatabaseError(error: unknown): error is DatabaseError\n{\n return error instanceof DatabaseError;\n}\n\n/**\n * Check if error is an HttpError\n */\nexport function isHttpError(error: unknown): error is HttpError\n{\n return error instanceof HttpError;\n}\n\n/**\n * Check if error has a statusCode property\n */\nexport function hasStatusCode(error: unknown): error is { statusCode: number }\n{\n return (\n typeof error === 'object' &&\n error !== null &&\n 'statusCode' in error &&\n typeof (error as any).statusCode === 'number'\n );\n}\n","/**\n * Error Module Exports\n *\n * Entry point for error handling module with serialization support\n */\n\n// Core Error Registry\n// Pre-configured registry with all built-in HTTP and Database errors\nimport { ErrorRegistry } from './error-registry';\nimport {\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n} from './http-errors';\nimport {\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n} from './database-errors';\n\n// Base Classes\nexport { SerializableError } from './serializable-error';\nexport type { SerializedError } from './serializable-error';\nexport { ErrorRegistry } from './error-registry';\nexport type { SerializableErrorConstructor, ErrorRegistryInput } from './error-registry';\n\n// Database Error Classes\nexport {\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n} from './database-errors';\n\n// HTTP Error Classes\nexport {\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n} from './http-errors';\n\n// Error Utilities\nexport {\n isDatabaseError,\n isHttpError,\n hasStatusCode,\n} from './error-utils';\n\nexport const errorRegistry = new ErrorRegistry();\n\n// HTTP Errors\nerrorRegistry.append([\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n]);\n\n// Database Errors\nerrorRegistry.append([\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n]);\n\nexport * as HttpErrors from './http-errors';\nexport * as DatabaseErrors from './database-errors';\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/errors/error-registry.ts","../../src/errors/http-errors.ts","../../src/errors/serializable-error.ts","../../src/errors/database-errors.ts","../../src/errors/error-utils.ts","../../src/errors/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Error Registry\n *\n * Central registry for serializable error types.\n * Enables automatic error deserialization on the client side.\n */\n\nimport type { SerializableError } from './serializable-error';\n\n/**\n * Error constructor type\n */\nexport type SerializableErrorConstructor = new (data: any) => SerializableError;\n\n/**\n * Input type for ErrorRegistry constructor\n */\nexport type ErrorRegistryInput =\n | SerializableErrorConstructor\n | SerializableErrorConstructor[]\n | ErrorRegistry;\n\n/**\n * Error registry for serialization/deserialization\n *\n * @example\n * ```typescript\n * // Create registry with initial errors\n * const registry = new ErrorRegistry([\n * errorRegistry, // Another ErrorRegistry\n * PaymentFailedError, // Single error class\n * [RefundError, OrderError], // Array of error classes\n * ]);\n *\n * // Or create empty and append\n * const registry = new ErrorRegistry();\n * registry\n * .append(ValidationError)\n * .append([PaymentFailedError, RefundError]);\n *\n * // Deserialize from JSON\n * const error = registry.deserialize({\n * __type: 'PaymentFailedError',\n * message: 'Payment failed',\n * transactionId: 'tx_123',\n * reason: 'insufficient_funds'\n * });\n *\n * // error instanceof PaymentFailedError === true\n * ```\n */\nexport class ErrorRegistry\n{\n private errors = new Map<string, SerializableErrorConstructor>();\n\n constructor(initialErrors?: ErrorRegistryInput[])\n {\n if (initialErrors)\n {\n for (const input of initialErrors)\n {\n if (input instanceof ErrorRegistry)\n {\n this.concat(input);\n }\n else if (Array.isArray(input))\n {\n this.append(input);\n }\n else\n {\n this.append(input);\n }\n }\n }\n }\n\n /**\n * Append error class(es) to the registry\n *\n * @param ErrorClass - Single error constructor\n * @returns This registry for chaining\n */\n append(ErrorClass: SerializableErrorConstructor): this;\n\n /**\n * Append error class(es) to the registry\n *\n * @param ErrorClasses - Array of error constructors\n * @returns This registry for chaining\n */\n append(ErrorClasses: SerializableErrorConstructor[]): this;\n\n /**\n * Append error class(es) to the registry\n *\n * @param input - Error constructor or array of constructors\n * @returns This registry for chaining\n */\n append(input: SerializableErrorConstructor | SerializableErrorConstructor[]): this\n {\n if (Array.isArray(input))\n {\n for (const ErrorClass of input)\n {\n this.errors.set(ErrorClass.name, ErrorClass);\n }\n }\n else\n {\n this.errors.set(input.name, input);\n }\n\n return this;\n }\n\n /**\n * Concatenate another ErrorRegistry into this one\n *\n * @param registry - Another ErrorRegistry to merge\n * @returns This registry for chaining\n */\n concat(registry: ErrorRegistry): this\n {\n for (const [name, ErrorClass] of registry.errors)\n {\n this.errors.set(name, ErrorClass);\n }\n\n return this;\n }\n\n /**\n * Check if error type is registered\n *\n * @param name - Error class name\n */\n has(name: string): boolean\n {\n return this.errors.has(name);\n }\n\n /**\n * Deserialize error from JSON data\n *\n * @param data - Serialized error data with __type field\n * @returns Deserialized error instance\n * @throws Error if error type is not registered\n */\n deserialize(data: { __type: string; [key: string]: any }): SerializableError\n {\n const ErrorClass = this.errors.get(data.__type);\n\n if (!ErrorClass)\n {\n throw new Error(`Unknown error type: ${data.__type}`);\n }\n\n // Pass entire data object to constructor\n return new ErrorClass(data);\n }\n\n /**\n * Try to deserialize error, return null if type unknown\n *\n * @param data - Serialized error data\n * @returns Deserialized error or null\n */\n tryDeserialize(data: { __type?: string; [key: string]: any }): SerializableError | null\n {\n if (!data.__type || !this.has(data.__type))\n {\n return null;\n }\n\n try\n {\n return this.deserialize(data as any);\n }\n catch\n {\n return null;\n }\n }\n\n /**\n * Get all registered error types\n */\n getRegisteredTypes(): string[]\n {\n return Array.from(this.errors.keys());\n }\n}\n","/**\n * HTTP Error Classes\n *\n * Standard HTTP error classes for API responses\n * All errors are serializable for type-safe client-side error handling\n */\n\nimport { SerializableError } from './serializable-error';\n\n/**\n * Base HTTP Error\n *\n * Base class for all HTTP-related errors\n */\nexport class HttpError extends SerializableError\n{\n public readonly statusCode: number;\n public details?: Record<string, unknown>;\n\n constructor(data: {\n message: string;\n statusCode: number;\n details?: Record<string, unknown>;\n })\n {\n super(data.message);\n\n this.name = 'HttpError';\n this.statusCode = data.statusCode;\n\n if (data.details)\n {\n this.details = data.details;\n }\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Bad Request Error (400)\n *\n * Generic bad request - malformed syntax, invalid parameters, etc.\n */\nexport class BadRequestError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Bad request',\n statusCode: 400,\n details: data.details,\n });\n\n this.name = 'BadRequestError';\n }\n}\n\n/**\n * Validation Error (400)\n *\n * Input validation failure (request params, query, body)\n * Used by define-route system for automatic validation\n */\nexport class ValidationError extends HttpError\n{\n fields?: Array<{ path: string; message: string; value?: any }>;\n\n constructor(data: {\n message: string;\n fields?: Array<{ path: string; message: string; value?: any }>;\n details?: Record<string, any>;\n })\n {\n super({\n message: data.message,\n statusCode: 400,\n details: data.details,\n });\n\n this.name = 'ValidationError';\n\n if (data.fields)\n {\n this.fields = data.fields;\n }\n }\n}\n\n/**\n * Unauthorized Error (401)\n *\n * Authentication required or authentication failed\n */\nexport class UnauthorizedError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Authentication required',\n statusCode: 401,\n details: data.details,\n });\n\n this.name = 'UnauthorizedError';\n }\n}\n\n/**\n * Forbidden Error (403)\n *\n * Authenticated but lacks permission to access resource\n */\nexport class ForbiddenError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Access forbidden',\n statusCode: 403,\n details: data.details,\n });\n\n this.name = 'ForbiddenError';\n }\n}\n\n/**\n * Not Found Error (404)\n *\n * Requested resource does not exist\n */\nexport class NotFoundError extends HttpError\n{\n resource?: string;\n\n constructor(data: { message?: string; resource?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource not found',\n statusCode: 404,\n details: data.details,\n });\n\n this.name = 'NotFoundError';\n\n if (data.resource)\n {\n this.resource = data.resource;\n }\n }\n}\n\n/**\n * Conflict Error (409)\n *\n * Generic conflict - resource state conflict, concurrent modification, etc.\n */\nexport class ConflictError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource conflict',\n statusCode: 409,\n details: data.details,\n });\n\n this.name = 'ConflictError';\n }\n}\n\n/**\n * Gone Error (410)\n *\n * Resource permanently deleted and no longer available\n */\nexport class GoneError extends HttpError\n{\n resource?: string;\n\n constructor(data: { message?: string; resource?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Resource permanently deleted',\n statusCode: 410,\n details: data.details,\n });\n\n this.name = 'GoneError';\n\n if (data.resource)\n {\n this.resource = data.resource;\n }\n }\n}\n\n/**\n * Too Many Requests Error (429)\n *\n * Rate limit exceeded\n */\nexport class TooManyRequestsError extends HttpError\n{\n retryAfter?: number;\n\n constructor(data: {\n message?: string;\n retryAfter?: number;\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Too many requests',\n statusCode: 429,\n details: data.details,\n });\n\n this.name = 'TooManyRequestsError';\n\n if (data.retryAfter)\n {\n this.retryAfter = data.retryAfter;\n }\n }\n}\n\n/**\n * Internal Server Error (500)\n *\n * Generic server error when no specific error type applies\n */\nexport class InternalServerError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Internal server error',\n statusCode: 500,\n details: data.details,\n });\n\n this.name = 'InternalServerError';\n }\n}\n\n/**\n * Unsupported Media Type Error (415)\n *\n * Media type not supported - invalid file type, content type, etc.\n */\nexport class UnsupportedMediaTypeError extends HttpError\n{\n mediaType?: string;\n supportedTypes?: string[];\n\n constructor(data: {\n message?: string;\n mediaType?: string;\n supportedTypes?: string[];\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Unsupported media type',\n statusCode: 415,\n details: data.details,\n });\n\n this.name = 'UnsupportedMediaTypeError';\n\n if (data.mediaType)\n {\n this.mediaType = data.mediaType;\n }\n\n if (data.supportedTypes)\n {\n this.supportedTypes = data.supportedTypes;\n }\n }\n}\n\n/**\n * Unprocessable Entity Error (422)\n *\n * Request is well-formed but contains semantic errors\n */\nexport class UnprocessableEntityError extends HttpError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Unprocessable entity',\n statusCode: 422,\n details: data.details,\n });\n\n this.name = 'UnprocessableEntityError';\n }\n}\n\n/**\n * Service Unavailable Error (503)\n *\n * Service temporarily unavailable (maintenance, overload, etc.)\n */\nexport class ServiceUnavailableError extends HttpError\n{\n retryAfter?: number;\n\n constructor(data: {\n message?: string;\n retryAfter?: number;\n details?: Record<string, any>;\n } = {})\n {\n super({\n message: data.message || 'Service unavailable',\n statusCode: 503,\n details: data.details,\n });\n\n this.name = 'ServiceUnavailableError';\n\n if (data.retryAfter)\n {\n this.retryAfter = data.retryAfter;\n }\n }\n}\n","/**\n * Serializable Error Base Class\n *\n * Base class for errors that can be serialized/deserialized across HTTP boundary.\n * Automatically serializes public fields for transmission to client.\n */\n\n/**\n * Serialized error format for JSON transmission\n */\nexport interface SerializedError\n{\n __type: string;\n message: string;\n [key: string]: unknown;\n}\n\n/**\n * Base class for all serializable errors\n *\n * Features:\n * - Auto-serialization of public fields via toJSON()\n * - Constructor accepts object with all fields\n * - Type-safe error handling with instanceof\n *\n * @example\n * ```typescript\n * class PaymentFailedError extends SerializableError\n * {\n * readonly statusCode = 402;\n * transactionId!: string;\n * reason!: 'insufficient_funds' | 'card_declined';\n *\n * constructor(data: {\n * message: string;\n * transactionId: string;\n * reason: 'insufficient_funds' | 'card_declined';\n * })\n * {\n * super(data.message);\n * this.name = 'PaymentFailedError';\n * Object.assign(this, data);\n * }\n * }\n * ```\n */\nexport abstract class SerializableError extends Error\n{\n /**\n * HTTP status code for this error type\n */\n abstract readonly statusCode: number;\n\n /**\n * Serialize error to JSON-compatible object\n *\n * Automatically includes:\n * - __type: Constructor name for deserialization\n * - message: Error message\n * - All public instance properties (except name, stack)\n */\n toJSON(): SerializedError\n {\n const json: SerializedError = {\n __type: this.constructor.name,\n message: this.message,\n };\n\n // Extract all public instance properties\n for (const key of Object.keys(this))\n {\n // Skip Error built-ins and statusCode (inferred from type)\n if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== 'statusCode')\n {\n json[key] = (this as unknown as Record<string, unknown>)[key];\n }\n }\n\n return json;\n }\n}\n","/**\n * Database Error Classes\n *\n * Type-safe error handling with custom error class hierarchy\n * Mapped to HTTP status codes for API responses\n * All errors extend SerializableError for consistent JSON serialization\n */\n\nimport { SerializableError } from './serializable-error';\n\n/**\n * Base Database Error\n *\n * Base class for all database-related errors\n */\nexport class DatabaseError<TDetails extends Record<string, unknown> = Record<string, unknown>>\n extends SerializableError\n{\n public readonly statusCode: number;\n public readonly details?: TDetails;\n\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: TDetails;\n })\n {\n super(data.message);\n this.name = 'DatabaseError';\n this.statusCode = data.statusCode ?? 500;\n this.details = data.details;\n Error.captureStackTrace(this, this.constructor);\n }\n\n /**\n * Whether this error's message/details are derived from the raw database\n * driver (SQL text, table/column/constraint names, parameter values) and\n * therefore must NOT be exposed to clients in production. Defined as a\n * prototype getter so it is never serialized into the response by toJSON().\n * Subclasses with a safe, constructed message override this to `false`.\n */\n get internal(): boolean\n {\n return true;\n }\n}\n\n/**\n * Connection Error (503 Service Unavailable)\n *\n * Database connection failure, connection pool exhaustion, etc.\n */\nexport class ConnectionError extends DatabaseError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 503, details: data.details });\n this.name = 'ConnectionError';\n }\n}\n\n/**\n * Query Error (500 Internal Server Error)\n *\n * SQL query execution failure, syntax errors, etc.\n */\nexport class QueryError extends DatabaseError\n{\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: Record<string, unknown>;\n })\n {\n super({\n message: data.message,\n statusCode: data.statusCode ?? 500,\n details: data.details,\n });\n this.name = 'QueryError';\n }\n}\n\n/**\n * Entity Not Found Error (404 Not Found)\n *\n * Database entity does not exist\n */\nexport class EntityNotFoundError extends QueryError\n{\n public readonly resource: string;\n public readonly id: string | number;\n\n constructor(data: { resource: string; id: string | number })\n {\n super({\n message: `${data.resource} with id ${data.id} not found`,\n statusCode: 404,\n details: { resource: data.resource, id: data.id },\n });\n this.name = 'EntityNotFoundError';\n this.resource = data.resource;\n this.id = data.id;\n }\n\n // Message/details are constructed from the resource + id, not driver text\n override get internal(): boolean\n {\n return false;\n }\n}\n\n/**\n * Constraint Violation Error (400 Bad Request)\n *\n * Database constraint violation (NOT NULL, CHECK, FOREIGN KEY, etc.)\n * This is different from HTTP ValidationError which validates request input\n */\nexport class ConstraintViolationError extends QueryError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 400, details: data.details });\n this.name = 'ConstraintViolationError';\n }\n}\n\n/**\n * Transaction Error (500 Internal Server Error)\n *\n * Transaction start/commit/rollback failure\n */\nexport class TransactionError extends DatabaseError\n{\n constructor(data: {\n message: string;\n statusCode?: number;\n details?: Record<string, unknown>;\n })\n {\n super({\n message: data.message,\n statusCode: data.statusCode ?? 500,\n details: data.details,\n });\n this.name = 'TransactionError';\n }\n}\n\n/**\n * Deadlock Error (409 Conflict)\n *\n * Database deadlock detected\n */\nexport class DeadlockError extends TransactionError\n{\n constructor(data: { message: string; details?: Record<string, unknown> })\n {\n super({ message: data.message, statusCode: 409, details: data.details });\n this.name = 'DeadlockError';\n }\n}\n\n/**\n * Duplicate Entry Error (409 Conflict)\n *\n * Unique constraint violation (e.g., duplicate email)\n */\nexport class DuplicateEntryError extends QueryError\n{\n public readonly field: string;\n public readonly value: string | number;\n\n constructor(data: { field: string; value: string | number })\n {\n // The client-facing message/details name the field but NOT the value: echoing\n // the value back (e.g. an email) confirms it exists, an enumeration aid. The\n // raw value stays on the instance for server-side handling/logging.\n super({\n message: `${data.field} already exists`,\n statusCode: 409,\n details: { field: data.field },\n });\n this.name = 'DuplicateEntryError';\n this.field = data.field;\n this.value = data.value;\n }\n\n // Field-only message/details — safe to surface to the client (not internal)\n override get internal(): boolean\n {\n return false;\n }\n}\n","/**\n * Error Utility Functions\n *\n * Generic error type checking utilities\n */\n\nimport { DatabaseError } from './database-errors';\nimport { HttpError } from './http-errors';\n\n/**\n * Check if error is a DatabaseError\n */\nexport function isDatabaseError(error: unknown): error is DatabaseError\n{\n return error instanceof DatabaseError;\n}\n\n/**\n * Check if error is an HttpError\n */\nexport function isHttpError(error: unknown): error is HttpError\n{\n return error instanceof HttpError;\n}\n\n/**\n * Check if error has a statusCode property\n */\nexport function hasStatusCode(error: unknown): error is { statusCode: number }\n{\n return (\n typeof error === 'object' &&\n error !== null &&\n 'statusCode' in error &&\n typeof (error as any).statusCode === 'number'\n );\n}\n","/**\n * Error Module Exports\n *\n * Entry point for error handling module with serialization support\n */\n\n// Core Error Registry\n// Pre-configured registry with all built-in HTTP and Database errors\nimport { ErrorRegistry } from './error-registry';\nimport {\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n} from './http-errors';\nimport {\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n} from './database-errors';\n\n// Base Classes\nexport { SerializableError } from './serializable-error';\nexport type { SerializedError } from './serializable-error';\nexport { ErrorRegistry } from './error-registry';\nexport type { SerializableErrorConstructor, ErrorRegistryInput } from './error-registry';\n\n// Database Error Classes\nexport {\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n} from './database-errors';\n\n// HTTP Error Classes\nexport {\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n} from './http-errors';\n\n// Error Utilities\nexport {\n isDatabaseError,\n isHttpError,\n hasStatusCode,\n} from './error-utils';\n\nexport const errorRegistry = new ErrorRegistry();\n\n// HTTP Errors\nerrorRegistry.append([\n HttpError,\n BadRequestError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n GoneError,\n TooManyRequestsError,\n UnsupportedMediaTypeError,\n UnprocessableEntityError,\n InternalServerError,\n ServiceUnavailableError,\n]);\n\n// Database Errors\nerrorRegistry.append([\n DatabaseError,\n ConnectionError,\n QueryError,\n EntityNotFoundError,\n ConstraintViolationError,\n TransactionError,\n DeadlockError,\n DuplicateEntryError,\n]);\n\nexport * as HttpErrors from './http-errors';\nexport * as DatabaseErrors from './database-errors';\n"]}
|
package/dist/event/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TSchema, Static } from '@sinclair/typebox';
|
|
2
|
-
import { a as EventDef } from '../token-manager-
|
|
3
|
-
export { e as EventHandler, E as EventRouterDef, I as InferEventNames, f as InferEventPayload, c as InferEventPayloads, b as InferRouterEventPayload, J as JobQueueSender, P as PubSubCache, d as defineEventRouter } from '../token-manager-
|
|
2
|
+
import { a as EventDef } from '../token-manager-jKD_EsSE.js';
|
|
3
|
+
export { e as EventHandler, E as EventRouterDef, I as InferEventNames, f as InferEventPayload, c as InferEventPayloads, b as InferRouterEventPayload, J as JobQueueSender, P as PubSubCache, d as defineEventRouter } from '../token-manager-jKD_EsSE.js';
|
|
4
4
|
export { defineWSRouter } from './ws/index.js';
|
|
5
|
-
import '../types-
|
|
5
|
+
import '../types-DVjf37yO.js';
|
|
6
6
|
import 'hono';
|
|
7
7
|
import 'node:http';
|
|
8
8
|
|
package/dist/event/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/event/event.ts","../../src/event/sse/route-map.ts","../../src/event/router.ts","../../src/event/ws/handler.ts","../../src/event/ws/index.ts"],"names":["logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Event System\n *\n * Decoupled pub/sub event system with optional cache integration for multi-instance support\n *\n * @example\n * ```typescript\n * // Define event\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * // Subscribe (in-memory)\n * userCreated.subscribe((payload) => {\n * console.log('User created:', payload.userId);\n * });\n *\n * // Emit\n * await userCreated.emit({ userId: '123' });\n *\n * // With cache for multi-instance\n * const event = defineEvent('user.created', schema);\n * await event.useCache(cache); // Must await before emitting\n * await event.emit({ userId: '123' }); // Broadcast to all instances\n * ```\n */\n\nimport type { TSchema, Static } from '@sinclair/typebox';\nimport { logger } from '@spfn/core/logger';\nimport type { EventDef, EventHandler, JobQueueSender, PubSubCache } from './types';\n\nconst eventLogger = logger.child('@spfn/core:event');\n\n/**\n * Log handler error with consistent format\n */\nfunction logHandlerError(eventName: string, error: unknown): void\n{\n eventLogger.error(`Event handler error: ${eventName}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n}\n\n/**\n * Log job queue error with consistent format\n */\nfunction logJobQueueError(queueName: string, error: unknown): void\n{\n eventLogger.error(`Failed to send event to job queue: ${queueName}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n}\n\n/**\n * Create handler subscription manager\n */\nfunction createHandlerManager<TPayload>(name: string)\n{\n const handlers: Set<EventHandler<TPayload>> = new Set();\n\n return {\n add: (handler: EventHandler<TPayload>): (() => void) =>\n {\n handlers.add(handler);\n eventLogger.debug(`Subscribed to event: ${name}`, { handlerCount: handlers.size });\n\n return () =>\n {\n handlers.delete(handler);\n eventLogger.debug(`Unsubscribed from event: ${name}`, { handlerCount: handlers.size });\n };\n },\n\n clear: (): void =>\n {\n handlers.clear();\n eventLogger.debug(`Unsubscribed all from event: ${name}`);\n },\n\n trigger: async (payload: TPayload): Promise<void> =>\n {\n const results = await Promise.allSettled(\n [...handlers].map((handler) => handler(payload)),\n );\n\n for (const result of results)\n {\n if (result.status === 'rejected')\n {\n logHandlerError(name, result.reason);\n }\n }\n },\n };\n}\n\n/**\n * Create job queue manager\n */\nfunction createJobQueueManager(name: string)\n{\n const jobQueues: Map<string, JobQueueSender> = new Map();\n\n return {\n register: (queueName: string, sender: JobQueueSender): void =>\n {\n jobQueues.set(queueName, sender);\n eventLogger.debug(`Registered job queue for event: ${name}`, { queueName });\n },\n\n send: async (payload: unknown): Promise<void> =>\n {\n if (jobQueues.size === 0)\n {\n return;\n }\n\n const entries = [...jobQueues.entries()];\n const results = await Promise.allSettled(\n entries.map(([queueName, sender]) => sender(queueName, payload)),\n );\n\n for (const [i, result] of results.entries())\n {\n if (result.status === 'rejected')\n {\n logJobQueueError(entries[i][0], result.reason);\n }\n }\n },\n\n get size(): number\n {\n return jobQueues.size;\n },\n };\n}\n\n/**\n * Internal: Create event implementation\n */\nfunction createEventImpl<TPayload>(\n name: string,\n schema?: TSchema,\n): EventDef<TPayload>\n{\n const handlerManager = createHandlerManager<TPayload>(name);\n const jobQueueManager = createJobQueueManager(name);\n let cache: PubSubCache | undefined;\n let cacheSubscribed = false;\n\n const emit = async (payload?: TPayload): Promise<void> =>\n {\n eventLogger.debug(`Emitting event: ${name}`, {\n payload,\n hasCache: !!cache,\n jobQueueCount: jobQueueManager.size,\n });\n\n if (cache)\n {\n await cache.publish(name, payload);\n }\n else\n {\n await handlerManager.trigger(payload as TPayload);\n }\n\n await jobQueueManager.send(payload);\n eventLogger.debug(`Event emitted: ${name}`);\n };\n\n const useCache = async (newCache: PubSubCache): Promise<EventDef<TPayload>> =>\n {\n if (cacheSubscribed)\n {\n eventLogger.warn(`Cache already configured for event: ${name}`);\n\n return self;\n }\n\n // Subscribe FIRST, then flip to cache mode. If subscribe rejects, the\n // event stays in-process (cache undefined) and cacheSubscribed stays\n // false — so emit keeps delivering locally and a later useCache can retry.\n // Setting these before the await would latch a half-broken state: emit\n // would publish to a channel this instance never subscribed to.\n await newCache.subscribe(name, async (message: unknown) =>\n {\n eventLogger.debug(`Received event from cache: ${name}`);\n await handlerManager.trigger(message as TPayload);\n });\n\n cache = newCache;\n cacheSubscribed = true;\n\n eventLogger.debug(`Cache subscription ready for event: ${name}`);\n\n return self;\n };\n\n // Drop the cache binding so a later useCache can rebind a fresh transport.\n // Without this, the cacheSubscribed latch would make a second server start in\n // the same process (tests, hot-reload) a no-op while the event still points at\n // a torn-down cache — cross-pod fan-out would silently stay dead.\n const resetCache = (): void =>\n {\n cache = undefined;\n cacheSubscribed = false;\n };\n\n const self: EventDef<TPayload> = {\n name,\n schema,\n subscribe: handlerManager.add,\n unsubscribeAll: handlerManager.clear,\n emit: emit as EventDef<TPayload>['emit'],\n useCache,\n _registerJobQueue: jobQueueManager.register,\n _resetCache: resetCache,\n _payload: undefined as unknown as TPayload,\n };\n\n return self;\n}\n\n/**\n * Define an event without payload\n */\nexport function defineEvent(name: string): EventDef<void>;\n\n/**\n * Define an event with typed payload\n */\nexport function defineEvent<T extends TSchema>(\n name: string,\n schema: T,\n): EventDef<Static<T>>;\n\n/**\n * Define an event for decoupled pub/sub\n *\n * @example\n * ```typescript\n * // Define event with payload\n * export const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * // Subscribe to event (in-memory)\n * const unsubscribe = userCreated.subscribe((payload) => {\n * console.log('User created:', payload.userId);\n * });\n *\n * // Emit event\n * await userCreated.emit({ userId: '123' });\n *\n * // Unsubscribe when done\n * unsubscribe();\n *\n * // Multi-instance with cache\n * await userCreated.useCache(cache);\n * await userCreated.emit({ userId: '123' }); // Broadcast to all instances\n * ```\n */\nexport function defineEvent<T extends TSchema>(\n name: string,\n schema?: T,\n): EventDef<Static<T>> | EventDef\n{\n if (schema)\n {\n return createEventImpl<Static<T>>(name, schema);\n }\n\n return createEventImpl<void>(name);\n}\n","/**\n * SSE Event Route Map\n *\n * Static route map for SSE token endpoint.\n * Merge into RPC proxy routeMap so `eventsToken` resolves to `POST /events/token`.\n *\n * @example\n * ```typescript\n * // app/api/rpc/[routeName]/route.ts\n * import { createRpcProxy } from '@spfn/core/nextjs/server';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { authRouteMap } from '@spfn/auth';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\nexport const eventRouteMap = {\n eventsToken: { method: 'POST' as const, path: '/events/token' },\n};\n","/**\n * Event Router\n *\n * Type-safe event router for SSE subscription\n *\n * @example\n * ```typescript\n * import { defineEvent, defineEventRouter } from '@spfn/core/event';\n * import { Type } from '@sinclair/typebox';\n *\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const orderPlaced = defineEvent('order.placed', Type.Object({\n * orderId: Type.String(),\n * amount: Type.Number(),\n * }));\n *\n * export const eventRouter = defineEventRouter({\n * userCreated,\n * orderPlaced,\n * });\n *\n * export type EventRouter = typeof eventRouter;\n * ```\n */\n\nimport type { EventDef } from './types';\n\n/**\n * Event Router Definition\n */\nexport interface EventRouterDef<TEvents extends Record<string, EventDef<any>>>\n{\n /**\n * Event definitions\n */\n readonly events: TEvents;\n\n /**\n * Event names as array\n */\n readonly eventNames: (keyof TEvents)[];\n\n /**\n * Type inference helper - payload types by event name\n */\n readonly _types: {\n [K in keyof TEvents]: TEvents[K]['_payload'];\n };\n}\n\n/**\n * Infer event names from EventRouter\n */\nexport type InferEventNames<T> = T extends EventRouterDef<infer E>\n ? keyof E & string\n : never;\n\n/**\n * Infer payload type for specific event\n */\nexport type InferEventPayload<\n T extends EventRouterDef<any>,\n K extends InferEventNames<T>,\n> = T['_types'][K];\n\n/**\n * Infer all event payloads map\n */\nexport type InferEventPayloads<T extends EventRouterDef<any>> = T['_types'];\n\n/**\n * Define an event router for SSE subscription\n *\n * @example\n * ```typescript\n * export const eventRouter = defineEventRouter({\n * userCreated,\n * orderPlaced,\n * });\n *\n * // Type inference\n * type Names = InferEventNames<typeof eventRouter>;\n * // 'userCreated' | 'orderPlaced'\n *\n * type Payload = InferEventPayload<typeof eventRouter, 'userCreated'>;\n * // { userId: string }\n * ```\n */\nexport function defineEventRouter<\n TEvents extends Record<string, EventDef<any>>,\n>(events: TEvents): EventRouterDef<TEvents>\n{\n return {\n events,\n eventNames: Object.keys(events) as (keyof TEvents)[],\n _types: {} as EventRouterDef<TEvents>['_types'],\n };\n}\n","/**\n * WebSocket Handler\n *\n * Attaches a WebSocket server to an existing Node.js http.Server.\n * Handles authentication, event subscription, and client message routing.\n */\n\nimport type { Server } from 'node:http';\nimport type { EventDef } from '../types';\nimport type {\n WSRouterDef,\n WSHandlerConfig,\n WSHandlerAuthConfig,\n WSMessageHandlers,\n WSRawConnection,\n} from './types';\nimport type { SSETokenManager } from '../sse/token-manager';\nimport { logger } from '@spfn/core/logger';\n\nconst wsLogger = logger.child('@spfn/core:ws');\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Attach a WebSocket server to a Node.js http.Server\n *\n * @returns cleanup function that closes the WebSocket server\n */\nexport async function attachWSHandler<\n TEvents extends Record<string, EventDef<any>>,\n TMessages extends WSMessageHandlers,\n>(\n server: Server,\n router: WSRouterDef<TEvents, TMessages>,\n config: WSHandlerConfig & { path?: string } = {},\n tokenManager?: SSETokenManager,\n): Promise<() => Promise<void>>\n{\n const WebSocketServer = await loadWSServer();\n\n const {\n pingInterval = 30000,\n path = '/ws',\n auth: authConfig,\n } = config;\n\n if (authConfig?.enabled && !tokenManager)\n {\n throw new Error(\n 'WebSocket auth.enabled=true requires a tokenManager. ' +\n 'Pass tokenManager or use .websockets(router, { auth: { enabled: true } }) via startServer.',\n );\n }\n\n const wss = new WebSocketServer({ server, path });\n\n // Track live connections for graceful shutdown\n const clients = new Set<any>();\n\n wss.on('connection', (ws: any, req: any) =>\n {\n clients.add(ws);\n ws.on('close', () => clients.delete(ws));\n handleConnection(ws, req, router, authConfig, tokenManager, pingInterval)\n .catch((err: Error) =>\n {\n wsLogger.error('WebSocket connection handler error', err);\n if (ws.readyState === 1) ws.close(1011, 'Internal server error');\n });\n });\n\n wss.on('error', (err: Error) =>\n {\n wsLogger.error('WebSocket server error', err);\n });\n\n wsLogger.info(`✓ WebSocket endpoint registered at ${path}`, {\n events: router.eventNames,\n auth: !!authConfig?.enabled,\n });\n\n return () => new Promise<void>((resolve, reject) =>\n {\n // Close all existing connections with 1001 Going Away\n for (const client of clients)\n {\n client.close(1001, 'Server shutting down');\n }\n clients.clear();\n\n wss.close((err?: Error) =>\n {\n if (err) reject(err);\n else resolve();\n });\n });\n}\n\n// ============================================================================\n// Connection Handler\n// ============================================================================\n\nasync function handleConnection(\n ws: any,\n req: any,\n router: WSRouterDef<any, any>,\n authConfig: WSHandlerAuthConfig | undefined,\n tokenManager: SSETokenManager | undefined,\n pingInterval: number,\n): Promise<void>\n{\n // Register close handler before any await — ensures we never miss the event even during auth\n let pingTimer: ReturnType<typeof setInterval> | undefined;\n let connectionUnsubscribes: (() => void)[] = [];\n let subscribedEvents: string[] = [];\n ws.on('close', () =>\n {\n clearInterval(pingTimer);\n connectionUnsubscribes.forEach(fn => fn());\n if (subscribedEvents.length > 0)\n wsLogger.info('WebSocket connection closed', { events: subscribedEvents });\n });\n\n const url = parseURL(req);\n if (!url)\n {\n ws.close(1002, 'Invalid request URL');\n\n return;\n }\n\n // ── 1. Authenticate ──\n const subject = await resolveSubject(url, authConfig?.enabled ? tokenManager : undefined);\n if (subject === false)\n {\n ws.close(4001, 'Missing token');\n\n return;\n }\n if (subject === null)\n {\n ws.close(4001, 'Invalid or expired token');\n\n return;\n }\n\n // ── 2. Resolve subscribed events ──\n const requestedEvents = parseRequestedEvents(url, router.eventNames as string[]);\n if (requestedEvents.length === 0)\n {\n ws.close(4000, 'No valid event names specified');\n\n return;\n }\n\n // ── 3. Authorize ──\n const allowedEvents = await resolveAllowedEvents(subject, requestedEvents, authConfig);\n if (allowedEvents === null)\n {\n ws.close(4003, 'Not authorized for any requested events');\n\n return;\n }\n\n subscribedEvents = allowedEvents;\n wsLogger.info('WebSocket connection established', {\n events: allowedEvents,\n subject: subject ?? undefined,\n });\n\n // ── 4. Build connection wrapper ──\n const connection = createConnection(ws);\n\n // ── 5. Subscribe to server-push events ──\n connectionUnsubscribes = subscribeEvents(ws, router, allowedEvents, subject, authConfig);\n\n // If socket closed during auth awaits, clean up and bail\n if (ws.readyState !== 1)\n {\n connectionUnsubscribes.forEach(fn => fn());\n connectionUnsubscribes = [];\n\n return;\n }\n\n // ── 6. Handle incoming messages ──\n ws.on('message', (data: Buffer | string) =>\n {\n onClientMessage(data, router, connection, subject)\n .catch((err: Error) => wsLogger.error('Unhandled message error', err));\n });\n\n // ── 7. Keep-alive ping ──\n if (pingInterval > 0)\n {\n pingTimer = setInterval(() =>\n {\n if (ws.readyState === 1) ws.ping();\n }, pingInterval);\n }\n\n // ── 9. Send connected ack ──\n connection.send('__connected', {\n subscribedEvents: allowedEvents,\n timestamp: Date.now(),\n });\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction parseURL(req: any): URL | null\n{\n try\n {\n return new URL(req.url ?? '/', 'ws://localhost');\n }\n catch\n {\n return null;\n }\n}\n\n/**\n * Resolve subject from token\n * - undefined: no auth required\n * - false: token param missing (when required)\n * - null: token invalid/expired\n * - string: authenticated subject\n */\nasync function resolveSubject(\n url: URL,\n tokenManager?: SSETokenManager,\n): Promise<string | undefined | false | null>\n{\n if (!tokenManager)\n {\n return undefined;\n }\n\n const token = url.searchParams.get('token');\n if (!token)\n {\n return false;\n }\n\n return await tokenManager.verify(token);\n}\n\nfunction parseRequestedEvents(url: URL, validEventNames: string[]): string[]\n{\n const eventsParam = url.searchParams.get('events');\n if (!eventsParam)\n {\n return [];\n }\n\n return eventsParam\n .split(',')\n .map(e => e.trim())\n .filter(e => validEventNames.includes(e));\n}\n\nasync function resolveAllowedEvents(\n subject: string | undefined,\n requestedEvents: string[],\n authConfig?: WSHandlerAuthConfig,\n): Promise<string[] | null>\n{\n if (!subject || !authConfig?.authorize)\n {\n return requestedEvents;\n }\n\n const allowed = await authConfig.authorize(subject, requestedEvents);\n\n return allowed.length === 0 ? null : allowed;\n}\n\nfunction createConnection(ws: any): WSRawConnection\n{\n return {\n send: (type, payload) =>\n {\n if (ws.readyState !== 1) return;\n ws.send(JSON.stringify({ type, data: payload }));\n },\n close: (code, reason) => ws.close(code, reason),\n };\n}\n\nfunction subscribeEvents(\n ws: any,\n router: WSRouterDef<any, any>,\n allowedEvents: string[],\n subject: string | undefined,\n authConfig?: WSHandlerAuthConfig,\n): (() => void)[]\n{\n const unsubscribes: (() => void)[] = [];\n\n for (const eventName of allowedEvents)\n {\n const eventDef = router.events[eventName];\n if (!eventDef) continue;\n\n const unsubscribe = eventDef.subscribe((payload: unknown) =>\n {\n if (ws.readyState !== 1) return;\n\n if (subject && authConfig?.filter?.[eventName])\n {\n if (!authConfig.filter[eventName](subject, payload)) return;\n }\n\n try\n {\n ws.send(JSON.stringify({ type: eventName, data: payload }));\n }\n catch\n {\n // Socket closed between readyState check and send — ignore\n }\n });\n\n unsubscribes.push(unsubscribe);\n }\n\n return unsubscribes;\n}\n\nasync function onClientMessage(\n data: Buffer | string,\n router: WSRouterDef<any, any>,\n connection: WSRawConnection,\n subject: string | undefined,\n): Promise<void>\n{\n let message: { type?: string; data?: unknown };\n\n try\n {\n message = JSON.parse(data.toString());\n }\n catch\n {\n return;\n }\n\n const { type, data: payload } = message;\n if (!type) return;\n\n const handler = router.messages[type];\n if (!handler) return;\n\n try\n {\n await handler({ payload, subject, ws: connection });\n }\n catch (err)\n {\n wsLogger.error(`WebSocket message handler error: ${type}`, err as Error);\n }\n}\n\n// ============================================================================\n// Dynamic import for optional 'ws' dependency\n// ============================================================================\n\nasync function loadWSServer(): Promise<any>\n{\n try\n {\n // ws is a CJS package: module.exports = WebSocket, WebSocket.WebSocketServer is set on it.\n // ESM dynamic import wraps CJS default export under .default\n \n const mod = await import('ws') as any;\n const WS = mod.default ?? mod;\n const WSS = WS.WebSocketServer ?? WS.Server;\n\n if (typeof WSS !== 'function')\n {\n throw new Error(\n 'WebSocketServer not found in ws module. ' +\n 'Ensure ws@^8 is installed: pnpm add ws',\n );\n }\n\n return WSS;\n }\n catch (err)\n {\n if (err instanceof Error && err.message.includes('WebSocketServer not found'))\n {\n throw err;\n }\n throw new Error(\n '@spfn/core WebSocket support requires the \"ws\" package.\\n' +\n 'Install it with: pnpm add ws',\n );\n }\n}\n","/**\n * WebSocket Module\n *\n * Type-safe WebSocket server with event-based pub/sub and bidirectional messaging.\n *\n * @example Server setup\n * ```typescript\n * // src/server/ws.ts\n * import { defineWSRouter } from '@spfn/core/event/ws';\n * import { defineEvent } from '@spfn/core/event';\n * import { Type } from '@sinclair/typebox';\n *\n * const userUpdated = defineEvent('user.updated', Type.Object({ userId: Type.String() }));\n * const notification = defineEvent('notification', Type.Object({ message: Type.String() }));\n *\n * export const wsRouter = defineWSRouter({\n * events: { userUpdated, notification },\n * messages: {\n * ping: ({ ws }) => ws.send('pong', {}),\n * },\n * });\n *\n * export type WSRouter = typeof wsRouter;\n *\n * // server.config.ts\n * defineServerConfig()\n * .websockets(wsRouter)\n * .build();\n * ```\n *\n * @example Client usage\n * ```typescript\n * import { createWSClient } from '@spfn/core/event/ws/client';\n * import type { WSRouter } from '@/server/ws';\n *\n * const client = createWSClient<WSRouter>();\n *\n * client.subscribe({\n * events: ['userUpdated', 'notification'],\n * handlers: {\n * userUpdated: ({ userId }) => console.log(userId),\n * notification: ({ message }) => console.log(message),\n * },\n * });\n *\n * client.send('ping', {});\n * ```\n */\n\nimport type { EventDef } from '../types';\nimport type { WSRouterDef, WSMessageHandlers } from './types';\n\nexport { attachWSHandler } from './handler';\nexport type {\n WSRouterDef,\n WSHandlerConfig,\n WSAuthConfig,\n WSHandlerAuthConfig,\n WSMessageContext,\n WSMessageHandlerFn,\n WSMessageHandlers,\n WSRawConnection,\n WSClientConfig,\n WSConnectionState,\n WSEventHandlers,\n WSSubscribeOptions,\n WSUnsubscribe,\n} from './types';\n\n/**\n * Define a WebSocket router\n *\n * Combines server→client event push with client→server message handlers.\n *\n * @example\n * ```typescript\n * export const wsRouter = defineWSRouter({\n * events: { userUpdated, notification },\n * messages: {\n * ping: ({ ws }) => ws.send('pong', {}),\n * 'chat.send': ({ payload, subject }) => handleChat(payload, subject),\n * },\n * });\n * ```\n */\nexport function defineWSRouter<\n TEvents extends Record<string, EventDef<any>>,\n TMessages extends WSMessageHandlers = WSMessageHandlers,\n>(def: {\n events: TEvents;\n messages?: TMessages;\n}): WSRouterDef<TEvents, TMessages>\n{\n return {\n events: def.events,\n eventNames: Object.keys(def.events) as (keyof TEvents)[],\n messages: (def.messages ?? {}) as TMessages,\n _types: {} as WSRouterDef<TEvents, TMessages>['_types'],\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/event/event.ts","../../src/event/sse/route-map.ts","../../src/event/router.ts","../../src/event/ws/handler.ts","../../src/event/ws/index.ts"],"names":["logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Event System\n *\n * Decoupled pub/sub event system with optional cache integration for multi-instance support\n *\n * @example\n * ```typescript\n * // Define event\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * // Subscribe (in-memory)\n * userCreated.subscribe((payload) => {\n * console.log('User created:', payload.userId);\n * });\n *\n * // Emit\n * await userCreated.emit({ userId: '123' });\n *\n * // With cache for multi-instance\n * const event = defineEvent('user.created', schema);\n * await event.useCache(cache); // Must await before emitting\n * await event.emit({ userId: '123' }); // Broadcast to all instances\n * ```\n */\n\nimport type { TSchema, Static } from '@sinclair/typebox';\nimport { logger } from '@spfn/core/logger';\nimport type { EventDef, EventHandler, JobQueueSender, PubSubCache } from './types';\n\nconst eventLogger = logger.child('@spfn/core:event');\n\n/**\n * Log handler error with consistent format\n */\nfunction logHandlerError(eventName: string, error: unknown): void\n{\n eventLogger.error(`Event handler error: ${eventName}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n}\n\n/**\n * Log job queue error with consistent format\n */\nfunction logJobQueueError(queueName: string, error: unknown): void\n{\n eventLogger.error(`Failed to send event to job queue: ${queueName}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n}\n\n/**\n * Create handler subscription manager\n */\nfunction createHandlerManager<TPayload>(name: string)\n{\n const handlers: Set<EventHandler<TPayload>> = new Set();\n\n return {\n add: (handler: EventHandler<TPayload>): (() => void) =>\n {\n handlers.add(handler);\n eventLogger.debug(`Subscribed to event: ${name}`, { handlerCount: handlers.size });\n\n return () =>\n {\n handlers.delete(handler);\n eventLogger.debug(`Unsubscribed from event: ${name}`, { handlerCount: handlers.size });\n };\n },\n\n clear: (): void =>\n {\n handlers.clear();\n eventLogger.debug(`Unsubscribed all from event: ${name}`);\n },\n\n trigger: async (payload: TPayload): Promise<void> =>\n {\n const results = await Promise.allSettled(\n [...handlers].map((handler) => handler(payload)),\n );\n\n for (const result of results)\n {\n if (result.status === 'rejected')\n {\n logHandlerError(name, result.reason);\n }\n }\n },\n };\n}\n\n/**\n * Create job queue manager\n */\nfunction createJobQueueManager(name: string)\n{\n const jobQueues: Map<string, JobQueueSender> = new Map();\n\n return {\n register: (queueName: string, sender: JobQueueSender): void =>\n {\n jobQueues.set(queueName, sender);\n eventLogger.debug(`Registered job queue for event: ${name}`, { queueName });\n },\n\n send: async (payload: unknown): Promise<void> =>\n {\n if (jobQueues.size === 0)\n {\n return;\n }\n\n const entries = [...jobQueues.entries()];\n const results = await Promise.allSettled(\n entries.map(([queueName, sender]) => sender(queueName, payload)),\n );\n\n for (const [i, result] of results.entries())\n {\n if (result.status === 'rejected')\n {\n logJobQueueError(entries[i][0], result.reason);\n }\n }\n },\n\n get size(): number\n {\n return jobQueues.size;\n },\n };\n}\n\n/**\n * Internal: Create event implementation\n */\nfunction createEventImpl<TPayload>(\n name: string,\n schema?: TSchema,\n): EventDef<TPayload>\n{\n const handlerManager = createHandlerManager<TPayload>(name);\n const jobQueueManager = createJobQueueManager(name);\n let cache: PubSubCache | undefined;\n let cacheSubscribed = false;\n\n const emit = async (payload?: TPayload): Promise<void> =>\n {\n eventLogger.debug(`Emitting event: ${name}`, {\n payload,\n hasCache: !!cache,\n jobQueueCount: jobQueueManager.size,\n });\n\n if (cache)\n {\n await cache.publish(name, payload);\n }\n else\n {\n await handlerManager.trigger(payload as TPayload);\n }\n\n await jobQueueManager.send(payload);\n eventLogger.debug(`Event emitted: ${name}`);\n };\n\n const useCache = async (newCache: PubSubCache): Promise<EventDef<TPayload>> =>\n {\n if (cacheSubscribed)\n {\n eventLogger.warn(`Cache already configured for event: ${name}`);\n\n return self;\n }\n\n // Subscribe FIRST, then flip to cache mode. If subscribe rejects, the\n // event stays in-process (cache undefined) and cacheSubscribed stays\n // false — so emit keeps delivering locally and a later useCache can retry.\n // Setting these before the await would latch a half-broken state: emit\n // would publish to a channel this instance never subscribed to.\n await newCache.subscribe(name, async (message: unknown) =>\n {\n eventLogger.debug(`Received event from cache: ${name}`);\n await handlerManager.trigger(message as TPayload);\n });\n\n cache = newCache;\n cacheSubscribed = true;\n\n eventLogger.debug(`Cache subscription ready for event: ${name}`);\n\n return self;\n };\n\n // Drop the cache binding so a later useCache can rebind a fresh transport.\n // Without this, the cacheSubscribed latch would make a second server start in\n // the same process (tests, hot-reload) a no-op while the event still points at\n // a torn-down cache — cross-pod fan-out would silently stay dead.\n const resetCache = (): void =>\n {\n cache = undefined;\n cacheSubscribed = false;\n };\n\n const self: EventDef<TPayload> = {\n name,\n schema,\n subscribe: handlerManager.add,\n unsubscribeAll: handlerManager.clear,\n emit: emit as EventDef<TPayload>['emit'],\n useCache,\n _registerJobQueue: jobQueueManager.register,\n _resetCache: resetCache,\n _payload: undefined as unknown as TPayload,\n };\n\n return self;\n}\n\n/**\n * Define an event without payload\n */\nexport function defineEvent(name: string): EventDef<void>;\n\n/**\n * Define an event with typed payload\n */\nexport function defineEvent<T extends TSchema>(\n name: string,\n schema: T,\n): EventDef<Static<T>>;\n\n/**\n * Define an event for decoupled pub/sub\n *\n * @example\n * ```typescript\n * // Define event with payload\n * export const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * // Subscribe to event (in-memory)\n * const unsubscribe = userCreated.subscribe((payload) => {\n * console.log('User created:', payload.userId);\n * });\n *\n * // Emit event\n * await userCreated.emit({ userId: '123' });\n *\n * // Unsubscribe when done\n * unsubscribe();\n *\n * // Multi-instance with cache\n * await userCreated.useCache(cache);\n * await userCreated.emit({ userId: '123' }); // Broadcast to all instances\n * ```\n */\nexport function defineEvent<T extends TSchema>(\n name: string,\n schema?: T,\n): EventDef<Static<T>> | EventDef\n{\n if (schema)\n {\n return createEventImpl<Static<T>>(name, schema);\n }\n\n return createEventImpl<void>(name);\n}\n","/**\n * SSE Event Route Map\n *\n * Static route map for SSE token endpoint.\n * Merge into RPC proxy routeMap so `eventsToken` resolves to `POST /events/token`.\n *\n * @example\n * ```typescript\n * // app/api/rpc/[routeName]/route.ts\n * import { createRpcProxy } from '@spfn/core/nextjs/server';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { authRouteMap } from '@spfn/auth';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\nexport const eventRouteMap = {\n eventsToken: { method: 'POST' as const, path: '/events/token' },\n};\n","/**\n * Event Router\n *\n * Type-safe event router for SSE subscription\n *\n * @example\n * ```typescript\n * import { defineEvent, defineEventRouter } from '@spfn/core/event';\n * import { Type } from '@sinclair/typebox';\n *\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const orderPlaced = defineEvent('order.placed', Type.Object({\n * orderId: Type.String(),\n * amount: Type.Number(),\n * }));\n *\n * export const eventRouter = defineEventRouter({\n * userCreated,\n * orderPlaced,\n * });\n *\n * export type EventRouter = typeof eventRouter;\n * ```\n */\n\nimport type { EventDef } from './types';\n\n/**\n * Event Router Definition\n */\nexport interface EventRouterDef<TEvents extends Record<string, EventDef<any>>>\n{\n /**\n * Event definitions\n */\n readonly events: TEvents;\n\n /**\n * Event names as array\n */\n readonly eventNames: (keyof TEvents)[];\n\n /**\n * Type inference helper - payload types by event name\n */\n readonly _types: {\n [K in keyof TEvents]: TEvents[K]['_payload'];\n };\n}\n\n/**\n * Infer event names from EventRouter\n */\nexport type InferEventNames<T> = T extends EventRouterDef<infer E>\n ? keyof E & string\n : never;\n\n/**\n * Infer payload type for specific event\n */\nexport type InferEventPayload<\n T extends EventRouterDef<any>,\n K extends InferEventNames<T>,\n> = T['_types'][K];\n\n/**\n * Infer all event payloads map\n */\nexport type InferEventPayloads<T extends EventRouterDef<any>> = T['_types'];\n\n/**\n * Define an event router for SSE subscription\n *\n * @example\n * ```typescript\n * export const eventRouter = defineEventRouter({\n * userCreated,\n * orderPlaced,\n * });\n *\n * // Type inference\n * type Names = InferEventNames<typeof eventRouter>;\n * // 'userCreated' | 'orderPlaced'\n *\n * type Payload = InferEventPayload<typeof eventRouter, 'userCreated'>;\n * // { userId: string }\n * ```\n */\nexport function defineEventRouter<\n TEvents extends Record<string, EventDef<any>>,\n>(events: TEvents): EventRouterDef<TEvents>\n{\n return {\n events,\n eventNames: Object.keys(events) as (keyof TEvents)[],\n _types: {} as EventRouterDef<TEvents>['_types'],\n };\n}\n","/**\n * WebSocket Handler\n *\n * Attaches a WebSocket server to an existing Node.js http.Server.\n * Handles authentication, event subscription, and client message routing.\n */\n\nimport type { Server } from 'node:http';\nimport type { EventDef } from '../types';\nimport type {\n WSRouterDef,\n WSHandlerConfig,\n WSHandlerAuthConfig,\n WSMessageHandlers,\n WSRawConnection,\n} from './types';\nimport type { SSETokenManager } from '../sse/token-manager';\nimport { logger } from '@spfn/core/logger';\n\nconst wsLogger = logger.child('@spfn/core:ws');\n\n/**\n * Decide whether a WebSocket upgrade's Origin is permitted.\n *\n * - no allow-list configured → allowed (check disabled)\n * - no Origin header (native/non-browser client) → allowed\n * - Origin present → must be in the allow-list\n */\nexport function isOriginAllowed(\n origin: string | undefined,\n allowList: Set<string> | null,\n): boolean\n{\n if (!allowList)\n {\n return true;\n }\n\n if (!origin)\n {\n return true;\n }\n\n return allowList.has(origin);\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Attach a WebSocket server to a Node.js http.Server\n *\n * @returns cleanup function that closes the WebSocket server\n */\nexport async function attachWSHandler<\n TEvents extends Record<string, EventDef<any>>,\n TMessages extends WSMessageHandlers,\n>(\n server: Server,\n router: WSRouterDef<TEvents, TMessages>,\n config: WSHandlerConfig & { path?: string } = {},\n tokenManager?: SSETokenManager,\n): Promise<() => Promise<void>>\n{\n const WebSocketServer = await loadWSServer();\n\n const {\n pingInterval = 30000,\n path = '/ws',\n maxPayload = 1_048_576,\n maxBufferedBytes = 1_048_576,\n maxConnections = 10_000,\n maxConnectionsPerSubject = 0,\n allowedOrigins,\n auth: authConfig,\n } = config;\n\n const originAllowList = allowedOrigins && allowedOrigins.length > 0\n ? new Set(allowedOrigins)\n : null;\n\n if (authConfig?.enabled && !tokenManager)\n {\n throw new Error(\n 'WebSocket auth.enabled=true requires a tokenManager. ' +\n 'Pass tokenManager or use .websockets(router, { auth: { enabled: true } }) via startServer.',\n );\n }\n\n const wss = new WebSocketServer({ server, path, maxPayload });\n\n // Track live connections for graceful shutdown\n const clients = new Set<any>();\n // Live connection count per authenticated subject (for the per-subject cap)\n const subjectCounts = new Map<string, number>();\n\n wss.on('connection', (ws: any, req: any) =>\n {\n // Origin allow-list (cross-site WebSocket hijacking protection). Browsers\n // always send Origin on the upgrade; reject one that isn't allowed before\n // doing any work. A missing Origin (native/non-browser client, which can't\n // be CSRF'd into using ambient cookies) passes through.\n if (!isOriginAllowed(req.headers?.origin, originAllowList))\n {\n wsLogger.warn('WebSocket rejected: disallowed origin', { origin: req.headers?.origin });\n ws.close(1008, 'Origin not allowed');\n\n return;\n }\n\n // Global connection cap — reject before doing any work\n if (clients.size >= maxConnections)\n {\n ws.close(1013, 'Server at capacity');\n\n return;\n }\n\n clients.add(ws);\n ws.on('close', () => clients.delete(ws));\n handleConnection(ws, req, router, authConfig, tokenManager, {\n pingInterval,\n maxBufferedBytes,\n maxConnectionsPerSubject,\n subjectCounts,\n })\n .catch((err: Error) =>\n {\n wsLogger.error('WebSocket connection handler error', err);\n if (ws.readyState === 1) ws.close(1011, 'Internal server error');\n });\n });\n\n wss.on('error', (err: Error) =>\n {\n wsLogger.error('WebSocket server error', err);\n });\n\n wsLogger.info(`✓ WebSocket endpoint registered at ${path}`, {\n events: router.eventNames,\n auth: !!authConfig?.enabled,\n });\n\n return () => new Promise<void>((resolve, reject) =>\n {\n // Close all existing connections with 1001 Going Away\n for (const client of clients)\n {\n client.close(1001, 'Server shutting down');\n }\n clients.clear();\n\n wss.close((err?: Error) =>\n {\n if (err) reject(err);\n else resolve();\n });\n });\n}\n\n// ============================================================================\n// Connection Handler\n// ============================================================================\n\ninterface ConnectionOptions\n{\n pingInterval: number;\n maxBufferedBytes: number;\n maxConnectionsPerSubject: number;\n subjectCounts: Map<string, number>;\n}\n\nasync function handleConnection(\n ws: any,\n req: any,\n router: WSRouterDef<any, any>,\n authConfig: WSHandlerAuthConfig | undefined,\n tokenManager: SSETokenManager | undefined,\n opts: ConnectionOptions,\n): Promise<void>\n{\n const { pingInterval, maxBufferedBytes, maxConnectionsPerSubject, subjectCounts } = opts;\n // Register close handler before any await — ensures we never miss the event even during auth\n let pingTimer: ReturnType<typeof setInterval> | undefined;\n let connectionUnsubscribes: (() => void)[] = [];\n let subscribedEvents: string[] = [];\n ws.on('close', () =>\n {\n clearInterval(pingTimer);\n connectionUnsubscribes.forEach(fn => fn());\n if (subscribedEvents.length > 0)\n wsLogger.info('WebSocket connection closed', { events: subscribedEvents });\n });\n\n const url = parseURL(req);\n if (!url)\n {\n ws.close(1002, 'Invalid request URL');\n\n return;\n }\n\n // ── 1. Authenticate ──\n const subject = await resolveSubject(url, authConfig?.enabled ? tokenManager : undefined);\n if (subject === false)\n {\n ws.close(4001, 'Missing token');\n\n return;\n }\n if (subject === null)\n {\n ws.close(4001, 'Invalid or expired token');\n\n return;\n }\n\n // ── 2. Resolve subscribed events ──\n const requestedEvents = parseRequestedEvents(url, router.eventNames as string[]);\n if (requestedEvents.length === 0)\n {\n ws.close(4000, 'No valid event names specified');\n\n return;\n }\n\n // ── 3. Authorize ──\n const allowedEvents = await resolveAllowedEvents(subject, requestedEvents, authConfig);\n if (allowedEvents === null)\n {\n ws.close(4003, 'Not authorized for any requested events');\n\n return;\n }\n\n // ── Per-subject connection cap (authenticated subjects only) ──\n if (maxConnectionsPerSubject > 0 && typeof subject === 'string')\n {\n const current = subjectCounts.get(subject) ?? 0;\n if (current >= maxConnectionsPerSubject)\n {\n ws.close(1013, 'Too many connections for this subject');\n\n return;\n }\n\n subjectCounts.set(subject, current + 1);\n ws.on('close', () =>\n {\n const remaining = (subjectCounts.get(subject) ?? 1) - 1;\n if (remaining <= 0) subjectCounts.delete(subject);\n else subjectCounts.set(subject, remaining);\n });\n }\n\n subscribedEvents = allowedEvents;\n wsLogger.info('WebSocket connection established', {\n events: allowedEvents,\n subject: subject ?? undefined,\n });\n\n // ── 4. Build connection wrapper ──\n const connection = createConnection(ws, maxBufferedBytes);\n\n // ── 5. Subscribe to server-push events ──\n connectionUnsubscribes = subscribeEvents(ws, router, allowedEvents, subject, authConfig, maxBufferedBytes);\n\n // If socket closed during auth awaits, clean up and bail\n if (ws.readyState !== 1)\n {\n connectionUnsubscribes.forEach(fn => fn());\n connectionUnsubscribes = [];\n\n return;\n }\n\n // ── 6. Handle incoming messages ──\n ws.on('message', (data: Buffer | string) =>\n {\n onClientMessage(data, router, connection, subject)\n .catch((err: Error) => wsLogger.error('Unhandled message error', err));\n });\n\n // ── 7. Keep-alive ping with liveness (pong) tracking ──\n // Without reaping un-ponged sockets, a half-open connection (sleeping device,\n // NAT drop with no FIN) lingers with its subscriptions, timer, and buffers.\n if (pingInterval > 0)\n {\n ws.isAlive = true;\n ws.on('pong', () =>\n {\n ws.isAlive = true;\n });\n\n pingTimer = setInterval(() =>\n {\n if (ws.readyState !== 1) return;\n\n if (ws.isAlive === false)\n {\n // No pong since the previous tick — drop the dead/half-open socket\n ws.terminate();\n\n return;\n }\n\n ws.isAlive = false;\n ws.ping();\n }, pingInterval);\n }\n\n // ── 9. Send connected ack ──\n connection.send('__connected', {\n subscribedEvents: allowedEvents,\n timestamp: Date.now(),\n });\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction parseURL(req: any): URL | null\n{\n try\n {\n return new URL(req.url ?? '/', 'ws://localhost');\n }\n catch\n {\n return null;\n }\n}\n\n/**\n * Resolve subject from token\n * - undefined: no auth required\n * - false: token param missing (when required)\n * - null: token invalid/expired\n * - string: authenticated subject\n */\nasync function resolveSubject(\n url: URL,\n tokenManager?: SSETokenManager,\n): Promise<string | undefined | false | null>\n{\n if (!tokenManager)\n {\n return undefined;\n }\n\n const token = url.searchParams.get('token');\n if (!token)\n {\n return false;\n }\n\n return await tokenManager.verify(token);\n}\n\nfunction parseRequestedEvents(url: URL, validEventNames: string[]): string[]\n{\n const eventsParam = url.searchParams.get('events');\n if (!eventsParam)\n {\n return [];\n }\n\n return eventsParam\n .split(',')\n .map(e => e.trim())\n .filter(e => validEventNames.includes(e));\n}\n\nasync function resolveAllowedEvents(\n subject: string | undefined,\n requestedEvents: string[],\n authConfig?: WSHandlerAuthConfig,\n): Promise<string[] | null>\n{\n if (!subject || !authConfig?.authorize)\n {\n return requestedEvents;\n }\n\n const allowed = await authConfig.authorize(subject, requestedEvents);\n\n return allowed.length === 0 ? null : allowed;\n}\n\n/**\n * Send a JSON frame with backpressure protection. If the socket's outbound\n * buffer is already past the cap, the consumer is too slow — close the\n * connection (1013) instead of buffering more and risking OOM. The client\n * reconnects and re-subscribes.\n */\nexport function safeSend(ws: any, frame: unknown, maxBufferedBytes: number): void\n{\n if (ws.readyState !== 1) return;\n\n if (ws.bufferedAmount > maxBufferedBytes)\n {\n ws.close(1013, 'Send buffer overflow');\n\n return;\n }\n\n try\n {\n ws.send(JSON.stringify(frame));\n }\n catch\n {\n // Socket closed between the readyState check and send — ignore\n }\n}\n\nfunction createConnection(ws: any, maxBufferedBytes: number): WSRawConnection\n{\n return {\n send: (type, payload) => safeSend(ws, { type, data: payload }, maxBufferedBytes),\n close: (code, reason) => ws.close(code, reason),\n };\n}\n\nfunction subscribeEvents(\n ws: any,\n router: WSRouterDef<any, any>,\n allowedEvents: string[],\n subject: string | undefined,\n authConfig: WSHandlerAuthConfig | undefined,\n maxBufferedBytes: number,\n): (() => void)[]\n{\n const unsubscribes: (() => void)[] = [];\n\n for (const eventName of allowedEvents)\n {\n const eventDef = router.events[eventName];\n if (!eventDef) continue;\n\n const unsubscribe = eventDef.subscribe((payload: unknown) =>\n {\n if (ws.readyState !== 1) return;\n\n if (subject && authConfig?.filter?.[eventName])\n {\n if (!authConfig.filter[eventName](subject, payload)) return;\n }\n\n safeSend(ws, { type: eventName, data: payload }, maxBufferedBytes);\n });\n\n unsubscribes.push(unsubscribe);\n }\n\n return unsubscribes;\n}\n\nasync function onClientMessage(\n data: Buffer | string,\n router: WSRouterDef<any, any>,\n connection: WSRawConnection,\n subject: string | undefined,\n): Promise<void>\n{\n let message: { type?: string; data?: unknown };\n\n try\n {\n message = JSON.parse(data.toString());\n }\n catch\n {\n return;\n }\n\n const { type, data: payload } = message;\n if (!type) return;\n\n const handler = router.messages[type];\n if (!handler) return;\n\n try\n {\n await handler({ payload, subject, ws: connection });\n }\n catch (err)\n {\n wsLogger.error(`WebSocket message handler error: ${type}`, err as Error);\n }\n}\n\n// ============================================================================\n// Dynamic import for optional 'ws' dependency\n// ============================================================================\n\nasync function loadWSServer(): Promise<any>\n{\n try\n {\n // ws is a CJS package: module.exports = WebSocket, WebSocket.WebSocketServer is set on it.\n // ESM dynamic import wraps CJS default export under .default\n \n const mod = await import('ws') as any;\n const WS = mod.default ?? mod;\n const WSS = WS.WebSocketServer ?? WS.Server;\n\n if (typeof WSS !== 'function')\n {\n throw new Error(\n 'WebSocketServer not found in ws module. ' +\n 'Ensure ws@^8 is installed: pnpm add ws',\n );\n }\n\n return WSS;\n }\n catch (err)\n {\n if (err instanceof Error && err.message.includes('WebSocketServer not found'))\n {\n throw err;\n }\n throw new Error(\n '@spfn/core WebSocket support requires the \"ws\" package.\\n' +\n 'Install it with: pnpm add ws',\n );\n }\n}\n","/**\n * WebSocket Module\n *\n * Type-safe WebSocket server with event-based pub/sub and bidirectional messaging.\n *\n * @example Server setup\n * ```typescript\n * // src/server/ws.ts\n * import { defineWSRouter } from '@spfn/core/event/ws';\n * import { defineEvent } from '@spfn/core/event';\n * import { Type } from '@sinclair/typebox';\n *\n * const userUpdated = defineEvent('user.updated', Type.Object({ userId: Type.String() }));\n * const notification = defineEvent('notification', Type.Object({ message: Type.String() }));\n *\n * export const wsRouter = defineWSRouter({\n * events: { userUpdated, notification },\n * messages: {\n * ping: ({ ws }) => ws.send('pong', {}),\n * },\n * });\n *\n * export type WSRouter = typeof wsRouter;\n *\n * // server.config.ts\n * defineServerConfig()\n * .websockets(wsRouter)\n * .build();\n * ```\n *\n * @example Client usage\n * ```typescript\n * import { createWSClient } from '@spfn/core/event/ws/client';\n * import type { WSRouter } from '@/server/ws';\n *\n * const client = createWSClient<WSRouter>();\n *\n * client.subscribe({\n * events: ['userUpdated', 'notification'],\n * handlers: {\n * userUpdated: ({ userId }) => console.log(userId),\n * notification: ({ message }) => console.log(message),\n * },\n * });\n *\n * client.send('ping', {});\n * ```\n */\n\nimport type { EventDef } from '../types';\nimport type { WSRouterDef, WSMessageHandlers } from './types';\n\nexport { attachWSHandler } from './handler';\nexport type {\n WSRouterDef,\n WSHandlerConfig,\n WSAuthConfig,\n WSHandlerAuthConfig,\n WSMessageContext,\n WSMessageHandlerFn,\n WSMessageHandlers,\n WSRawConnection,\n WSClientConfig,\n WSConnectionState,\n WSEventHandlers,\n WSSubscribeOptions,\n WSUnsubscribe,\n} from './types';\n\n/**\n * Define a WebSocket router\n *\n * Combines server→client event push with client→server message handlers.\n *\n * @example\n * ```typescript\n * export const wsRouter = defineWSRouter({\n * events: { userUpdated, notification },\n * messages: {\n * ping: ({ ws }) => ws.send('pong', {}),\n * 'chat.send': ({ payload, subject }) => handleChat(payload, subject),\n * },\n * });\n * ```\n */\nexport function defineWSRouter<\n TEvents extends Record<string, EventDef<any>>,\n TMessages extends WSMessageHandlers = WSMessageHandlers,\n>(def: {\n events: TEvents;\n messages?: TMessages;\n}): WSRouterDef<TEvents, TMessages>\n{\n return {\n events: def.events,\n eventNames: Object.keys(def.events) as (keyof TEvents)[],\n messages: (def.messages ?? {}) as TMessages,\n _types: {} as WSRouterDef<TEvents, TMessages>['_types'],\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as EventRouterDef, I as InferEventNames } from '../../token-manager-
|
|
2
|
-
import { g as SSESubscribeOptions, i as SSEUnsubscribe, h as SSEConnectionState, d as SSEClientConfig } from '../../types-
|
|
1
|
+
import { E as EventRouterDef, I as InferEventNames } from '../../token-manager-jKD_EsSE.js';
|
|
2
|
+
import { g as SSESubscribeOptions, i as SSEUnsubscribe, h as SSEConnectionState, d as SSEClientConfig } from '../../types-BFB72jbM.js';
|
|
3
3
|
import '@sinclair/typebox';
|
|
4
4
|
import 'hono';
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Context } from 'hono';
|
|
2
|
-
import { E as EventRouterDef, S as SSETokenManager } from '../../token-manager-
|
|
3
|
-
export { C as CacheTokenStore, g as SSEToken, i as SSETokenManagerConfig, h as SSETokenStore } from '../../token-manager-
|
|
4
|
-
import { S as SSEHandlerConfig } from '../../types-
|
|
5
|
-
export { a as SSEAuthConfig, d as SSEClientConfig, h as SSEConnectionState, e as SSEEventHandler, f as SSEEventHandlers, c as SSEHandlerAuthConfig, b as SSEMessage, g as SSESubscribeOptions, i as SSEUnsubscribe } from '../../types-
|
|
2
|
+
import { E as EventRouterDef, S as SSETokenManager } from '../../token-manager-jKD_EsSE.js';
|
|
3
|
+
export { C as CacheTokenStore, g as SSEToken, i as SSETokenManagerConfig, h as SSETokenStore } from '../../token-manager-jKD_EsSE.js';
|
|
4
|
+
import { S as SSEHandlerConfig } from '../../types-BFB72jbM.js';
|
|
5
|
+
export { a as SSEAuthConfig, d as SSEClientConfig, h as SSEConnectionState, e as SSEEventHandler, f as SSEEventHandlers, c as SSEHandlerAuthConfig, b as SSEMessage, g as SSESubscribeOptions, i as SSEUnsubscribe } from '../../types-BFB72jbM.js';
|
|
6
6
|
import '@sinclair/typebox';
|
|
7
7
|
|
|
8
8
|
/**
|
package/dist/event/sse/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { streamSSE } from 'hono/streaming';
|
|
2
2
|
import { logger } from '@spfn/core/logger';
|
|
3
|
-
import { randomBytes } from 'crypto';
|
|
3
|
+
import { randomBytes, createHash } from 'crypto';
|
|
4
4
|
|
|
5
5
|
// src/event/sse/handler.ts
|
|
6
6
|
|
|
@@ -50,6 +50,23 @@ function createBoundedWriter(stream, maxQueue, onClose) {
|
|
|
50
50
|
|
|
51
51
|
// src/event/sse/handler.ts
|
|
52
52
|
var sseLogger = logger.child("@spfn/core:sse");
|
|
53
|
+
var frameCache = /* @__PURE__ */ new WeakMap();
|
|
54
|
+
function serializeFrame(eventName, payload) {
|
|
55
|
+
if (payload === null || typeof payload !== "object") {
|
|
56
|
+
return JSON.stringify({ event: eventName, data: payload });
|
|
57
|
+
}
|
|
58
|
+
let byEvent = frameCache.get(payload);
|
|
59
|
+
if (!byEvent) {
|
|
60
|
+
byEvent = /* @__PURE__ */ new Map();
|
|
61
|
+
frameCache.set(payload, byEvent);
|
|
62
|
+
}
|
|
63
|
+
let serialized = byEvent.get(eventName);
|
|
64
|
+
if (serialized === void 0) {
|
|
65
|
+
serialized = JSON.stringify({ event: eventName, data: payload });
|
|
66
|
+
byEvent.set(eventName, serialized);
|
|
67
|
+
}
|
|
68
|
+
return serialized;
|
|
69
|
+
}
|
|
53
70
|
var DEFAULT_MAX_QUEUE = 1e3;
|
|
54
71
|
function createSSEHandler(router, config = {}, tokenManager) {
|
|
55
72
|
const {
|
|
@@ -100,12 +117,14 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
100
117
|
let connectionDead = false;
|
|
101
118
|
let pingTimer;
|
|
102
119
|
let writer;
|
|
120
|
+
let onClosed;
|
|
103
121
|
const cleanup = (reason) => {
|
|
104
122
|
if (connectionDead) return;
|
|
105
123
|
connectionDead = true;
|
|
106
124
|
clearInterval(pingTimer);
|
|
107
125
|
unsubscribes.forEach((fn) => fn());
|
|
108
126
|
writer?.close();
|
|
127
|
+
onClosed?.();
|
|
109
128
|
sseLogger.info("SSE dead connection cleaned up", {
|
|
110
129
|
events: allowedEvents,
|
|
111
130
|
reason
|
|
@@ -135,10 +154,6 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
135
154
|
}
|
|
136
155
|
}
|
|
137
156
|
messageId++;
|
|
138
|
-
const message = {
|
|
139
|
-
event: eventName,
|
|
140
|
-
data: payload
|
|
141
|
-
};
|
|
142
157
|
sseLogger.debug("SSE sending event", {
|
|
143
158
|
event: eventName,
|
|
144
159
|
messageId
|
|
@@ -146,7 +161,7 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
146
161
|
writer.enqueue({
|
|
147
162
|
id: String(messageId),
|
|
148
163
|
event: eventName,
|
|
149
|
-
data:
|
|
164
|
+
data: serializeFrame(eventName, payload)
|
|
150
165
|
});
|
|
151
166
|
});
|
|
152
167
|
unsubscribes.push(unsubscribe);
|
|
@@ -163,8 +178,15 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
163
178
|
});
|
|
164
179
|
}, pingInterval);
|
|
165
180
|
const abortSignal = c.req.raw.signal;
|
|
166
|
-
|
|
167
|
-
await
|
|
181
|
+
if (!abortSignal.aborted && !connectionDead) {
|
|
182
|
+
await new Promise((resolve) => {
|
|
183
|
+
const onAbort = () => resolve();
|
|
184
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
185
|
+
onClosed = () => {
|
|
186
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
187
|
+
resolve();
|
|
188
|
+
};
|
|
189
|
+
});
|
|
168
190
|
}
|
|
169
191
|
cleanup();
|
|
170
192
|
}, async (err) => {
|
|
@@ -201,24 +223,28 @@ async function authorizeEvents(subject, requestedEvents, authConfig) {
|
|
|
201
223
|
}
|
|
202
224
|
return allowed;
|
|
203
225
|
}
|
|
226
|
+
function hashToken(token) {
|
|
227
|
+
return createHash("sha256").update(token).digest("hex");
|
|
228
|
+
}
|
|
204
229
|
var InMemoryTokenStore = class {
|
|
205
230
|
tokens = /* @__PURE__ */ new Map();
|
|
206
231
|
async set(token, data) {
|
|
207
|
-
this.tokens.set(token, data);
|
|
232
|
+
this.tokens.set(hashToken(token), data);
|
|
208
233
|
}
|
|
209
234
|
async consume(token) {
|
|
210
|
-
const
|
|
235
|
+
const key = hashToken(token);
|
|
236
|
+
const data = this.tokens.get(key);
|
|
211
237
|
if (!data) {
|
|
212
238
|
return null;
|
|
213
239
|
}
|
|
214
|
-
this.tokens.delete(
|
|
240
|
+
this.tokens.delete(key);
|
|
215
241
|
return data;
|
|
216
242
|
}
|
|
217
243
|
async cleanup() {
|
|
218
244
|
const now = Date.now();
|
|
219
|
-
for (const [
|
|
245
|
+
for (const [key, data] of this.tokens) {
|
|
220
246
|
if (data.expiresAt <= now) {
|
|
221
|
-
this.tokens.delete(
|
|
247
|
+
this.tokens.delete(key);
|
|
222
248
|
}
|
|
223
249
|
}
|
|
224
250
|
}
|
|
@@ -231,14 +257,14 @@ var CacheTokenStore = class {
|
|
|
231
257
|
async set(token, data) {
|
|
232
258
|
const ttlSeconds = Math.max(1, Math.ceil((data.expiresAt - Date.now()) / 1e3));
|
|
233
259
|
await this.cache.set(
|
|
234
|
-
this.prefix + token,
|
|
260
|
+
this.prefix + hashToken(token),
|
|
235
261
|
JSON.stringify(data),
|
|
236
262
|
"EX",
|
|
237
263
|
ttlSeconds
|
|
238
264
|
);
|
|
239
265
|
}
|
|
240
266
|
async consume(token) {
|
|
241
|
-
const key = this.prefix + token;
|
|
267
|
+
const key = this.prefix + hashToken(token);
|
|
242
268
|
let raw = null;
|
|
243
269
|
if (this.cache.getdel) {
|
|
244
270
|
raw = await this.cache.getdel(key);
|
|
@@ -273,7 +299,6 @@ var SSETokenManager = class {
|
|
|
273
299
|
async issue(subject) {
|
|
274
300
|
const token = randomBytes(32).toString("hex");
|
|
275
301
|
await this.store.set(token, {
|
|
276
|
-
token,
|
|
277
302
|
subject,
|
|
278
303
|
expiresAt: Date.now() + this.ttl
|
|
279
304
|
});
|