@fitzzero/quickdraw-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shared/types.ts","../../src/server/ServiceRegistry.ts","../../src/server/createServer.ts","../../src/server/testing.ts"],"names":["result","SocketIOServer","ioClient"],"mappings":";;;;;;;;AA8KO,IAAM,aAAA,GAAwB;AAAA,EACnC,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,IAAI,CAAA,OAAA,EAAU,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACpE,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,KAAK,CAAA,OAAA,EAAU,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACrE,KAAA,EAAO,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,MAAM,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACxE,KAAA,EAAO,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,MAAM,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACxE,OAAO,MAAM;AACf,CAAA;;;ACvJO,IAAM,kBAAN,MAAyD;AAAA,EAC7C,EAAA;AAAA,EACA,QAAA,uBAAiD,GAAA,EAAI;AAAA,EACrD,MAAA;AAAA,EAEjB,WAAA,CAAY,IAAoB,OAAA,EAA+B;AAC7D,IAAA,IAAA,CAAK,EAAA,GAAK,EAAA;AACV,IAAA,IAAA,CAAK,MAAA,GAAS,OAAA,EAAS,MAAA,EAAQ,KAAA,CAAM,EAAE,OAAA,EAAS,iBAAA,EAAmB,CAAA,IACjE,aAAA,CAAc,KAAA,CAAM,EAAE,OAAA,EAAS,mBAAmB,CAAA;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKO,eAAA,CACL,aACA,OAAA,EACM;AACN,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,WAAA,EAAa,OAAO,CAAA;AACtC,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,oBAAA,EAAuB,WAAW,CAAA,CAAE,CAAA;AAGrD,IAAA,IAAA,CAAK,sBAAA,CAAuB,aAAa,OAAO,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAA,CACN,aACA,OAAA,EACM;AACN,IAAA,MAAM,OAAA,GAAU,QAAQ,gBAAA,EAAiB;AAEzC,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,MAAM,SAAA,GAAY,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,OAAO,IAAI,CAAA,CAAA;AAC/C,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,0BAAA,EAA6B,SAAS,CAAA,CAAE,CAAA;AAGzD,MAAA,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,YAAA,EAAc,CAAC,MAAA,KAAW;AACnC,QAAA,IAAA,CAAK,sBAAA;AAAA,UACH,MAAA;AAAA,UACA,SAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,YAAA,EAAc,CAAC,MAAA,KAAW;AACnC,MAAA,IAAA,CAAK,4BAAA;AAAA,QACH,MAAA;AAAA,QACA,GAAG,WAAW,CAAA,UAAA,CAAA;AAAA,QACd;AAAA,OACF;AACA,MAAA,IAAA,CAAK,8BAAA;AAAA,QACH,MAAA;AAAA,QACA,GAAG,WAAW,CAAA,YAAA,CAAA;AAAA,QACd;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAA,CACN,MAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,EACM;AACN,IAAA,MAAA,CAAO,EAAA;AAAA,MACL,SAAA;AAAA,MACA,OACE,SACA,QAAA,KACG;AACH,QAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,QAAA,MAAM,CAAC,WAAA,EAAa,UAAU,CAAA,GAAI,SAAA,CAAU,MAAM,GAAG,CAAA;AACrD,QAAA,MAAM,cAAc,MAAA,CAAO,MAAA,EAAQ,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,IAAK,MAAA;AAElD,QAAA,IAAI;AAEF,UAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,WAAW,QAAA,EAAU;AAChD,YAAA,MAAM,aAAA,GAA0C;AAAA,cAC9C,OAAA,EAAS,KAAA;AAAA,cACT,KAAA,EAAO,yBAAA;AAAA,cACP,IAAA,EAAM;AAAA,aACR;AACA,YAAA,QAAA,GAAW,aAAa,CAAA;AACxB,YAAA;AAAA,UACF;AAGA,UAAA,IAAI,gBAAA,GAAmB,OAAA;AACvB,UAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,YAAA,MAAMA,OAAAA,GAAS,MAAA,CAAO,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA;AAC9C,YAAA,IAAI,CAACA,QAAO,OAAA,EAAS;AACnB,cAAA,MAAM,aAAA,GAA0C;AAAA,gBAC9C,OAAA,EAAS,KAAA;AAAA,gBACT,KAAA,EAAO,CAAA,kBAAA,EAAqBA,OAAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,gBAChD,IAAA,EAAM;AAAA,eACR;AACA,cAAA,QAAA,GAAW,aAAa,CAAA;AACxB,cAAA;AAAA,YACF;AACA,YAAA,gBAAA,GAAmBA,OAAAA,CAAO,IAAA;AAAA,UAC5B;AAGA,UAAA,IAAI,OAAA;AACJ,UAAA,IAAI,OAAO,cAAA,EAAgB;AACzB,YAAA,OAAA,GAAU,MAAA,CAAO,cAAA,CAAe,gBAAgB,CAAA,IAAK,KAAA,CAAA;AAAA,UACvD,WACE,gBAAA,IACA,OAAO,gBAAA,KAAqB,QAAA,IAC5B,QAAQ,gBAAA,EACR;AACA,YAAA,MAAM,UAAW,gBAAA,CAA6C,EAAA;AAC9D,YAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,cAAA,OAAA,GAAU,OAAA;AAAA,YACZ;AAAA,UACF;AAGA,UAAA,MAAM,OAAA,CAAQ,qBAAA,CAAsB,MAAA,CAAO,MAAA,EAAQ,QAAQ,OAAO,CAAA;AAGlE,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,YACV,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAA,EAAI,WAAW,IAAI,UAAU,CAAA,SAAA,CAAA;AAAA,YAChD;AAAA,cACE,QAAA,EAAU,oBAAA;AAAA,cACV,WAAA;AAAA,cACA,UAAA;AAAA,cACA,QAAQ,MAAA,CAAO,MAAA;AAAA,cACf,UAAU,MAAA,CAAO;AAAA;AACnB,WACF;AAGA,UAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,OAAA,CAAQ,gBAAA,EAAkB;AAAA,YACpD,QAAQ,MAAA,CAAO,MAAA;AAAA,YACf,UAAU,MAAA,CAAO,EAAA;AAAA,YACjB,aAAA,EAAe,MAAA,CAAO,aAAA,IAAiB;AAAC,WACzC,CAAA;AAED,UAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAChC,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,YACV,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAA,EAAI,WAAW,IAAI,UAAU,CAAA,WAAA,CAAA;AAAA,YAChD;AAAA,cACE,QAAA,EAAU,oBAAA;AAAA,cACV,OAAA,EAAS,SAAA;AAAA,cACT,UAAA;AAAA,cACA,WAAA;AAAA,cACA,UAAA;AAAA,cACA,QAAQ,MAAA,CAAO;AAAA;AACjB,WACF;AAEA,UAAA,MAAM,eAAA,GAA4C;AAAA,YAChD,OAAA,EAAS,IAAA;AAAA,YACT,IAAA,EAAM;AAAA,WACR;AACA,UAAA,QAAA,GAAW,eAAe,CAAA;AAAA,QAC5B,SAAS,KAAA,EAAO;AACd,UAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAChC,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,YACV,CAAA,KAAA,EAAQ,WAAW,CAAA,CAAA,EAAI,WAAW,IAAI,UAAU,CAAA,QAAA,CAAA;AAAA,YAChD;AAAA,cACE,QAAA,EAAU,oBAAA;AAAA,cACV,OAAA,EAAS,MAAA;AAAA,cACT,UAAA;AAAA,cACA,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA;AAClD,WACF;AAEA,UAAA,MAAM,aAAA,GAA0C;AAAA,YAC9C,OAAA,EAAS,KAAA;AAAA,YACT,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,gBAAA;AAAA,YAChD,IAAA,EAAM;AAAA,WACR;AACA,UAAA,QAAA,GAAW,aAAa,CAAA;AAAA,QAC1B;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,4BAAA,CACN,MAAA,EACA,SAAA,EACA,OAAA,EACM;AACN,IAAA,MAAA,CAAO,EAAA;AAAA,MACL,SAAA;AAAA,MACA,OACE,SACA,QAAA,KACG;AACH,QAAA,IAAI;AACF,UAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,YAAA,QAAA,GAAW;AAAA,cACT,OAAA,EAAS,KAAA;AAAA,cACT,KAAA,EAAO,yBAAA;AAAA,cACP,IAAA,EAAM;AAAA,aACP,CAAA;AACD,YAAA;AAAA,UACF;AAEA,UAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,SAAA;AAAA,YACzB,OAAA,CAAQ,OAAA;AAAA,YACR,MAAA;AAAA,YACC,QAAQ,aAAA,IAAmD;AAAA,WAC9D;AAEA,UAAA,IAAI,SAAS,IAAA,EAAM;AACjB,YAAA,QAAA,GAAW;AAAA,cACT,OAAA,EAAS,KAAA;AAAA,cACT,KAAA,EAAO,kCAAA;AAAA,cACP,IAAA,EAAM;AAAA,aACP,CAAA;AACD,YAAA;AAAA,UACF;AAEA,UAAA,QAAA,GAAW,EAAE,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,QACpC,SAAS,KAAA,EAAO;AACd,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,sBAAA,EAAyB,SAAS,CAAA,CAAA,CAAA,EAAK;AAAA,YACvD,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,WACjD,CAAA;AACD,UAAA,QAAA,GAAW;AAAA,YACT,OAAA,EAAS,KAAA;AAAA,YACT,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,WACjD,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,8BAAA,CACN,MAAA,EACA,SAAA,EACA,OAAA,EACM;AACN,IAAA,MAAA,CAAO,EAAA;AAAA,MACL,SAAA;AAAA,MACA,CACE,SACA,QAAA,KAGG;AACH,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,WAAA,CAAY,OAAA,CAAQ,OAAA,EAAS,MAAM,CAAA;AAC3C,UAAA,QAAA,GAAW;AAAA,YACT,OAAA,EAAS,IAAA;AAAA,YACT,MAAM,EAAE,YAAA,EAAc,IAAA,EAAM,OAAA,EAAS,QAAQ,OAAA;AAAQ,WACtD,CAAA;AAAA,QACH,SAAS,KAAA,EAAO;AACd,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,CAAA,CAAA,CAAA,EAAK;AAAA,YACzD,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,WACjD,CAAA;AACD,UAAA,QAAA,GAAW;AAAA,YACT,OAAA,EAAS,KAAA;AAAA,YACT,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,WACjD,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,WAAA,GAAwB;AAC7B,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKO,mBAAA,GAA6C;AAClD,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AAAA,EAC1C;AACF,CAAA;;;AC3RO,SAAS,sBACd,OAAA,EACuB;AACvB,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,aAAA;AACjC,EAAA,MAAM,eAAe,MAAA,CAAO,KAAA,CAAM,EAAE,OAAA,EAAS,mBAAmB,CAAA;AAGhE,EAAA,MAAM,MAAM,OAAA,EAAQ;AACpB,EAAA,GAAA,CAAI,GAAA,CAAI,OAAA,CAAQ,IAAA,EAAM,CAAA;AAGtB,EAAA,GAAA,CAAI,GAAA,CAAI,SAAA,EAAW,CAAC,IAAA,EAAM,GAAA,KAAQ;AAChC,IAAA,GAAA,CAAI,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAA,qBAAe,IAAA,EAAK,EAAE,WAAA,EAAY,EAAG,CAAA;AAAA,EAChE,CAAC,CAAA;AAGD,EAAA,MAAM,UAAA,GAAa,aAAa,GAAG,CAAA;AAGnC,EAAA,MAAM,EAAA,GAAK,IAAIC,MAAA,CAAe,UAAA,EAAY;AAAA,IACxC,IAAA,EAAM;AAAA,MACJ,MAAA,EAAQ,OAAA,CAAQ,IAAA,EAAM,MAAA,IAAU,GAAA;AAAA,MAChC,SAAS,OAAA,CAAQ,IAAA,EAAM,OAAA,IAAW,CAAC,OAAO,MAAM,CAAA;AAAA,MAChD,WAAA,EAAa,OAAA,CAAQ,IAAA,EAAM,WAAA,IAAe;AAAA;AAC5C,GACD,CAAA;AAGD,EAAA,MAAM,WAAW,IAAI,eAAA,CAAgB,EAAA,EAAI,EAAE,QAAQ,CAAA;AAGnD,EAAA,EAAA,CAAG,GAAA,CAAI,OAAO,MAAA,EAAQ,IAAA,KAAS;AAC7B,IAAA,MAAM,eAAA,GAAkB,MAAA;AAExB,IAAA,IAAI;AACF,MAAA,IAAI,OAAA,CAAQ,MAAM,YAAA,EAAc;AAC9B,QAAA,MAAM,IAAA,GAAO,OAAO,SAAA,CAAU,IAAA;AAC9B,QAAA,MAAM,SAAS,MAAM,OAAA,CAAQ,IAAA,CAAK,YAAA,CAAa,iBAAiB,IAAI,CAAA;AACpE,QAAA,eAAA,CAAgB,MAAA,GAAS,MAAA;AAIzB,QAAA,eAAA,CAAgB,gBAAgB,EAAC;AAAA,MACnC;AACA,MAAA,IAAA,EAAK;AAAA,IACP,SAAS,KAAA,EAAO;AACd,MAAA,YAAA,CAAa,MAAM,uBAAA,EAAyB;AAAA,QAC1C,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,eAAA;AAAA,QAChD,UAAU,MAAA,CAAO;AAAA,OAClB,CAAA;AACD,MAAA,IAAA,CAAK,IAAI,KAAA,CAAM,uBAAuB,CAAC,CAAA;AAAA,IACzC;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,KAAA,MAAW,CAAC,aAAa,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACrE,IAAA,QAAA,CAAS,eAAA,CAAgB,aAAa,OAAO,CAAA;AAAA,EAC/C;AAGA,EAAA,EAAA,CAAG,EAAA,CAAG,YAAA,EAAc,CAAC,MAAA,KAAW;AAC9B,IAAA,MAAM,eAAA,GAAkB,MAAA;AACxB,IAAA,YAAA,CAAa,KAAK,kBAAA,EAAoB;AAAA,MACpC,UAAU,eAAA,CAAgB,EAAA;AAAA,MAC1B,QAAQ,eAAA,CAAgB;AAAA,KACzB,CAAA;AAED,IAAA,eAAA,CAAgB,EAAA,CAAG,cAAc,MAAM;AACrC,MAAA,YAAA,CAAa,KAAK,qBAAA,EAAuB;AAAA,QACvC,UAAU,eAAA,CAAgB,EAAA;AAAA,QAC1B,QAAQ,eAAA,CAAgB;AAAA,OACzB,CAAA;AAGD,MAAA,KAAA,MAAW,OAAA,IAAW,QAAA,CAAS,mBAAA,EAAoB,EAAG;AACpD,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,kBAAkB,eAAe,CAAA;AAAA,QAC3C,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AAGD,EAAA,UAAA,CAAW,MAAA,CAAO,OAAA,CAAQ,IAAA,EAAM,SAAA,EAAW,MAAM;AAC/C,IAAA,YAAA,CAAa,IAAA,CAAK,CAAA,yBAAA,EAA4B,OAAA,CAAQ,IAAI,CAAA,CAAE,CAAA;AAC5D,IAAA,YAAA,CAAa,IAAA;AAAA,MACX,wBAAwB,QAAA,CAAS,WAAA,EAAY,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAC3D;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,EAAA,EAAI,UAAA,EAAY,QAAA,EAAS;AACpC;;;AC9FA,IAAI,QAAA,GAAW,GAAA;AAKR,SAAS,gBAAA,GAA2B;AACzC,EAAA,OAAO,QAAA,EAAA;AACT;AAsBA,eAAsB,iBACpB,OAAA,EACqB;AACrB,EAAA,MAAM,OAAO,gBAAA,EAAiB;AAE9B,EAAA,MAAM,SAAS,qBAAA,CAAsB;AAAA,IACnC,IAAA;AAAA,IACA,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,IAAA,EAAM;AAAA;AAAA,MAEJ,YAAA,EAAc,OAAO,OAAA,EAAS,IAAA,KAAS;AACrC,QAAA,OAAO,IAAA,CAAK,MAAA;AAAA,MACd;AAAA;AACF,GACD,CAAA;AAGD,EAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACnC,IAAA,MAAA,CAAO,UAAA,CAAW,IAAA,CAAK,WAAA,EAAa,OAAO,CAAA;AAAA,EAC7C,CAAC,CAAA;AAGD,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAM,QAAQ,MAAA,EAAO;AAAA,EACvB;AAEA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA,EAAM,MACJ,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AAC7B,MAAA,MAAA,CAAO,GAAG,KAAA,EAAM;AAChB,MAAA,MAAA,CAAO,UAAA,CAAW,KAAA,CAAM,MAAM,OAAA,EAAS,CAAA;AAAA,IACzC,CAAC,CAAA;AAAA,IACH,SAAA,EAAW,CAAC,MAAA,KAAmB,aAAA,CAAc,MAAM,MAAM;AAAA,GAC3D;AACF;AAKA,eAAsB,aAAA,CACpB,MACA,MAAA,EACqB;AACrB,EAAA,MAAM,MAAA,GAASC,EAAA,CAAS,CAAA,iBAAA,EAAoB,IAAI,CAAA,CAAA,EAAI;AAAA,IAClD,IAAA,EAAM,EAAE,MAAA,EAAO;AAAA,IACf,UAAA,EAAY,CAAC,WAAW,CAAA;AAAA,IACxB,WAAA,EAAa;AAAA,GACd,CAAA;AAED,EAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,IAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,oBAAoB,CAAC,CAAA;AAAA,IACxC,GAAG,GAAI,CAAA;AAEP,IAAA,MAAA,CAAO,EAAA,CAAG,WAAW,MAAM;AACzB,MAAA,YAAA,CAAa,OAAO,CAAA;AACpB,MAAA,OAAA,EAAQ;AAAA,IACV,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,eAAA,EAAiB,CAAC,GAAA,KAAQ;AAClC,MAAA,YAAA,CAAa,OAAO,CAAA;AACpB,MAAA,MAAA,CAAO,GAAG,CAAA;AAAA,IACZ,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,MAAM,CAAsB,KAAA,EAAe,YACzC,WAAA,CAAiC,MAAA,EAAQ,OAAO,OAAO,CAAA;AAAA,IACzD,KAAA,EAAO,MAAM,MAAA,CAAO,KAAA;AAAM,GAC5B;AACF;AAKO,SAAS,WAAA,CACd,MAAA,EACA,KAAA,EACA,OAAA,EACA,YAAoB,GAAA,EACA;AACpB,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,KAAK,EAAE,CAAC,CAAA;AAAA,IAClD,GAAG,SAAS,CAAA;AAEZ,IAAA,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,OAAA,EAAS,CAAC,QAAA,KAAyC;AACpE,MAAA,YAAA,CAAa,OAAO,CAAA;AAEpB,MAAA,IAAI,SAAS,OAAA,EAAS;AACpB,QAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,MAClC;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAKO,SAAS,YAAA,CACd,MAAA,EACA,KAAA,EACA,SAAA,GAAoB,GAAA,EACR;AACZ,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,MAAA,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AACzB,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,KAAK,EAAE,CAAC,CAAA;AAAA,IACxD,GAAG,SAAS,CAAA;AAEZ,IAAA,MAAM,OAAA,GAAU,CAAC,IAAA,KAAY;AAC3B,MAAA,YAAA,CAAa,OAAO,CAAA;AACpB,MAAA,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AACzB,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA;AAEA,IAAA,MAAA,CAAO,EAAA,CAAG,OAAO,OAAO,CAAA;AAAA,EAC1B,CAAC,CAAA;AACH","file":"testing.js","sourcesContent":["import type { z } from \"zod\";\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport type AccessLevel = \"Public\" | \"Read\" | \"Moderate\" | \"Admin\";\n\nexport type ACE = {\n userId: string;\n level: AccessLevel;\n};\n\nexport type ACL = ACE[];\n\n// ============================================================================\n// Service Response Types\n// ============================================================================\n\nexport type ServiceResponse<T = unknown> =\n | { success: true; data: T }\n | { success: false; error: string; code?: number };\n\n// ============================================================================\n// Service Method Definition Types\n// ============================================================================\n\n/**\n * Defines the shape of a service method including its payload and response types.\n * Used to create a type-safe contract between server and client.\n */\nexport interface ServiceMethodDefinition<\n TPayload = unknown,\n TResponse = unknown,\n> {\n name: string;\n access: AccessLevel;\n handler: (\n payload: TPayload,\n context: ServiceMethodContext\n ) => Promise<TResponse>;\n schema?: z.ZodType<TPayload>;\n resolveEntryId?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to service method handlers\n */\nexport interface ServiceMethodContext {\n userId: string | undefined;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service method maps.\n * Each service should define its methods using this structure.\n *\n * @example\n * ```typescript\n * type ChatServiceMethods = ServiceMethodMap<{\n * createChat: {\n * payload: { title: string };\n * response: { id: string };\n * };\n * updateTitle: {\n * payload: { id: string; title: string };\n * response: { id: string; title: string };\n * };\n * }>;\n * ```\n */\nexport type ServiceMethodMap<\n T extends Record<string, { payload: unknown; response: unknown }>,\n> = T;\n\n// ============================================================================\n// Admin Method Types (Generic shapes for all services)\n// ============================================================================\n\nexport type AdminListPayload = {\n page?: number;\n pageSize?: number;\n sort?: {\n field?: string;\n direction?: \"asc\" | \"desc\";\n };\n filter?: {\n id?: string;\n ids?: string[];\n createdAfter?: string;\n createdBefore?: string;\n updatedAfter?: string;\n updatedBefore?: string;\n } & Record<string, unknown>;\n};\n\nexport type AdminListResponse<T> = {\n rows: T[];\n page: number;\n pageSize: number;\n total: number;\n};\n\nexport type AdminGetPayload = { id: string };\n\nexport type AdminCreatePayload<TInsert> = { data: Partial<TInsert> };\n\nexport type AdminUpdatePayload<TInsert> = {\n id: string;\n data: Partial<TInsert>;\n};\n\nexport type AdminDeletePayload = { id: string };\nexport type AdminDeleteResponse = { id: string; deleted: true };\n\nexport type AdminSetEntryACLPayload = {\n id: string;\n acl: ACL;\n};\n\nexport type AdminGetSubscribersPayload = { id: string };\nexport type AdminGetSubscribersResponse = {\n id: string;\n subscribers: Array<{ socketId: string; userId?: string }>;\n};\n\nexport type AdminReemitPayload = { id: string };\nexport type AdminReemitResponse = { emitted: boolean };\n\nexport type AdminUnsubscribeAllPayload = { id: string };\nexport type AdminUnsubscribeAllResponse = { id: string; unsubscribed: number };\n\n// ============================================================================\n// Subscription Types\n// ============================================================================\n\nexport type SubscribePayload = {\n entryId: string;\n requiredLevel?: AccessLevel;\n};\n\nexport type UnsubscribePayload = {\n entryId: string;\n};\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Extracts payload type from a service method definition\n */\nexport type ExtractPayload<T> = T extends { payload: infer P } ? P : never;\n\n/**\n * Extracts response type from a service method definition\n */\nexport type ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\n/**\n * Logger interface compatible with Winston and other loggers\n */\nexport interface Logger {\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n child(options: Record<string, unknown>): Logger;\n}\n\n/**\n * Default console logger implementation\n */\nexport const consoleLogger: Logger = {\n info: (message, meta) => console.log(`[INFO] ${message}`, meta ?? \"\"),\n warn: (message, meta) => console.warn(`[WARN] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[ERROR] ${message}`, meta ?? \"\"),\n debug: (message, meta) => console.debug(`[DEBUG] ${message}`, meta ?? \"\"),\n child: () => consoleLogger,\n};\n","import type { Server as SocketIOServer } from \"socket.io\";\nimport type {\n ServiceResponse,\n ServiceMethodDefinition,\n Logger,\n} from \"../shared/types\";\nimport { consoleLogger } from \"../shared/types\";\nimport type {\n QuickdrawSocket,\n BaseServiceInstance,\n ServiceRegistryInstance,\n} from \"./types\";\n\n/**\n * Central registry for auto-discovering and registering service methods as Socket.io events.\n *\n * Features:\n * - Auto-discovers public methods from registered services\n * - Wires methods to Socket.io events with standardized naming\n * - Handles subscription lifecycle\n * - Provides consistent error handling and logging\n *\n * @example\n * ```typescript\n * const registry = new ServiceRegistry(io, { logger });\n * registry.registerService('chatService', chatService);\n * registry.registerService('userService', userService);\n * ```\n */\nexport class ServiceRegistry implements ServiceRegistryInstance {\n private readonly io: SocketIOServer;\n private readonly services: Map<string, BaseServiceInstance> = new Map();\n private readonly logger: Logger;\n\n constructor(io: SocketIOServer, options?: { logger?: Logger }) {\n this.io = io;\n this.logger = options?.logger?.child({ service: \"ServiceRegistry\" }) ??\n consoleLogger.child({ service: \"ServiceRegistry\" });\n }\n\n /**\n * Register a service and auto-discover its public methods.\n */\n public registerService(\n serviceName: string,\n service: BaseServiceInstance\n ): void {\n this.services.set(serviceName, service);\n this.logger.info(`Registered service: ${serviceName}`);\n\n // Auto-discover and wire public methods\n this.discoverServiceMethods(serviceName, service);\n }\n\n /**\n * Discover and register all public methods from a service.\n */\n private discoverServiceMethods(\n serviceName: string,\n service: BaseServiceInstance\n ): void {\n const methods = service.getPublicMethods();\n\n for (const method of methods) {\n const eventName = `${serviceName}:${method.name}`;\n this.logger.info(`Registering socket event: ${eventName}`);\n\n // Register on connection\n this.io.on(\"connection\", (socket) => {\n this.registerMethodListener(\n socket as QuickdrawSocket,\n eventName,\n method,\n service\n );\n });\n }\n\n // Register subscription handlers\n this.io.on(\"connection\", (socket) => {\n this.registerSubscriptionListener(\n socket as QuickdrawSocket,\n `${serviceName}:subscribe`,\n service\n );\n this.registerUnsubscriptionListener(\n socket as QuickdrawSocket,\n `${serviceName}:unsubscribe`,\n service\n );\n });\n }\n\n /**\n * Register a method as a socket event listener.\n */\n private registerMethodListener(\n socket: QuickdrawSocket,\n eventName: string,\n method: ServiceMethodDefinition<unknown, unknown>,\n service: BaseServiceInstance\n ): void {\n socket.on(\n eventName,\n async (\n payload: unknown,\n callback?: (response: ServiceResponse<unknown>) => void\n ) => {\n const startedAt = Date.now();\n const [serviceName, methodName] = eventName.split(\":\");\n const userIdShort = socket.userId?.slice(0, 8) ?? \"anon\";\n\n try {\n // Authentication check\n if (!socket.userId && method.access !== \"Public\") {\n const errorResponse: ServiceResponse<unknown> = {\n success: false,\n error: \"Authentication required\",\n code: 401,\n };\n callback?.(errorResponse);\n return;\n }\n\n // Validate payload with Zod schema if provided\n let validatedPayload = payload;\n if (method.schema) {\n const result = method.schema.safeParse(payload);\n if (!result.success) {\n const errorResponse: ServiceResponse<unknown> = {\n success: false,\n error: `Validation error: ${result.error.message}`,\n code: 400,\n };\n callback?.(errorResponse);\n return;\n }\n validatedPayload = result.data;\n }\n\n // Resolve entry ID for access check\n let entryId: string | undefined;\n if (method.resolveEntryId) {\n entryId = method.resolveEntryId(validatedPayload) ?? undefined;\n } else if (\n validatedPayload &&\n typeof validatedPayload === \"object\" &&\n \"id\" in validatedPayload\n ) {\n const idValue = (validatedPayload as Record<string, unknown>).id;\n if (typeof idValue === \"string\") {\n entryId = idValue;\n }\n }\n\n // Check access\n await service.ensureAccessForMethod(method.access, socket, entryId);\n\n // Log start\n this.logger.info(\n `User ${userIdShort} ${serviceName}.${methodName} -> start`,\n {\n category: \"request_processing\",\n serviceName,\n methodName,\n userId: socket.userId,\n socketId: socket.id,\n }\n );\n\n // Execute handler\n const result = await method.handler(validatedPayload, {\n userId: socket.userId,\n socketId: socket.id,\n serviceAccess: socket.serviceAccess ?? {},\n });\n\n const durationMs = Date.now() - startedAt;\n this.logger.info(\n `User ${userIdShort} ${serviceName}.${methodName} -> success`,\n {\n category: \"request_processing\",\n outcome: \"success\",\n durationMs,\n serviceName,\n methodName,\n userId: socket.userId,\n }\n );\n\n const successResponse: ServiceResponse<unknown> = {\n success: true,\n data: result,\n };\n callback?.(successResponse);\n } catch (error) {\n const durationMs = Date.now() - startedAt;\n this.logger.error(\n `User ${userIdShort} ${serviceName}.${methodName} -> fail`,\n {\n category: \"request_processing\",\n outcome: \"fail\",\n durationMs,\n error: error instanceof Error ? error.message : \"Unknown error\",\n }\n );\n\n const errorResponse: ServiceResponse<unknown> = {\n success: false,\n error: error instanceof Error ? error.message : \"Internal error\",\n code: 500,\n };\n callback?.(errorResponse);\n }\n }\n );\n }\n\n /**\n * Register subscription listener for a service.\n */\n private registerSubscriptionListener(\n socket: QuickdrawSocket,\n eventName: string,\n service: BaseServiceInstance\n ): void {\n socket.on(\n eventName,\n async (\n payload: { entryId: string; requiredLevel?: string },\n callback?: (response: ServiceResponse<unknown>) => void\n ) => {\n try {\n if (!socket.userId) {\n callback?.({\n success: false,\n error: \"Authentication required\",\n code: 401,\n });\n return;\n }\n\n const data = await service.subscribe(\n payload.entryId,\n socket,\n (payload.requiredLevel as \"Read\" | \"Moderate\" | \"Admin\") ?? \"Read\"\n );\n\n if (data === null) {\n callback?.({\n success: false,\n error: \"Access denied or entry not found\",\n code: 403,\n });\n return;\n }\n\n callback?.({ success: true, data });\n } catch (error) {\n this.logger.error(`Error in subscription ${eventName}:`, {\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n callback?.({\n success: false,\n error: error instanceof Error ? error.message : \"Subscription failed\",\n });\n }\n }\n );\n }\n\n /**\n * Register unsubscription listener for a service.\n */\n private registerUnsubscriptionListener(\n socket: QuickdrawSocket,\n eventName: string,\n service: BaseServiceInstance\n ): void {\n socket.on(\n eventName,\n (\n payload: { entryId: string },\n callback?: (\n response: ServiceResponse<{ unsubscribed: true; entryId: string }>\n ) => void\n ) => {\n try {\n service.unsubscribe(payload.entryId, socket);\n callback?.({\n success: true,\n data: { unsubscribed: true, entryId: payload.entryId },\n });\n } catch (error) {\n this.logger.error(`Error in unsubscription ${eventName}:`, {\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n callback?.({\n success: false,\n error: error instanceof Error ? error.message : \"Unsubscription failed\",\n });\n }\n }\n );\n }\n\n /**\n * Get list of registered service names.\n */\n public getServices(): string[] {\n return Array.from(this.services.keys());\n }\n\n /**\n * Get all service instances for lifecycle management.\n */\n public getServiceInstances(): BaseServiceInstance[] {\n return Array.from(this.services.values());\n }\n}\n","import express from \"express\";\nimport { createServer } from \"http\";\nimport { Server as SocketIOServer } from \"socket.io\";\nimport { consoleLogger } from \"../shared/types\";\nimport type {\n QuickdrawSocket,\n QuickdrawServerOptions,\n QuickdrawServerResult,\n} from \"./types\";\nimport { ServiceRegistry } from \"./ServiceRegistry\";\n\n/**\n * Create a fully configured quickdraw server with one function call.\n *\n * @example\n * ```typescript\n * import { createQuickdrawServer } from '@quickdraw/core/server';\n * import { ChatService, UserService } from './services';\n *\n * const { io, httpServer, registry } = createQuickdrawServer({\n * port: 4000,\n * cors: { origin: 'http://localhost:3000' },\n * services: {\n * chatService: new ChatService(prisma),\n * userService: new UserService(prisma),\n * },\n * auth: {\n * authenticate: async (socket, auth) => {\n * // Verify JWT and return userId\n * const payload = await verifyJWT(auth.token);\n * return payload.userId;\n * },\n * },\n * });\n * ```\n */\nexport function createQuickdrawServer(\n options: QuickdrawServerOptions\n): QuickdrawServerResult {\n const logger = options.logger ?? consoleLogger;\n const serverLogger = logger.child({ service: \"QuickdrawServer\" });\n\n // Create Express app with basic middleware\n const app = express();\n app.use(express.json());\n\n // Health check endpoint\n app.get(\"/health\", (_req, res) => {\n res.json({ status: \"ok\", timestamp: new Date().toISOString() });\n });\n\n // Create HTTP server\n const httpServer = createServer(app);\n\n // Create Socket.io server\n const io = new SocketIOServer(httpServer, {\n cors: {\n origin: options.cors?.origin ?? \"*\",\n methods: options.cors?.methods ?? [\"GET\", \"POST\"],\n credentials: options.cors?.credentials ?? true,\n },\n });\n\n // Create service registry\n const registry = new ServiceRegistry(io, { logger });\n\n // Apply authentication middleware\n io.use(async (socket, next) => {\n const quickdrawSocket = socket as QuickdrawSocket;\n\n try {\n if (options.auth?.authenticate) {\n const auth = socket.handshake.auth as Record<string, unknown>;\n const userId = await options.auth.authenticate(quickdrawSocket, auth);\n quickdrawSocket.userId = userId;\n\n // TODO: Load service access from user record\n // For now, default to empty service access\n quickdrawSocket.serviceAccess = {};\n }\n next();\n } catch (error) {\n serverLogger.error(\"Authentication failed\", {\n error: error instanceof Error ? error.message : \"Unknown error\",\n socketId: socket.id,\n });\n next(new Error(\"Authentication failed\"));\n }\n });\n\n // Register services\n for (const [serviceName, service] of Object.entries(options.services)) {\n registry.registerService(serviceName, service);\n }\n\n // Handle connection lifecycle\n io.on(\"connection\", (socket) => {\n const quickdrawSocket = socket as QuickdrawSocket;\n serverLogger.info(\"Socket connected\", {\n socketId: quickdrawSocket.id,\n userId: quickdrawSocket.userId,\n });\n\n quickdrawSocket.on(\"disconnect\", () => {\n serverLogger.info(\"Socket disconnected\", {\n socketId: quickdrawSocket.id,\n userId: quickdrawSocket.userId,\n });\n\n // Cleanup subscriptions across all services\n for (const service of registry.getServiceInstances()) {\n try {\n service.unsubscribeSocket(quickdrawSocket);\n } catch {\n // Ignore cleanup errors\n }\n }\n });\n });\n\n // Start listening\n httpServer.listen(options.port, \"0.0.0.0\", () => {\n serverLogger.info(`Server listening on port ${options.port}`);\n serverLogger.info(\n `Registered services: ${registry.getServices().join(\", \")}`\n );\n });\n\n return { io, httpServer, registry };\n}\n","import { io as ioClient, Socket } from \"socket.io-client\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport type { BaseServiceInstance, QuickdrawServerResult } from \"./types\";\nimport { createQuickdrawServer } from \"./createServer\";\n\n/**\n * Options for creating a test server.\n */\nexport interface CreateTestServerOptions {\n services: Record<string, BaseServiceInstance>;\n seedDb?: () => Promise<void>;\n}\n\n/**\n * Test server instance with helper methods.\n */\nexport interface TestServer {\n port: number;\n result: QuickdrawServerResult;\n stop: () => Promise<void>;\n connectAs: (userId: string) => Promise<TestClient>;\n}\n\n/**\n * Test client instance with helper methods.\n */\nexport interface TestClient {\n socket: Socket;\n emit: <TPayload, TResponse>(\n event: string,\n payload: TPayload\n ) => Promise<TResponse>;\n close: () => void;\n}\n\nlet nextPort = 10000;\n\n/**\n * Get an available port for testing.\n */\nexport function getAvailablePort(): number {\n return nextPort++;\n}\n\n/**\n * Create a test server with dev authentication.\n *\n * @example\n * ```typescript\n * const server = await createTestServer({\n * services: {\n * chatService: new ChatService(testPrisma),\n * },\n * seedDb: async () => {\n * await testPrisma.user.create({ ... });\n * },\n * });\n *\n * const client = await server.connectAs('user-id-123');\n * const result = await client.emit('chatService:createChat', { title: 'Test' });\n *\n * await server.stop();\n * ```\n */\nexport async function createTestServer(\n options: CreateTestServerOptions\n): Promise<TestServer> {\n const port = getAvailablePort();\n\n const result = createQuickdrawServer({\n port,\n services: options.services,\n auth: {\n // Dev mode: accept userId directly from auth\n authenticate: async (_socket, auth) => {\n return auth.userId as string | undefined;\n },\n },\n });\n\n // Wait for server to start\n await new Promise<void>((resolve) => {\n result.httpServer.once(\"listening\", resolve);\n });\n\n // Seed database if provided\n if (options.seedDb) {\n await options.seedDb();\n }\n\n return {\n port,\n result,\n stop: () =>\n new Promise<void>((resolve) => {\n result.io.close();\n result.httpServer.close(() => resolve());\n }),\n connectAs: (userId: string) => connectAsUser(port, userId),\n };\n}\n\n/**\n * Connect to a test server as a specific user.\n */\nexport async function connectAsUser(\n port: number,\n userId: string\n): Promise<TestClient> {\n const socket = ioClient(`http://localhost:${port}`, {\n auth: { userId },\n transports: [\"websocket\"],\n autoConnect: true,\n });\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Connection timeout\"));\n }, 5000);\n\n socket.on(\"connect\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n socket.on(\"connect_error\", (err) => {\n clearTimeout(timeout);\n reject(err);\n });\n });\n\n return {\n socket,\n emit: <TPayload, TResponse>(event: string, payload: TPayload) =>\n emitWithAck<TPayload, TResponse>(socket, event, payload),\n close: () => socket.close(),\n };\n}\n\n/**\n * Emit an event and wait for acknowledgment.\n */\nexport function emitWithAck<TPayload, TResponse>(\n socket: Socket,\n event: string,\n payload: TPayload,\n timeoutMs: number = 5000\n): Promise<TResponse> {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(`Timeout waiting for ${event}`));\n }, timeoutMs);\n\n socket.emit(event, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n });\n });\n}\n\n/**\n * Wait for a specific socket event.\n */\nexport function waitForEvent<T>(\n socket: Socket,\n event: string,\n timeoutMs: number = 5000\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n socket.off(event, handler);\n reject(new Error(`Timeout waiting for event ${event}`));\n }, timeoutMs);\n\n const handler = (data: T) => {\n clearTimeout(timeout);\n socket.off(event, handler);\n resolve(data);\n };\n\n socket.on(event, handler);\n });\n}\n"]}
@@ -0,0 +1,158 @@
1
+ import { z } from 'zod';
2
+
3
+ type AccessLevel = "Public" | "Read" | "Moderate" | "Admin";
4
+ type ACE = {
5
+ userId: string;
6
+ level: AccessLevel;
7
+ };
8
+ type ACL = ACE[];
9
+ type ServiceResponse<T = unknown> = {
10
+ success: true;
11
+ data: T;
12
+ } | {
13
+ success: false;
14
+ error: string;
15
+ code?: number;
16
+ };
17
+ /**
18
+ * Defines the shape of a service method including its payload and response types.
19
+ * Used to create a type-safe contract between server and client.
20
+ */
21
+ interface ServiceMethodDefinition<TPayload = unknown, TResponse = unknown> {
22
+ name: string;
23
+ access: AccessLevel;
24
+ handler: (payload: TPayload, context: ServiceMethodContext) => Promise<TResponse>;
25
+ schema?: z.ZodType<TPayload>;
26
+ resolveEntryId?: (payload: TPayload) => string | null;
27
+ }
28
+ /**
29
+ * Context provided to service method handlers
30
+ */
31
+ interface ServiceMethodContext {
32
+ userId: string | undefined;
33
+ socketId: string;
34
+ serviceAccess: Record<string, AccessLevel>;
35
+ }
36
+ /**
37
+ * Type helper for defining service method maps.
38
+ * Each service should define its methods using this structure.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * type ChatServiceMethods = ServiceMethodMap<{
43
+ * createChat: {
44
+ * payload: { title: string };
45
+ * response: { id: string };
46
+ * };
47
+ * updateTitle: {
48
+ * payload: { id: string; title: string };
49
+ * response: { id: string; title: string };
50
+ * };
51
+ * }>;
52
+ * ```
53
+ */
54
+ type ServiceMethodMap<T extends Record<string, {
55
+ payload: unknown;
56
+ response: unknown;
57
+ }>> = T;
58
+ type AdminListPayload = {
59
+ page?: number;
60
+ pageSize?: number;
61
+ sort?: {
62
+ field?: string;
63
+ direction?: "asc" | "desc";
64
+ };
65
+ filter?: {
66
+ id?: string;
67
+ ids?: string[];
68
+ createdAfter?: string;
69
+ createdBefore?: string;
70
+ updatedAfter?: string;
71
+ updatedBefore?: string;
72
+ } & Record<string, unknown>;
73
+ };
74
+ type AdminListResponse<T> = {
75
+ rows: T[];
76
+ page: number;
77
+ pageSize: number;
78
+ total: number;
79
+ };
80
+ type AdminGetPayload = {
81
+ id: string;
82
+ };
83
+ type AdminCreatePayload<TInsert> = {
84
+ data: Partial<TInsert>;
85
+ };
86
+ type AdminUpdatePayload<TInsert> = {
87
+ id: string;
88
+ data: Partial<TInsert>;
89
+ };
90
+ type AdminDeletePayload = {
91
+ id: string;
92
+ };
93
+ type AdminDeleteResponse = {
94
+ id: string;
95
+ deleted: true;
96
+ };
97
+ type AdminSetEntryACLPayload = {
98
+ id: string;
99
+ acl: ACL;
100
+ };
101
+ type AdminGetSubscribersPayload = {
102
+ id: string;
103
+ };
104
+ type AdminGetSubscribersResponse = {
105
+ id: string;
106
+ subscribers: Array<{
107
+ socketId: string;
108
+ userId?: string;
109
+ }>;
110
+ };
111
+ type AdminReemitPayload = {
112
+ id: string;
113
+ };
114
+ type AdminReemitResponse = {
115
+ emitted: boolean;
116
+ };
117
+ type AdminUnsubscribeAllPayload = {
118
+ id: string;
119
+ };
120
+ type AdminUnsubscribeAllResponse = {
121
+ id: string;
122
+ unsubscribed: number;
123
+ };
124
+ type SubscribePayload = {
125
+ entryId: string;
126
+ requiredLevel?: AccessLevel;
127
+ };
128
+ type UnsubscribePayload = {
129
+ entryId: string;
130
+ };
131
+ /**
132
+ * Extracts payload type from a service method definition
133
+ */
134
+ type ExtractPayload<T> = T extends {
135
+ payload: infer P;
136
+ } ? P : never;
137
+ /**
138
+ * Extracts response type from a service method definition
139
+ */
140
+ type ExtractResponse<T> = T extends {
141
+ response: infer R;
142
+ } ? R : never;
143
+ /**
144
+ * Logger interface compatible with Winston and other loggers
145
+ */
146
+ interface Logger {
147
+ info(message: string, meta?: Record<string, unknown>): void;
148
+ warn(message: string, meta?: Record<string, unknown>): void;
149
+ error(message: string, meta?: Record<string, unknown>): void;
150
+ debug(message: string, meta?: Record<string, unknown>): void;
151
+ child(options: Record<string, unknown>): Logger;
152
+ }
153
+ /**
154
+ * Default console logger implementation
155
+ */
156
+ declare const consoleLogger: Logger;
157
+
158
+ export { type ACE, type ACL, type AccessLevel, type AdminCreatePayload, type AdminDeletePayload, type AdminDeleteResponse, type AdminGetPayload, type AdminGetSubscribersPayload, type AdminGetSubscribersResponse, type AdminListPayload, type AdminListResponse, type AdminReemitPayload, type AdminReemitResponse, type AdminSetEntryACLPayload, type AdminUnsubscribeAllPayload, type AdminUnsubscribeAllResponse, type AdminUpdatePayload, type ExtractPayload, type ExtractResponse, type Logger, type ServiceMethodContext, type ServiceMethodDefinition, type ServiceMethodMap, type ServiceResponse, type SubscribePayload, type UnsubscribePayload, consoleLogger };
@@ -0,0 +1,12 @@
1
+ // src/shared/types.ts
2
+ var consoleLogger = {
3
+ info: (message, meta) => console.log(`[INFO] ${message}`, meta ?? ""),
4
+ warn: (message, meta) => console.warn(`[WARN] ${message}`, meta ?? ""),
5
+ error: (message, meta) => console.error(`[ERROR] ${message}`, meta ?? ""),
6
+ debug: (message, meta) => console.debug(`[DEBUG] ${message}`, meta ?? ""),
7
+ child: () => consoleLogger
8
+ };
9
+
10
+ export { consoleLogger };
11
+ //# sourceMappingURL=index.js.map
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shared/types.ts"],"names":[],"mappings":";AA8KO,IAAM,aAAA,GAAwB;AAAA,EACnC,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,IAAI,CAAA,OAAA,EAAU,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACpE,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,KAAK,CAAA,OAAA,EAAU,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACrE,KAAA,EAAO,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,MAAM,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACxE,KAAA,EAAO,CAAC,OAAA,EAAS,IAAA,KAAS,OAAA,CAAQ,MAAM,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA,EAAI,IAAA,IAAQ,EAAE,CAAA;AAAA,EACxE,OAAO,MAAM;AACf","file":"index.js","sourcesContent":["import type { z } from \"zod\";\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport type AccessLevel = \"Public\" | \"Read\" | \"Moderate\" | \"Admin\";\n\nexport type ACE = {\n userId: string;\n level: AccessLevel;\n};\n\nexport type ACL = ACE[];\n\n// ============================================================================\n// Service Response Types\n// ============================================================================\n\nexport type ServiceResponse<T = unknown> =\n | { success: true; data: T }\n | { success: false; error: string; code?: number };\n\n// ============================================================================\n// Service Method Definition Types\n// ============================================================================\n\n/**\n * Defines the shape of a service method including its payload and response types.\n * Used to create a type-safe contract between server and client.\n */\nexport interface ServiceMethodDefinition<\n TPayload = unknown,\n TResponse = unknown,\n> {\n name: string;\n access: AccessLevel;\n handler: (\n payload: TPayload,\n context: ServiceMethodContext\n ) => Promise<TResponse>;\n schema?: z.ZodType<TPayload>;\n resolveEntryId?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to service method handlers\n */\nexport interface ServiceMethodContext {\n userId: string | undefined;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service method maps.\n * Each service should define its methods using this structure.\n *\n * @example\n * ```typescript\n * type ChatServiceMethods = ServiceMethodMap<{\n * createChat: {\n * payload: { title: string };\n * response: { id: string };\n * };\n * updateTitle: {\n * payload: { id: string; title: string };\n * response: { id: string; title: string };\n * };\n * }>;\n * ```\n */\nexport type ServiceMethodMap<\n T extends Record<string, { payload: unknown; response: unknown }>,\n> = T;\n\n// ============================================================================\n// Admin Method Types (Generic shapes for all services)\n// ============================================================================\n\nexport type AdminListPayload = {\n page?: number;\n pageSize?: number;\n sort?: {\n field?: string;\n direction?: \"asc\" | \"desc\";\n };\n filter?: {\n id?: string;\n ids?: string[];\n createdAfter?: string;\n createdBefore?: string;\n updatedAfter?: string;\n updatedBefore?: string;\n } & Record<string, unknown>;\n};\n\nexport type AdminListResponse<T> = {\n rows: T[];\n page: number;\n pageSize: number;\n total: number;\n};\n\nexport type AdminGetPayload = { id: string };\n\nexport type AdminCreatePayload<TInsert> = { data: Partial<TInsert> };\n\nexport type AdminUpdatePayload<TInsert> = {\n id: string;\n data: Partial<TInsert>;\n};\n\nexport type AdminDeletePayload = { id: string };\nexport type AdminDeleteResponse = { id: string; deleted: true };\n\nexport type AdminSetEntryACLPayload = {\n id: string;\n acl: ACL;\n};\n\nexport type AdminGetSubscribersPayload = { id: string };\nexport type AdminGetSubscribersResponse = {\n id: string;\n subscribers: Array<{ socketId: string; userId?: string }>;\n};\n\nexport type AdminReemitPayload = { id: string };\nexport type AdminReemitResponse = { emitted: boolean };\n\nexport type AdminUnsubscribeAllPayload = { id: string };\nexport type AdminUnsubscribeAllResponse = { id: string; unsubscribed: number };\n\n// ============================================================================\n// Subscription Types\n// ============================================================================\n\nexport type SubscribePayload = {\n entryId: string;\n requiredLevel?: AccessLevel;\n};\n\nexport type UnsubscribePayload = {\n entryId: string;\n};\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Extracts payload type from a service method definition\n */\nexport type ExtractPayload<T> = T extends { payload: infer P } ? P : never;\n\n/**\n * Extracts response type from a service method definition\n */\nexport type ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\n/**\n * Logger interface compatible with Winston and other loggers\n */\nexport interface Logger {\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n child(options: Record<string, unknown>): Logger;\n}\n\n/**\n * Default console logger implementation\n */\nexport const consoleLogger: Logger = {\n info: (message, meta) => console.log(`[INFO] ${message}`, meta ?? \"\"),\n warn: (message, meta) => console.warn(`[WARN] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[ERROR] ${message}`, meta ?? \"\"),\n debug: (message, meta) => console.debug(`[DEBUG] ${message}`, meta ?? \"\"),\n child: () => consoleLogger,\n};\n"]}
@@ -0,0 +1,109 @@
1
+ import { Socket } from 'socket.io-client';
2
+ import { QueryClient } from '@tanstack/react-query';
3
+ import { AccessLevel } from './shared/index.js';
4
+
5
+ interface QuickdrawSocketContextValue {
6
+ socket: Socket | null;
7
+ isConnected: boolean;
8
+ userId: string | null;
9
+ serviceAccess: Record<string, AccessLevel>;
10
+ connect: (token?: string) => void;
11
+ disconnect: () => void;
12
+ }
13
+ interface QuickdrawProviderProps {
14
+ children: React.ReactNode;
15
+ /**
16
+ * Socket.io server URL.
17
+ * @example "http://localhost:4000"
18
+ */
19
+ serverUrl: string;
20
+ /**
21
+ * Optional custom QueryClient instance.
22
+ * If not provided, a default one will be created.
23
+ */
24
+ queryClient?: QueryClient;
25
+ /**
26
+ * Optional auth token to send on connection.
27
+ */
28
+ authToken?: string;
29
+ /**
30
+ * Auto-connect on mount (default: true if authToken provided)
31
+ */
32
+ autoConnect?: boolean;
33
+ }
34
+ /**
35
+ * Options for the useService hook.
36
+ */
37
+ interface UseServiceOptions<TResponse> {
38
+ onSuccess?: (data: TResponse) => void;
39
+ onError?: (error: string) => void;
40
+ /**
41
+ * TanStack Query mutation options
42
+ */
43
+ retry?: boolean | number;
44
+ retryDelay?: number;
45
+ }
46
+ /**
47
+ * Return type for the useService hook.
48
+ */
49
+ interface UseServiceResult<TPayload, TResponse> {
50
+ mutate: (payload: TPayload) => void;
51
+ mutateAsync: (payload: TPayload) => Promise<TResponse>;
52
+ isPending: boolean;
53
+ isError: boolean;
54
+ error: string | null;
55
+ data: TResponse | undefined;
56
+ reset: () => void;
57
+ }
58
+ /**
59
+ * Options for the useSubscription hook.
60
+ */
61
+ interface UseSubscriptionOptions<TData> {
62
+ /**
63
+ * Whether to automatically subscribe on mount (default: true)
64
+ */
65
+ enabled?: boolean;
66
+ /**
67
+ * Callback when data is received
68
+ */
69
+ onData?: (data: TData) => void;
70
+ /**
71
+ * Callback on error
72
+ */
73
+ onError?: (error: string) => void;
74
+ /**
75
+ * Required access level for subscription (default: "Read")
76
+ */
77
+ requiredLevel?: AccessLevel;
78
+ /**
79
+ * Stale time for TanStack Query cache (default: Infinity for subscriptions)
80
+ */
81
+ staleTime?: number;
82
+ }
83
+ /**
84
+ * Return type for the useSubscription hook.
85
+ */
86
+ interface UseSubscriptionResult<TData> {
87
+ data: TData | null;
88
+ isLoading: boolean;
89
+ isError: boolean;
90
+ error: string | null;
91
+ isSubscribed: boolean;
92
+ subscribe: () => void;
93
+ unsubscribe: () => void;
94
+ }
95
+ /**
96
+ * Type helper for defining service methods map on the client.
97
+ * This mirrors the server-side ServiceMethodMap.
98
+ */
99
+ type ClientServiceMethodMap<T extends Record<string, {
100
+ payload: unknown;
101
+ response: unknown;
102
+ }>> = T;
103
+ /**
104
+ * Type helper for defining subscription data map.
105
+ * Maps service names to their entity types.
106
+ */
107
+ type SubscriptionDataMap<T extends Record<string, unknown>> = T;
108
+
109
+ export type { ClientServiceMethodMap as C, QuickdrawSocketContextValue as Q, SubscriptionDataMap as S, UseServiceOptions as U, QuickdrawProviderProps as a, UseServiceResult as b, UseSubscriptionOptions as c, UseSubscriptionResult as d };
@@ -0,0 +1,118 @@
1
+ import { Socket, Server } from 'socket.io';
2
+ import { Server as Server$1 } from 'http';
3
+ import { AccessLevel, ServiceMethodDefinition, Logger } from './shared/index.js';
4
+
5
+ /**
6
+ * Extended Socket type with authentication properties
7
+ */
8
+ interface QuickdrawSocket extends Socket {
9
+ userId?: string;
10
+ serviceAccess?: Record<string, AccessLevel>;
11
+ }
12
+ interface QuickdrawServerOptions {
13
+ port: number;
14
+ cors?: {
15
+ origin: string | string[];
16
+ methods?: string[];
17
+ credentials?: boolean;
18
+ };
19
+ services: Record<string, BaseServiceInstance>;
20
+ auth?: {
21
+ /**
22
+ * Custom authentication function.
23
+ * Return the userId if authenticated, or throw/return undefined to reject.
24
+ */
25
+ authenticate?: (socket: QuickdrawSocket, auth: Record<string, unknown>) => Promise<string | undefined>;
26
+ };
27
+ logger?: Logger;
28
+ }
29
+ interface QuickdrawServerResult {
30
+ io: Server;
31
+ httpServer: Server$1;
32
+ registry: ServiceRegistryInstance;
33
+ }
34
+ /**
35
+ * Interface for BaseService instances (used for type-safe registration)
36
+ */
37
+ interface BaseServiceInstance {
38
+ serviceName: string;
39
+ subscribe: (entryId: string, socket: QuickdrawSocket, requiredLevel?: AccessLevel) => Promise<Record<string, unknown> | null>;
40
+ unsubscribe: (entryId: string, socket: QuickdrawSocket) => void;
41
+ unsubscribeSocket: (socket: QuickdrawSocket) => void;
42
+ ensureAccessForMethod: (requiredLevel: AccessLevel, socket: QuickdrawSocket, entryId?: string) => Promise<void>;
43
+ getPublicMethods: () => ServiceMethodDefinition<unknown, unknown>[];
44
+ }
45
+ /**
46
+ * Interface for ServiceRegistry instances
47
+ */
48
+ interface ServiceRegistryInstance {
49
+ registerService: (serviceName: string, service: BaseServiceInstance) => void;
50
+ getServices: () => string[];
51
+ getServiceInstances: () => BaseServiceInstance[];
52
+ }
53
+ interface BaseServiceOptions {
54
+ serviceName: string;
55
+ hasEntryACL?: boolean;
56
+ defaultACL?: Array<{
57
+ userId: string;
58
+ level: AccessLevel;
59
+ }>;
60
+ logger?: Logger;
61
+ }
62
+ interface InstallAdminMethodsOptions {
63
+ expose: {
64
+ list?: boolean;
65
+ get?: boolean;
66
+ create?: boolean;
67
+ update?: boolean;
68
+ delete?: boolean;
69
+ setEntryACL?: boolean;
70
+ getSubscribers?: boolean;
71
+ reemit?: boolean;
72
+ unsubscribeAll?: boolean;
73
+ };
74
+ access: {
75
+ list: AccessLevel;
76
+ get: AccessLevel;
77
+ create: AccessLevel;
78
+ update: AccessLevel;
79
+ delete: AccessLevel;
80
+ setEntryACL: AccessLevel;
81
+ getSubscribers: AccessLevel;
82
+ reemit: AccessLevel;
83
+ unsubscribeAll: AccessLevel;
84
+ };
85
+ }
86
+ /**
87
+ * Generic Prisma delegate type for CRUD operations.
88
+ * This allows BaseService to work with any Prisma model without tight coupling.
89
+ */
90
+ interface PrismaDelegate<TEntity, TCreateInput, TUpdateInput, TWhereUniqueInput = {
91
+ id: string;
92
+ }> {
93
+ findUnique: (args: {
94
+ where: TWhereUniqueInput;
95
+ select?: Record<string, boolean>;
96
+ }) => Promise<TEntity | null>;
97
+ findMany: (args?: {
98
+ where?: Record<string, unknown>;
99
+ orderBy?: Record<string, "asc" | "desc">;
100
+ skip?: number;
101
+ take?: number;
102
+ }) => Promise<TEntity[]>;
103
+ create: (args: {
104
+ data: TCreateInput;
105
+ }) => Promise<TEntity>;
106
+ update: (args: {
107
+ where: TWhereUniqueInput;
108
+ data: TUpdateInput;
109
+ }) => Promise<TEntity>;
110
+ delete: (args: {
111
+ where: TWhereUniqueInput;
112
+ }) => Promise<TEntity>;
113
+ count: (args?: {
114
+ where?: Record<string, unknown>;
115
+ }) => Promise<number>;
116
+ }
117
+
118
+ export type { BaseServiceInstance as B, InstallAdminMethodsOptions as I, PrismaDelegate as P, QuickdrawServerResult as Q, ServiceRegistryInstance as S, QuickdrawSocket as a, BaseServiceOptions as b, QuickdrawServerOptions as c };
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@fitzzero/quickdraw-core",
3
+ "version": "0.1.0",
4
+ "description": "Fast fullstack patterns for real-time applications with Socket.io and TanStack Query",
5
+ "type": "module",
6
+ "main": "./dist/shared/index.js",
7
+ "module": "./dist/shared/index.js",
8
+ "types": "./dist/shared/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/shared/index.js",
12
+ "types": "./dist/shared/index.d.ts"
13
+ },
14
+ "./server": {
15
+ "import": "./dist/server/index.js",
16
+ "types": "./dist/server/index.d.ts"
17
+ },
18
+ "./client": {
19
+ "import": "./dist/client/index.js",
20
+ "types": "./dist/client/index.d.ts"
21
+ },
22
+ "./server/testing": {
23
+ "import": "./dist/server/testing.js",
24
+ "types": "./dist/server/testing.d.ts"
25
+ },
26
+ "./client/testing": {
27
+ "import": "./dist/client/testing.js",
28
+ "types": "./dist/client/testing.d.ts"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "dev": "tsup --watch",
37
+ "typecheck": "tsc --noEmit",
38
+ "lint": "eslint src --ext .ts,.tsx",
39
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "test:coverage": "vitest run --coverage",
43
+ "prepublishOnly": "pnpm build"
44
+ },
45
+ "keywords": [
46
+ "socket.io",
47
+ "tanstack-query",
48
+ "react",
49
+ "real-time",
50
+ "fullstack",
51
+ "typescript"
52
+ ],
53
+ "author": "",
54
+ "license": "MIT",
55
+ "peerDependencies": {
56
+ "@prisma/client": ">=5.0.0",
57
+ "@tanstack/react-query": ">=5.0.0",
58
+ "react": ">=18.0.0",
59
+ "socket.io": ">=4.0.0",
60
+ "socket.io-client": ">=4.0.0"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "@prisma/client": {
64
+ "optional": true
65
+ },
66
+ "@tanstack/react-query": {
67
+ "optional": true
68
+ },
69
+ "react": {
70
+ "optional": true
71
+ },
72
+ "socket.io": {
73
+ "optional": true
74
+ },
75
+ "socket.io-client": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "dependencies": {
80
+ "jose": "^5.2.0",
81
+ "zod": "^3.22.0"
82
+ },
83
+ "devDependencies": {
84
+ "@prisma/client": "^5.10.0",
85
+ "@tanstack/react-query": "^5.20.0",
86
+ "@testing-library/react": "^14.2.0",
87
+ "@types/express": "^4.17.21",
88
+ "@types/node": "^20.11.0",
89
+ "@types/react": "^18.2.0",
90
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
91
+ "@typescript-eslint/parser": "^7.0.0",
92
+ "@vitest/coverage-v8": "^1.3.0",
93
+ "eslint": "^8.56.0",
94
+ "express": "^4.18.2",
95
+ "jsdom": "^24.0.0",
96
+ "react": "^18.2.0",
97
+ "react-dom": "^18.2.0",
98
+ "socket.io": "^4.7.4",
99
+ "socket.io-client": "^4.7.4",
100
+ "tsup": "^8.0.2",
101
+ "typescript": "^5.3.3",
102
+ "vitest": "^1.3.0",
103
+ "winston": "^3.11.0"
104
+ },
105
+ "publishConfig": {
106
+ "access": "public"
107
+ },
108
+ "engines": {
109
+ "node": ">=20"
110
+ }
111
+ }