@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/client/QuickdrawProvider.tsx","../../src/client/useService.ts","../../src/client/useSubscription.ts","../../src/client/inputs/useSocketInput.ts","../../src/client/inputs/SocketCheckbox.tsx","../../src/client/inputs/SocketTextField.tsx","../../src/client/inputs/SocketSelect.tsx","../../src/client/inputs/SocketSlider.tsx","../../src/client/inputs/SocketSwitch.tsx"],"names":["React2","React3","React4","React5","jsx","React6","React7","React8","React9"],"mappings":";;;;;;AAeA,IAAM,sBAAA,GACE,oBAAkD,IAAI,CAAA;AAKvD,SAAS,kBAAA,GAAkD;AAChE,EAAA,MAAM,OAAA,GAAgB,iBAAW,sBAAsB,CAAA;AACvD,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAMA,IAAM,kBAAA,GAAqB,IAAI,WAAA,CAAY;AAAA,EACzC,cAAA,EAAgB;AAAA,IACd,OAAA,EAAS;AAAA,MACP,SAAA,EAAW,MAAO,EAAA,GAAK,CAAA;AAAA;AAAA,MACvB,KAAA,EAAO;AAAA,KACT;AAAA,IACA,SAAA,EAAW;AAAA,MACT,KAAA,EAAO;AAAA;AACT;AAEJ,CAAC,CAAA;AAoBM,SAAS,iBAAA,CAAkB;AAAA,EAChC,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA,GAAc,kBAAA;AAAA,EACd,SAAA;AAAA,EACA,WAAA,GAAc,CAAC,CAAC;AAClB,CAAA,EAA+C;AAC7C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAU,eAAwB,IAAI,CAAA;AAC9D,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAU,eAAS,KAAK,CAAA;AAC1D,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAU,eAAwB,IAAI,CAAA;AAC9D,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAU,KAAA,CAAA,QAAA,CAE9C,EAAE,CAAA;AAGJ,EAAA,MAAM,YAAA,GAAqB,aAAO,SAAS,CAAA;AAC3C,EAAM,gBAAU,MAAM;AACpB,IAAA,YAAA,CAAa,OAAA,GAAU,SAAA;AAAA,EACzB,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,OAAA,GAAgB,KAAA,CAAA,WAAA;AAAA,IACpB,CAAC,KAAA,KAAmB;AAClB,MAAA,MAAM,SAAA,GAAY,SAAS,YAAA,CAAa,OAAA;AAExC,MAAA,MAAM,SAAA,GAAY,GAAG,SAAA,EAAW;AAAA,QAC9B,IAAA,EAAM,SAAA,GAAY,EAAE,KAAA,EAAO,WAAU,GAAI,MAAA;AAAA,QACzC,UAAA,EAAY,CAAC,WAAA,EAAa,SAAS,CAAA;AAAA,QACnC,WAAA,EAAa;AAAA,OACd,CAAA;AAED,MAAA,SAAA,CAAU,EAAA,CAAG,WAAW,MAAM;AAC5B,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,MACrB,CAAC,CAAA;AAED,MAAA,SAAA,CAAU,EAAA,CAAG,cAAc,MAAM;AAC/B,QAAA,cAAA,CAAe,KAAK,CAAA;AAAA,MACtB,CAAC,CAAA;AAGD,MAAA,SAAA,CAAU,EAAA;AAAA,QACR,WAAA;AAAA,QACA,CAAC,IAAA,KAAyE;AACxE,UAAA,SAAA,CAAU,KAAK,MAAM,CAAA;AACrB,UAAA,gBAAA,CAAiB,KAAK,aAAa,CAAA;AAAA,QACrC;AAAA,OACF;AAEA,MAAA,SAAA,CAAU,EAAA,CAAG,eAAA,EAAiB,CAAC,KAAA,KAAU;AACvC,QAAA,OAAA,CAAQ,KAAA,CAAM,0BAAA,EAA4B,KAAA,CAAM,OAAO,CAAA;AAAA,MACzD,CAAC,CAAA;AAED,MAAA,SAAA,CAAU,SAAS,CAAA;AAAA,IACrB,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,UAAA,GAAmB,kBAAY,MAAM;AACzC,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAA,CAAO,UAAA,EAAW;AAClB,MAAA,SAAA,CAAU,IAAI,CAAA;AACd,MAAA,cAAA,CAAe,KAAK,CAAA;AACpB,MAAA,SAAA,CAAU,IAAI,CAAA;AACd,MAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,IACrB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,WAAA,IAAe,CAAC,MAAA,EAAQ;AAC1B,MAAA,OAAA,EAAQ;AAAA,IACV;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,MAAA,CAAO,UAAA,EAAW;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,EAEF,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAGhB,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,MAAA,IAAU,SAAA,KAAc,YAAA,CAAa,OAAA,EAAS;AAChD,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,OAAA,CAAQ,SAAS,CAAA;AAAA,MACnB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAA,EAAW,MAAA,EAAQ,UAAA,EAAY,OAAO,CAAC,CAAA;AAE3C,EAAA,MAAM,YAAA,GAAqB,KAAA,CAAA,OAAA;AAAA,IACzB,OAAO;AAAA,MACL,MAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,WAAA,EAAa,MAAA,EAAQ,aAAA,EAAe,SAAS,UAAU;AAAA,GAClE;AAEA,EAAA,uBACE,GAAA,CAAC,mBAAA,EAAA,EAAoB,MAAA,EAAQ,WAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,YAAA,EACrC,QAAA,EACH,CAAA,EACF,CAAA;AAEJ;AChIO,SAAS,UAAA,CAQd,WAAA,EACA,UAAA,EACA,OAAA,EAIA;AAIA,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAY,GAAI,kBAAA,EAAmB;AACnD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAUA,eAAwB,IAAI,CAAA;AAG5D,EAAA,MAAM,UAAA,GAAmBA,aAAO,OAAO,CAAA;AACvC,EAAMA,gBAAU,MAAM;AACpB,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAAA,EACvB,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,WAAW,WAAA,CAAwC;AAAA,IACvD,WAAA,EAAa,CAAC,WAAA,EAAa,UAAU,CAAA;AAAA,IACrC,UAAA,EAAY,OAAO,OAAA,KAA0C;AAC3D,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,WAAA,EAAa;AAC3B,QAAA,MAAM,IAAI,MAAM,sBAAsB,CAAA;AAAA,MACxC;AAEA,MAAA,OAAO,IAAI,OAAA,CAAmB,CAAC,OAAA,EAAS,MAAA,KAAW;AACjD,QAAA,MAAM,SAAA,GAAY,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA;AAC9C,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,iBAAiB,CAAC,CAAA;AAAA,QACrC,GAAG,GAAK,CAAA;AAER,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,SAAA;AAAA,UACA,OAAA;AAAA,UACA,CAAC,QAAA,KAAyC;AACxC,YAAA,YAAA,CAAa,OAAO,CAAA;AAEpB,YAAA,IAAI,SAAS,OAAA,EAAS;AACpB,cAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,YACvB,CAAA,MAAO;AACL,cAAA,MAAA,CAAO,IAAI,KAAA,CAAM,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,YAClC;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,IAAA,KAAS;AACnB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,UAAA,CAAW,OAAA,EAAS,YAAY,IAAI,CAAA;AAAA,IACtC,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,GAAA,KAAQ;AAChB,MAAA,MAAM,eAAe,GAAA,CAAI,OAAA;AACzB,MAAA,QAAA,CAAS,YAAY,CAAA;AACrB,MAAA,UAAA,CAAW,OAAA,EAAS,UAAU,YAAY,CAAA;AAAA,IAC5C,CAAA;AAAA,IACA,KAAA,EAAO,SAAS,KAAA,IAAS,KAAA;AAAA,IACzB,YAAY,OAAA,EAAS;AAAA,GACtB,CAAA;AAED,EAAA,OAAO;AAAA,IACL,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,aAAa,QAAA,CAAS,WAAA;AAAA,IACtB,WAAW,QAAA,CAAS,SAAA;AAAA,IACpB,SAAS,QAAA,CAAS,OAAA;AAAA,IAClB,KAAA;AAAA,IACA,MAAM,QAAA,CAAS,IAAA;AAAA,IACf,OAAO,QAAA,CAAS;AAAA,GAClB;AACF;AAkBO,SAAS,gBAAA,CACd,WAAA,EACA,UAAA,EACA,OAAA,EAOA;AACA,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAY,GAAI,kBAAA,EAAmB;AACnD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAUA,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAUA,eAAwB,IAAI,CAAA;AAC5D,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAUA,eAA2B,IAAI,CAAA;AAE7D,EAAA,MAAM,UAAA,GAAmBA,aAAO,OAAO,CAAA;AACvC,EAAMA,gBAAU,MAAM;AACpB,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAAA,EACvB,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,OAAA,GAAgBA,KAAA,CAAA,WAAA;AAAA,IACpB,OAAO,OAAA,KAAiD;AACtD,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,WAAA,EAAa;AAC3B,QAAA,MAAM,QAAA,GAAW,sBAAA;AACjB,QAAA,QAAA,CAAS,QAAQ,CAAA;AACjB,QAAA,UAAA,CAAW,OAAA,EAAS,UAAU,QAAQ,CAAA;AACtC,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,OAAO,IAAI,OAAA,CAA0B,CAAC,OAAA,KAAY;AAChD,QAAA,MAAM,SAAA,GAAY,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA;AAC9C,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,UAAA,MAAM,QAAA,GAAW,iBAAA;AACjB,UAAA,QAAA,CAAS,QAAQ,CAAA;AACjB,UAAA,UAAA,CAAW,KAAK,CAAA;AAChB,UAAA,UAAA,CAAW,OAAA,EAAS,UAAU,QAAQ,CAAA;AACtC,UAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,QACd,GAAG,GAAK,CAAA;AAER,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,SAAA;AAAA,UACA,OAAA;AAAA,UACA,CAAC,QAAA,KAAyC;AACxC,YAAA,YAAA,CAAa,OAAO,CAAA;AACpB,YAAA,UAAA,CAAW,KAAK,CAAA;AAEhB,YAAA,IAAI,SAAS,OAAA,EAAS;AACpB,cAAA,QAAA,CAAS,IAAI,CAAA;AACb,cAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AACrB,cAAA,UAAA,CAAW,OAAA,EAAS,SAAA,GAAY,QAAA,CAAS,IAAI,CAAA;AAC7C,cAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,YACvB,CAAA,MAAO;AACL,cAAA,QAAA,CAAS,SAAS,KAAK,CAAA;AACvB,cAAA,UAAA,CAAW,OAAA,EAAS,OAAA,GAAU,QAAA,CAAS,KAAK,CAAA;AAC5C,cAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,YACd;AAAA,UACF;AAAA,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,WAAA,EAAa,WAAA,EAAa,UAAU;AAAA,GAC/C;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA,EAAS,CAAC,CAAC,MAAA,IAAU;AAAA,GACvB;AACF;AC9MA,IAAM,mBAAA,uBAA0B,GAAA,EAM9B;AAkCK,SAAS,eAAA,CACd,WAAA,EACA,OAAA,EACA,OAAA,GAAyC,EAAC,EACZ;AAC9B,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAY,GAAI,kBAAA,EAAmB;AACnD,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAUC,eAAS,KAAK,CAAA;AAC5D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAUA,eAAwB,IAAI,CAAA;AAE5D,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,IAAA;AAAA,IACV,MAAA;AAAA,IACA,OAAA;AAAA,IACA,aAAA,GAAgB,MAAA;AAAA,IAChB,SAAA,GAAY;AAAA;AAAA,GACd,GAAI,OAAA;AAGJ,EAAA,MAAM,kBAAkB,OAAA,GAAU,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,GAAK,IAAA;AAChE,EAAA,MAAM,QAAA,GAAW,CAAC,WAAA,EAAa,cAAA,EAAgB,OAAO,CAAA;AAGtD,EAAA,MAAM,SAAA,GAAkBA,aAAO,MAAM,CAAA;AACrC,EAAA,MAAM,UAAA,GAAmBA,aAAO,OAAO,CAAA;AACvC,EAAMA,gBAAU,MAAM;AACpB,IAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AACpB,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAAA,EACvB,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAO,CAAC,CAAA;AAGpB,EAAA,MAAM,QAAQ,QAAA,CAAuB;AAAA,IACnC,QAAA;AAAA,IACA,SAAS,YAAY;AAEnB,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,OAAA,EAAS,KAAA;AAAA;AAAA,IACT;AAAA,GACD,CAAA;AAGD,EAAMA,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,UAAU,CAAC,WAAA,IAAe,CAAC,eAAA,IAAmB,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AACvE,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,GAAA,CAAI,eAAe,CAAA;AACxD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,QAAA,EAAA;AACT,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,QAAA,EAAA;AACT,QAAA,IAAI,QAAA,CAAS,aAAa,CAAA,EAAG;AAC3B,UAAA,QAAA,CAAS,OAAA,IAAU;AACnB,UAAA,mBAAA,CAAoB,OAAO,eAAe,CAAA;AAAA,QAC5C;AAAA,MACF,CAAA;AAAA,IACF;AAGA,IAAA,MAAM,cAAA,GAAiB,GAAG,WAAW,CAAA,UAAA,CAAA;AACrC,IAAA,MAAM,WAAA,GAAc,CAAA,EAAG,WAAW,CAAA,QAAA,EAAW,OAAO,CAAA,CAAA;AAGpD,IAAA,MAAM,YAAA,GAAe,CAAC,UAAA,KAA+B;AAEnD,MAAA,MAAM,KAAA,GAAQ,UAAA;AACd,MAAA,IAAI,MAAM,OAAA,EAAS;AACjB,QAAA,WAAA,CAAY,YAAA,CAA2B,UAAU,IAAI,CAAA;AACrD,QAAA;AAAA,MACF;AAGA,MAAA,WAAA,CAAY,YAAA,CAA2B,QAAA,EAAU,CAAC,OAAA,KAAY;AAC5D,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,OAAO,UAAA;AAAA,QACT;AACA,QAAA,MAAM,MAAA,GAAS,EAAE,GAAG,OAAA,EAAS,GAAG,UAAA,EAAW;AAC3C,QAAA,SAAA,CAAU,UAAU,MAAM,CAAA;AAC1B,QAAA,OAAO,MAAA;AAAA,MACT,CAAC,CAAA;AAAA,IACH,CAAA;AAGA,IAAA,MAAA,CAAO,EAAA,CAAG,aAAa,YAAY,CAAA;AAGnC,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,cAAA;AAAA,MACA,EAAE,SAAS,aAAA,EAAc;AAAA,MACzB,CAAC,QAAA,KAAqC;AACpC,QAAA,IAAI,QAAA,CAAS,OAAA,IAAW,QAAA,CAAS,IAAA,EAAM;AACrC,UAAA,WAAA,CAAY,YAAA,CAA2B,QAAA,EAAU,QAAA,CAAS,IAAI,CAAA;AAC9D,UAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,UAAA,QAAA,CAAS,IAAI,CAAA;AACb,UAAA,SAAA,CAAU,OAAA,GAAU,SAAS,IAAI,CAAA;AAAA,QACnC,CAAA,MAAA,IAAW,CAAC,QAAA,CAAS,OAAA,EAAS;AAC5B,UAAA,QAAA,CAAS,SAAS,KAAK,CAAA;AACvB,UAAA,eAAA,CAAgB,KAAK,CAAA;AACrB,UAAA,UAAA,CAAW,OAAA,GAAU,SAAS,KAAK,CAAA;AAAA,QACrC;AAAA,MACF;AAAA,KACF;AAGA,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,MAAA,CAAO,GAAA,CAAI,aAAa,YAAY,CAAA;AACpC,MAAA,MAAA,CAAO,KAAK,CAAA,EAAG,WAAW,CAAA,YAAA,CAAA,EAAgB,EAAE,SAAS,CAAA;AACrD,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACvB,CAAA;AAGA,IAAA,mBAAA,CAAoB,IAAI,eAAA,EAAiB;AAAA,MACvC,QAAA,EAAU,CAAA;AAAA,MACV;AAAA,KACD,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,MAAM,GAAA,GAAM,mBAAA,CAAoB,GAAA,CAAI,eAAe,CAAA;AACnD,MAAA,IAAI,GAAA,EAAK;AACP,QAAA,GAAA,CAAI,QAAA,EAAA;AACJ,QAAA,IAAI,GAAA,CAAI,aAAa,CAAA,EAAG;AACtB,UAAA,GAAA,CAAI,OAAA,IAAU;AACd,UAAA,mBAAA,CAAoB,OAAO,eAAe,CAAA;AAAA,QAC5C;AAAA,MACF;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,MAAA;AAAA,IACA,WAAA;AAAA,IACA,eAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,SAAA,GAAkBA,kBAAY,MAAM;AACxC,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,WAAA,IAAe,CAAC,OAAA,EAAS;AAEzC,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,GAAG,WAAW,CAAA,UAAA,CAAA;AAAA,MACd,EAAE,SAAS,aAAA,EAAc;AAAA,MACzB,CAAC,QAAA,KAAqC;AACpC,QAAA,IAAI,QAAA,CAAS,OAAA,IAAW,QAAA,CAAS,IAAA,EAAM;AACrC,UAAA,WAAA,CAAY,YAAA,CAA2B,QAAA,EAAU,QAAA,CAAS,IAAI,CAAA;AAC9D,UAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,UAAA,QAAA,CAAS,IAAI,CAAA;AAAA,QACf,CAAA,MAAA,IAAW,CAAC,QAAA,CAAS,OAAA,EAAS;AAC5B,UAAA,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,QACzB;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,WAAA,EAAa,SAAS,WAAA,EAAa,aAAA,EAAe,WAAA,EAAa,QAAQ,CAAC,CAAA;AAEpF,EAAA,MAAM,WAAA,GAAoBA,kBAAY,MAAM;AAC1C,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS;AAEzB,IAAA,MAAA,CAAO,KAAK,CAAA,EAAG,WAAW,CAAA,YAAA,CAAA,EAAgB,EAAE,SAAS,CAAA;AACrD,IAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,EACvB,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,WAAW,CAAC,CAAA;AAGjC,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,YAAA,CAA2B,QAAQ,CAAA,IAAK,IAAA;AAEjE,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,SAAA,EAAW,KAAA,CAAM,SAAA,IAAc,CAAC,IAAA,IAAQ,YAAA;AAAA,IACxC,OAAA,EAAS,CAAC,CAAC,KAAA;AAAA,IACX,KAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;ACrJO,SAAS,eAKd,OAAA,EAC+B;AAC/B,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA,GAAa,GAAA;AAAA,IACb,MAAA,GAAS,CAAC,CAAA,KAAM,CAAA;AAAA,IAChB,KAAA,GAAQ,CAAC,CAAA,KAAM,CAAA;AAAA,IACf,SAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAGJ,EAAA,MAAM,WAAA,GAAc,KAAA,GAAS,KAAA,CAAkC,QAAkB,CAAA,GAAI,MAAA;AACrF,EAAA,MAAM,oBAAA,GAAuB,OAAO,WAAW,CAAA;AAG/C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAUC,eAAkB,oBAAoB,CAAA;AAChF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAUA,eAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAUA,eAAS,KAAK,CAAA;AAGlD,EAAA,MAAM,gBAAA,GAAyBA,aAA6C,IAAI,CAAA;AAChF,EAAA,MAAM,eAAA,GAAwBA,aAAgB,IAAI,CAAA;AAGlD,EAAMA,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,QAAA,EAAU;AACzB,MAAA,aAAA,CAAc,oBAAoB,CAAA;AAAA,IACpC;AAAA,EACF,CAAA,EAAG,CAAC,oBAAA,EAAsB,OAAA,EAAS,QAAQ,CAAC,CAAA;AAG5C,EAAA,MAAM,MAAA,GAAeA,kBAAY,YAAY;AAC3C,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,aAAA,GAAgB,gBAAgB,OAAA,IAAW,UAAA;AACjD,IAAA,MAAM,WAAA,GAAc,MAAM,aAAa,CAAA;AAEvC,IAAA,WAAA,CAAY,IAAI,CAAA;AAEhB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,EAAE,CAAC,QAAQ,GAAG,aAAwC,CAAA;AAClF,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,MAAA,SAAA,GAAY,MAAM,CAAA;AAAA,IACpB,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAE9D,MAAA,aAAA,CAAc,oBAAoB,CAAA;AAClC,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB,CAAA,SAAE;AACA,MAAA,WAAA,CAAY,KAAK,CAAA;AAAA,IACnB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,UAAA,EAAY,KAAA,EAAO,UAAU,MAAA,EAAQ,SAAA,EAAW,OAAA,EAAS,oBAAoB,CAAC,CAAA;AAG3F,EAAA,MAAM,aAAA,GAAsBA,KAAA,CAAA,WAAA;AAAA,IAC1B,CAAC,QAAA,KAAsB;AACrB,MAAA,aAAA,CAAc,QAAQ,CAAA;AACtB,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,eAAA,CAAgB,OAAA,GAAU,QAAA;AAE1B,MAAA,IAAI,eAAe,QAAA,EAAU;AAE3B,QAAA,KAAA,CAAM,YAAY;AAChB,UAAA,MAAM,WAAA,GAAc,MAAM,QAAQ,CAAA;AAClC,UAAA,WAAA,CAAY,IAAI,CAAA;AAChB,UAAA,IAAI;AACF,YAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,EAAE,CAAC,QAAQ,GAAG,aAAwC,CAAA;AAClF,YAAA,UAAA,CAAW,KAAK,CAAA;AAChB,YAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,YAAA,SAAA,GAAY,MAAM,CAAA;AAAA,UACpB,SAAS,GAAA,EAAK;AACZ,YAAA,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAAA,UAChE,CAAA,SAAE;AACA,YAAA,WAAA,CAAY,KAAK,CAAA;AAAA,UACnB;AAAA,QACF,CAAA,GAAG;AAAA,MACL,CAAA,MAAA,IAAW,eAAe,UAAA,EAAY;AAEpC,QAAA,IAAI,iBAAiB,OAAA,EAAS;AAC5B,UAAA,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAAA,QACvC;AAEA,QAAA,gBAAA,CAAiB,OAAA,GAAU,WAAW,MAAM;AAC1C,UAAA,KAAK,MAAA,EAAO;AAAA,QACd,GAAG,UAAU,CAAA;AAAA,MACf;AAAA,IAEF,CAAA;AAAA,IACA,CAAC,YAAY,KAAA,EAAO,QAAA,EAAU,QAAQ,SAAA,EAAW,OAAA,EAAS,YAAY,MAAM;AAAA,GAC9E;AAGA,EAAA,MAAM,MAAA,GAAeA,kBAAY,MAAM;AACrC,IAAA,IAAI,UAAA,KAAe,UAAU,OAAA,EAAS;AACpC,MAAA,KAAK,MAAA,EAAO;AAAA,IACd;AAAA,EACF,CAAA,EAAG,CAAC,UAAA,EAAY,OAAA,EAAS,MAAM,CAAC,CAAA;AAGhC,EAAMA,gBAAU,MAAM;AACpB,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,iBAAiB,OAAA,EAAS;AAC5B,QAAA,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAAA,MACvC;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,UAAA;AAAA,IACP,aAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF;AACF;AC1KO,SAAS,eAId,KAAA,EAA8E;AAC9E,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,QAAA;AAAA,IACb,UAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,KAAA;AAEJ,EAAA,MAAM,aAAA,GAAsBC,kBAAY,CAAC,CAAA,KAAe,QAAQ,CAAC,CAAA,EAAG,EAAE,CAAA;AACtE,EAAA,MAAM,YAAA,GAAqBA,kBAAY,CAAC,CAAA,KAAe,QAAQ,CAAC,CAAA,EAAG,EAAE,CAAA;AAErE,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,QAAA,KAAa,cAAA,CAA6C;AAAA,IACtF,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAQ,MAAA,IAAU,aAAA;AAAA,IAClB,OAAO,KAAA,IAAS,YAAA;AAAA,IAChB,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAqBA,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,KAAA,KAA+C;AAC9C,MAAA,aAAA,CAAc,KAAA,CAAM,OAAO,OAAO,CAAA;AAAA,IACpC,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAEA,EAAA,uBACEC,GAAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,IAAA,EAAK,UAAA;AAAA,MACL,OAAA,EAAS,QAAQ,KAAK,CAAA;AAAA,MACtB,QAAA,EAAU,YAAA;AAAA,MACV,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK;AAAA;AAAA,GACjC;AAEJ;AChDO,SAAS,gBAId,KAAA,EAA+E;AAC/E,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,MAAA;AAAA,IACb,UAAA,GAAa,GAAA;AAAA,IACb,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,KAAA;AAEJ,EAAA,MAAM,aAAA,GAAsBC,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAA,IAAK,EAAE,CAAA,EAAG,EAAE,CAAA;AAC3E,EAAA,MAAM,YAAA,GAAqBA,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAC,CAAA,EAAG,EAAE,CAAA;AAEpE,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,MAAA,EAAQ,QAAA,KAAa,cAAA,CAIjD;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAQ,MAAA,IAAU,aAAA;AAAA,IAClB,OAAO,KAAA,IAAS,YAAA;AAAA,IAChB,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAqBA,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,KAAA,KAA+C;AAC9C,MAAA,aAAA,CAAc,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAEA,EAAA,uBACED,GAAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,IAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,MACnB,QAAA,EAAU,YAAA;AAAA,MACV,MAAA;AAAA,MACA,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK;AAAA;AAAA,GACjC;AAEJ;ACrDO,SAAS,aAId,KAAA,EAA4E;AAC5E,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,QAAA;AAAA,IACb,UAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,KAAA;AAEJ,EAAA,MAAM,aAAA,GAAsBE,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAA,IAAK,EAAE,CAAA,EAAG,EAAE,CAAA;AAC3E,EAAA,MAAM,YAAA,GAAqBA,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAC,CAAA,EAAG,EAAE,CAAA;AAEpE,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,QAAA,KAAa,cAAA,CAA6C;AAAA,IACtF,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAQ,MAAA,IAAU,aAAA;AAAA,IAClB,OAAO,KAAA,IAAS,YAAA;AAAA,IAChB,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAqBA,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,KAAA,KAAgD;AAC/C,MAAA,aAAA,CAAc,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAEA,EAAA,uBACEF,GAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,WAAA;AAAA,MACJ,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,MACnB,QAAA,EAAU,YAAA;AAAA,MACV,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK,QAAA;AAAA,MAE9B;AAAA;AAAA,GACH;AAEJ;ACrDO,SAAS,aAId,KAAA,EAA4E;AAC5E,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,UAAA;AAAA,IACb,UAAA,GAAa,GAAA;AAAA,IACb,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,KAAA;AAEJ,EAAA,MAAM,aAAA,GAAsBG,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAA,IAAK,CAAC,CAAA,EAAG,EAAE,CAAA;AAC1E,EAAA,MAAM,YAAA,GAAqBA,kBAAY,CAAC,CAAA,KAAe,OAAO,CAAC,CAAA,EAAG,EAAE,CAAA;AAEpE,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,QAAA,KAAa,cAAA,CAA6C;AAAA,IACtF,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAQ,MAAA,IAAU,aAAA;AAAA,IAClB,OAAO,KAAA,IAAS,YAAA;AAAA,IAChB,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAqBA,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,KAAA,KAA+C;AAC9C,MAAA,aAAA,CAAc,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAC1C,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAEA,EAAA,uBACEH,GAAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,IAAA,EAAK,OAAA;AAAA,MACL,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,MACnB,QAAA,EAAU,YAAA;AAAA,MACV,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK;AAAA;AAAA,GACjC;AAEJ;ACpDO,SAAS,aAId,KAAA,EAA4E;AAC5E,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,GAAa,QAAA;AAAA,IACb,UAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,GAAG;AAAA,GACL,GAAI,KAAA;AAEJ,EAAA,MAAM,aAAA,GAAsBI,kBAAY,CAAC,CAAA,KAAe,QAAQ,CAAC,CAAA,EAAG,EAAE,CAAA;AACtE,EAAA,MAAM,YAAA,GAAqBA,kBAAY,CAAC,CAAA,KAAe,QAAQ,CAAC,CAAA,EAAG,EAAE,CAAA;AAErE,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAe,QAAA,KAAa,cAAA,CAA6C;AAAA,IACtF,KAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAQ,MAAA,IAAU,aAAA;AAAA,IAClB,OAAO,KAAA,IAAS,YAAA;AAAA,IAChB,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAqBA,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,KAAA,KAA+C;AAC9C,MAAA,aAAA,CAAc,KAAA,CAAM,OAAO,OAAO,CAAA;AAAA,IACpC,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAGA,EAAA,uBACEJ,GAAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,IAAA,EAAK,UAAA;AAAA,MACL,IAAA,EAAK,QAAA;AAAA,MACL,cAAA,EAAc,QAAQ,KAAK,CAAA;AAAA,MAC3B,OAAA,EAAS,QAAQ,KAAK,CAAA;AAAA,MACtB,QAAA,EAAU,YAAA;AAAA,MACV,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK,QAAA;AAAA,MAC/B;AAAA;AAAA,GACF;AAEJ","file":"index.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { io, Socket } from \"socket.io-client\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AccessLevel } from \"../shared/types\";\nimport type {\n QuickdrawSocketContextValue,\n QuickdrawProviderProps,\n} from \"./types\";\n\n// ============================================================================\n// Socket Context\n// ============================================================================\n\nconst QuickdrawSocketContext =\n React.createContext<QuickdrawSocketContextValue | null>(null);\n\n/**\n * Hook to access the quickdraw socket context.\n */\nexport function useQuickdrawSocket(): QuickdrawSocketContextValue {\n const context = React.useContext(QuickdrawSocketContext);\n if (!context) {\n throw new Error(\n \"useQuickdrawSocket must be used within a QuickdrawProvider\"\n );\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\nconst defaultQueryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n retry: 1,\n },\n mutations: {\n retry: 0,\n },\n },\n});\n\n/**\n * Provider component that sets up TanStack Query and Socket.io connection.\n *\n * @example\n * ```tsx\n * // In your app layout\n * export default function RootLayout({ children }) {\n * return (\n * <QuickdrawProvider\n * serverUrl=\"http://localhost:4000\"\n * authToken={token}\n * >\n * {children}\n * </QuickdrawProvider>\n * );\n * }\n * ```\n */\nexport function QuickdrawProvider({\n children,\n serverUrl,\n queryClient = defaultQueryClient,\n authToken,\n autoConnect = !!authToken,\n}: QuickdrawProviderProps): React.ReactElement {\n const [socket, setSocket] = React.useState<Socket | null>(null);\n const [isConnected, setIsConnected] = React.useState(false);\n const [userId, setUserId] = React.useState<string | null>(null);\n const [serviceAccess, setServiceAccess] = React.useState<\n Record<string, AccessLevel>\n >({});\n\n // Store authToken in ref to avoid reconnecting on every render\n const authTokenRef = React.useRef(authToken);\n React.useEffect(() => {\n authTokenRef.current = authToken;\n }, [authToken]);\n\n const connect = React.useCallback(\n (token?: string) => {\n const authToUse = token ?? authTokenRef.current;\n\n const newSocket = io(serverUrl, {\n auth: authToUse ? { token: authToUse } : undefined,\n transports: [\"websocket\", \"polling\"],\n autoConnect: true,\n });\n\n newSocket.on(\"connect\", () => {\n setIsConnected(true);\n });\n\n newSocket.on(\"disconnect\", () => {\n setIsConnected(false);\n });\n\n // Listen for auth info from server\n newSocket.on(\n \"auth:info\",\n (info: { userId: string; serviceAccess: Record<string, AccessLevel> }) => {\n setUserId(info.userId);\n setServiceAccess(info.serviceAccess);\n }\n );\n\n newSocket.on(\"connect_error\", (error) => {\n console.error(\"Socket connection error:\", error.message);\n });\n\n setSocket(newSocket);\n },\n [serverUrl]\n );\n\n const disconnect = React.useCallback(() => {\n if (socket) {\n socket.disconnect();\n setSocket(null);\n setIsConnected(false);\n setUserId(null);\n setServiceAccess({});\n }\n }, [socket]);\n\n // Auto-connect on mount if enabled\n React.useEffect(() => {\n if (autoConnect && !socket) {\n connect();\n }\n\n return () => {\n if (socket) {\n socket.disconnect();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoConnect]);\n\n // Reconnect when authToken changes\n React.useEffect(() => {\n if (socket && authToken !== authTokenRef.current) {\n disconnect();\n if (authToken) {\n connect(authToken);\n }\n }\n }, [authToken, socket, disconnect, connect]);\n\n const contextValue = React.useMemo<QuickdrawSocketContextValue>(\n () => ({\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n }),\n [socket, isConnected, userId, serviceAccess, connect, disconnect]\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <QuickdrawSocketContext.Provider value={contextValue}>\n {children}\n </QuickdrawSocketContext.Provider>\n </QueryClientProvider>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseServiceOptions, UseServiceResult } from \"./types\";\n\n/**\n * Hook for invoking service methods via Socket.io with TanStack Query integration.\n *\n * @typeParam TServiceMethods - Map of service method definitions\n * @typeParam TService - Service name\n * @typeParam TMethod - Method name\n *\n * @example\n * ```tsx\n * // Define your service methods type\n * type ChatServiceMethods = {\n * createChat: { payload: { title: string }; response: { id: string } };\n * updateTitle: { payload: { id: string; title: string }; response: { id: string; title: string } };\n * };\n *\n * // In your component\n * function CreateChatButton() {\n * const createChat = useService<ChatServiceMethods, 'chatService', 'createChat'>(\n * 'chatService',\n * 'createChat',\n * {\n * onSuccess: (data) => {\n * router.push(`/chat/${data.id}`);\n * },\n * }\n * );\n *\n * return (\n * <button\n * onClick={() => createChat.mutate({ title: 'New Chat' })}\n * disabled={createChat.isPending}\n * >\n * {createChat.isPending ? 'Creating...' : 'Create Chat'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useService<\n TServiceMethods extends Record<\n string,\n Record<string, { payload: unknown; response: unknown }>\n >,\n TService extends keyof TServiceMethods & string,\n TMethod extends keyof TServiceMethods[TService] & string,\n>(\n serviceName: TService,\n methodName: TMethod,\n options?: UseServiceOptions<TServiceMethods[TService][TMethod][\"response\"]>\n): UseServiceResult<\n TServiceMethods[TService][TMethod][\"payload\"],\n TServiceMethods[TService][TMethod][\"response\"]\n> {\n type TPayload = TServiceMethods[TService][TMethod][\"payload\"];\n type TResponse = TServiceMethods[TService][TMethod][\"response\"];\n\n const { socket, isConnected } = useQuickdrawSocket();\n const [error, setError] = React.useState<string | null>(null);\n\n // Store options in ref to avoid unnecessary rerenders\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const mutation = useMutation<TResponse, Error, TPayload>({\n mutationKey: [serviceName, methodName],\n mutationFn: async (payload: TPayload): Promise<TResponse> => {\n if (!socket || !isConnected) {\n throw new Error(\"Socket not connected\");\n }\n\n return new Promise<TResponse>((resolve, reject) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n reject(new Error(\"Request timeout\"));\n }, 10000);\n\n socket.emit(\n eventName,\n payload,\n (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 onSuccess: (data) => {\n setError(null);\n optionsRef.current?.onSuccess?.(data);\n },\n onError: (err) => {\n const errorMessage = err.message;\n setError(errorMessage);\n optionsRef.current?.onError?.(errorMessage);\n },\n retry: options?.retry ?? false,\n retryDelay: options?.retryDelay,\n });\n\n return {\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending,\n isError: mutation.isError,\n error,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n\n/**\n * Simplified hook for basic service method calls.\n * Use this when you don't need the full TanStack Query mutation features.\n *\n * @example\n * ```tsx\n * const { execute, loading, error, data } = useServiceMethod('chatService', 'createChat');\n *\n * const handleCreate = async () => {\n * const result = await execute({ title: 'New Chat' });\n * if (result) {\n * router.push(`/chat/${result.id}`);\n * }\n * };\n * ```\n */\nexport function useServiceMethod<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>\n): {\n execute: (payload: TPayload) => Promise<TResponse | null>;\n loading: boolean;\n error: string | null;\n data: TResponse | null;\n isReady: boolean;\n} {\n const { socket, isConnected } = useQuickdrawSocket();\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [data, setData] = React.useState<TResponse | null>(null);\n\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const execute = React.useCallback(\n async (payload: TPayload): Promise<TResponse | null> => {\n if (!socket || !isConnected) {\n const errorMsg = \"Socket not connected\";\n setError(errorMsg);\n optionsRef.current?.onError?.(errorMsg);\n return null;\n }\n\n setLoading(true);\n setError(null);\n\n return new Promise<TResponse | null>((resolve) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n const errorMsg = \"Request timeout\";\n setError(errorMsg);\n setLoading(false);\n optionsRef.current?.onError?.(errorMsg);\n resolve(null);\n }, 10000);\n\n socket.emit(\n eventName,\n payload,\n (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n setLoading(false);\n\n if (response.success) {\n setError(null);\n setData(response.data);\n optionsRef.current?.onSuccess?.(response.data);\n resolve(response.data);\n } else {\n setError(response.error);\n optionsRef.current?.onError?.(response.error);\n resolve(null);\n }\n }\n );\n });\n },\n [socket, isConnected, serviceName, methodName]\n );\n\n return {\n execute,\n loading,\n error,\n data,\n isReady: !!socket && isConnected,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseSubscriptionOptions, UseSubscriptionResult } from \"./types\";\n\n// Global subscription state for deduplication\nconst activeSubscriptions = new Map<\n string,\n {\n refCount: number;\n cleanup?: () => void;\n }\n>();\n\n/**\n * Hook for subscribing to real-time entity updates with TanStack Query integration.\n *\n * Features:\n * - Automatic subscription management\n * - TanStack Query cache integration for optimistic updates\n * - Subscription deduplication across components\n * - Automatic reconnection handling\n *\n * @typeParam TData - The entity type\n *\n * @example\n * ```tsx\n * function ChatView({ chatId }: { chatId: string }) {\n * const { data: chat, isLoading, error } = useSubscription<Chat>(\n * 'chatService',\n * chatId,\n * {\n * onData: (chat) => {\n * console.log('Chat updated:', chat.title);\n * },\n * }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error}</div>;\n * if (!chat) return <div>Chat not found</div>;\n *\n * return <div>{chat.title}</div>;\n * }\n * ```\n */\nexport function useSubscription<TData extends { id: string }>(\n serviceName: string,\n entryId: string | null,\n options: UseSubscriptionOptions<TData> = {}\n): UseSubscriptionResult<TData> {\n const { socket, isConnected } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [isSubscribed, setIsSubscribed] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const {\n enabled = true,\n onData,\n onError,\n requiredLevel = \"Read\",\n staleTime = Infinity, // Subscriptions should stay fresh via socket updates\n } = options;\n\n // Subscription key for deduplication\n const subscriptionKey = entryId ? `${serviceName}:${entryId}` : null;\n const queryKey = [serviceName, \"subscription\", entryId];\n\n // Store callbacks in refs to avoid unnecessary effect reruns\n const onDataRef = React.useRef(onData);\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onDataRef.current = onData;\n onErrorRef.current = onError;\n }, [onData, onError]);\n\n // Query for initial data and cache management\n const query = useQuery<TData | null>({\n queryKey,\n queryFn: async () => {\n // Data is populated via subscription, not direct fetch\n return null;\n },\n enabled: false, // We manage data via socket\n staleTime,\n });\n\n // Handle subscription lifecycle\n React.useEffect(() => {\n if (!socket || !isConnected || !subscriptionKey || !enabled || !entryId) {\n return;\n }\n\n // Check if already subscribed (deduplication)\n const existing = activeSubscriptions.get(subscriptionKey);\n if (existing) {\n existing.refCount++;\n setIsSubscribed(true);\n return () => {\n existing.refCount--;\n if (existing.refCount === 0) {\n existing.cleanup?.();\n activeSubscriptions.delete(subscriptionKey);\n }\n };\n }\n\n // Create new subscription\n const subscribeEvent = `${serviceName}:subscribe`;\n const updateEvent = `${serviceName}:update:${entryId}`;\n\n // Handle incoming updates\n const handleUpdate = (updateData: Partial<TData>) => {\n // Check for deletion\n const asAny = updateData as { deleted?: boolean };\n if (asAny.deleted) {\n queryClient.setQueryData<TData | null>(queryKey, null);\n return;\n }\n\n // Merge update with existing data\n queryClient.setQueryData<TData | null>(queryKey, (oldData) => {\n if (!oldData) {\n return updateData as TData;\n }\n const merged = { ...oldData, ...updateData } as TData;\n onDataRef.current?.(merged);\n return merged;\n });\n };\n\n // Subscribe to updates\n socket.on(updateEvent, handleUpdate);\n\n // Send subscription request\n socket.emit(\n subscribeEvent,\n { entryId, requiredLevel },\n (response: ServiceResponse<TData>) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data);\n setIsSubscribed(true);\n setError(null);\n onDataRef.current?.(response.data);\n } else if (!response.success) {\n setError(response.error);\n setIsSubscribed(false);\n onErrorRef.current?.(response.error);\n }\n }\n );\n\n // Cleanup function\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n };\n\n // Track subscription\n activeSubscriptions.set(subscriptionKey, {\n refCount: 1,\n cleanup,\n });\n\n return () => {\n const sub = activeSubscriptions.get(subscriptionKey);\n if (sub) {\n sub.refCount--;\n if (sub.refCount === 0) {\n sub.cleanup?.();\n activeSubscriptions.delete(subscriptionKey);\n }\n }\n };\n }, [\n socket,\n isConnected,\n subscriptionKey,\n enabled,\n entryId,\n serviceName,\n requiredLevel,\n queryClient,\n queryKey,\n ]);\n\n // Manual subscribe/unsubscribe functions\n const subscribe = React.useCallback(() => {\n if (!socket || !isConnected || !entryId) return;\n\n socket.emit(\n `${serviceName}:subscribe`,\n { entryId, requiredLevel },\n (response: ServiceResponse<TData>) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data);\n setIsSubscribed(true);\n setError(null);\n } else if (!response.success) {\n setError(response.error);\n }\n }\n );\n }, [socket, isConnected, entryId, serviceName, requiredLevel, queryClient, queryKey]);\n\n const unsubscribe = React.useCallback(() => {\n if (!socket || !entryId) return;\n\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n }, [socket, entryId, serviceName]);\n\n // Get current data from query cache\n const data = queryClient.getQueryData<TData | null>(queryKey) ?? null;\n\n return {\n data,\n isLoading: query.isLoading || (!data && isSubscribed),\n isError: !!error,\n error,\n isSubscribed,\n subscribe,\n unsubscribe,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport type CommitMode = \"change\" | \"blur\" | \"debounce\";\n\nexport interface UseSocketInputOptions<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate> {\n /**\n * Current state of the entity\n */\n state: TEntry | null;\n /**\n * Property key to update\n */\n property: TKey;\n /**\n * Update function that sends the patch to the server\n */\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n /**\n * When to commit changes to the server\n * - \"change\": immediately on every change\n * - \"blur\": when input loses focus\n * - \"debounce\": after debounceMs of inactivity\n */\n commitMode: CommitMode;\n /**\n * Debounce delay in milliseconds (only used when commitMode is \"debounce\")\n */\n debounceMs?: number;\n /**\n * Format the value from state for display\n */\n format?: (value: unknown) => unknown;\n /**\n * Parse the display value back to the state format\n */\n parse?: (value: unknown) => unknown;\n /**\n * Callback on successful update\n */\n onSuccess?: (entry: TEntry | undefined | null) => void;\n /**\n * Callback on error\n */\n onError?: (error: string) => void;\n}\n\nexport interface UseSocketInputResult<T> {\n /**\n * Current local value (may differ from server while editing)\n */\n value: T;\n /**\n * Update the local value\n */\n onLocalChange: (newValue: T) => void;\n /**\n * Manually commit the current value to the server\n */\n commit: () => void;\n /**\n * Handle blur event (commits if commitMode is \"blur\")\n */\n onBlur: () => void;\n /**\n * Whether an update is currently in flight\n */\n inFlight: boolean;\n /**\n * Whether the local value differs from the server value\n */\n isDirty: boolean;\n}\n\n/**\n * Hook for managing input state that syncs with the server.\n * Handles optimistic updates, debouncing, and conflict resolution.\n */\nexport function useSocketInput<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(\n options: UseSocketInputOptions<TEntry, TAllowedUpdate, TKey>\n): UseSocketInputResult<unknown> {\n const {\n state,\n property,\n update,\n commitMode,\n debounceMs = 300,\n format = (v) => v,\n parse = (v) => v,\n onSuccess,\n onError,\n } = options;\n\n // Get server value\n const serverValue = state ? (state as Record<string, unknown>)[property as string] : undefined;\n const formattedServerValue = format(serverValue);\n\n // Local state\n const [localValue, setLocalValue] = React.useState<unknown>(formattedServerValue);\n const [inFlight, setInFlight] = React.useState(false);\n const [isDirty, setIsDirty] = React.useState(false);\n\n // Refs for debouncing\n const debounceTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = React.useRef<unknown>(null);\n\n // Sync local value with server when server value changes (and we're not dirty)\n React.useEffect(() => {\n if (!isDirty && !inFlight) {\n setLocalValue(formattedServerValue);\n }\n }, [formattedServerValue, isDirty, inFlight]);\n\n // Commit function\n const commit = React.useCallback(async () => {\n if (!isDirty) return;\n\n const valueToCommit = pendingValueRef.current ?? localValue;\n const parsedValue = parse(valueToCommit);\n\n setInFlight(true);\n\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n // Revert to server value on error\n setLocalValue(formattedServerValue);\n setIsDirty(false);\n } finally {\n setInFlight(false);\n }\n }, [isDirty, localValue, parse, property, update, onSuccess, onError, formattedServerValue]);\n\n // Handle local change\n const onLocalChange = React.useCallback(\n (newValue: unknown) => {\n setLocalValue(newValue);\n setIsDirty(true);\n pendingValueRef.current = newValue;\n\n if (commitMode === \"change\") {\n // Commit immediately\n void (async () => {\n const parsedValue = parse(newValue);\n setInFlight(true);\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n } finally {\n setInFlight(false);\n }\n })();\n } else if (commitMode === \"debounce\") {\n // Clear existing timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n // Set new timer\n debounceTimerRef.current = setTimeout(() => {\n void commit();\n }, debounceMs);\n }\n // For \"blur\" mode, we just update local state and wait for blur\n },\n [commitMode, parse, property, update, onSuccess, onError, debounceMs, commit]\n );\n\n // Handle blur\n const onBlur = React.useCallback(() => {\n if (commitMode === \"blur\" && isDirty) {\n void commit();\n }\n }, [commitMode, isDirty, commit]);\n\n // Cleanup debounce timer\n React.useEffect(() => {\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n };\n }, []);\n\n return {\n value: localValue,\n onLocalChange,\n commit,\n onBlur,\n inFlight,\n isDirty,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketCheckboxProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A checkbox input that syncs its checked state with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketCheckbox\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"isArchived\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketCheckbox<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketCheckboxProps<TEntry, TAllowedUpdate, TKey>): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange]\n );\n\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketTextFieldProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A text input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketTextField\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"title\"\n * commitMode=\"debounce\"\n * debounceMs={500}\n * placeholder=\"Chat title...\"\n * />\n * ```\n */\nexport function SocketTextField<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketTextFieldProps<TEntry, TAllowedUpdate, TKey>): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"blur\",\n debounceMs = 300,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, onBlur, inFlight } = useSocketInput<\n TEntry,\n TAllowedUpdate,\n TKey\n >({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange]\n );\n\n return (\n <input\n {...inputProps}\n type=\"text\"\n value={String(value)}\n onChange={handleChange}\n onBlur={onBlur}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSelectProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n children: React.ReactNode;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A select input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSelect\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"status\"\n * commitMode=\"change\"\n * >\n * <option value=\"active\">Active</option>\n * <option value=\"archived\">Archived</option>\n * </SocketSelect>\n * ```\n */\nexport function SocketSelect<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSelectProps<TEntry, TAllowedUpdate, TKey>): React.ReactElement {\n const {\n state,\n update,\n property,\n children,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...selectProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLSelectElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange]\n );\n\n return (\n <select\n {...selectProps}\n value={String(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n >\n {children}\n </select>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSliderProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A range slider input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSlider\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"volume\"\n * min={0}\n * max={100}\n * commitMode=\"debounce\"\n * debounceMs={200}\n * />\n * ```\n */\nexport function SocketSlider<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSliderProps<TEntry, TAllowedUpdate, TKey>): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"debounce\",\n debounceMs = 200,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Number(v ?? 0), []);\n const defaultParse = React.useCallback((v: unknown) => Number(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(Number(event.target.value));\n },\n [onLocalChange]\n );\n\n return (\n <input\n {...inputProps}\n type=\"range\"\n value={Number(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSwitchProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A switch/toggle input that syncs its state with the server via socket.\n * Styled as a switch by default using CSS.\n *\n * @example\n * ```tsx\n * <SocketSwitch\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"darkMode\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketSwitch<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSwitchProps<TEntry, TAllowedUpdate, TKey>): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n className,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange]\n );\n\n // Role=\"switch\" for accessibility\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n role=\"switch\"\n aria-checked={Boolean(value)}\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n className={className}\n />\n );\n}\n"]}
@@ -0,0 +1,73 @@
1
+ import * as React from 'react';
2
+ import { QueryClient } from '@tanstack/react-query';
3
+ import { ServiceResponse } from '../shared/index.js';
4
+ import { Q as QuickdrawSocketContextValue } from '../types-CUExGF7F.js';
5
+ import 'zod';
6
+ import 'socket.io-client';
7
+
8
+ type MockFn = (...args: unknown[]) => unknown;
9
+ /**
10
+ * Mock socket for testing client components.
11
+ */
12
+ interface MockSocket {
13
+ id: string;
14
+ connected: boolean;
15
+ emit: MockFn;
16
+ on: MockFn;
17
+ off: MockFn;
18
+ disconnect: MockFn;
19
+ }
20
+ /**
21
+ * Create a mock socket for testing.
22
+ */
23
+ declare function createMockSocket(): MockSocket;
24
+ /**
25
+ * Create a mock QuickdrawSocketContextValue for testing.
26
+ */
27
+ declare function createMockSocketContext(overrides?: Partial<QuickdrawSocketContextValue>): QuickdrawSocketContextValue;
28
+ /**
29
+ * Mock socket emit that resolves with a successful response.
30
+ */
31
+ declare function mockSuccessEmit<T>(data: T): (_event: string, _payload: unknown, callback?: (response: ServiceResponse<T>) => void) => void;
32
+ /**
33
+ * Mock socket emit that resolves with an error response.
34
+ */
35
+ declare function mockErrorEmit(error: string, code?: number): (_event: string, _payload: unknown, callback?: (response: ServiceResponse<unknown>) => void) => void;
36
+ /**
37
+ * Create a mock QueryClient for testing.
38
+ */
39
+ declare function createMockQueryClient(): QueryClient;
40
+ /**
41
+ * Test wrapper component that provides the necessary context.
42
+ */
43
+ interface TestWrapperProps {
44
+ children: React.ReactNode;
45
+ socketContext?: Partial<QuickdrawSocketContextValue>;
46
+ queryClient?: QueryClient;
47
+ }
48
+ /**
49
+ * Create a test wrapper for rendering components with quickdraw context.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * import { render } from '@testing-library/react';
54
+ * import { createTestWrapper, createMockSocketContext, mockSuccessEmit } from '@quickdraw/core/client/testing';
55
+ *
56
+ * test('creates chat', async () => {
57
+ * const mockSocket = createMockSocket();
58
+ * mockSocket.emit.mockImplementation(mockSuccessEmit({ id: 'chat-123' }));
59
+ *
60
+ * const wrapper = createTestWrapper({
61
+ * socketContext: { socket: mockSocket },
62
+ * });
63
+ *
64
+ * const { getByText } = render(<CreateChatButton />, { wrapper });
65
+ * // ...
66
+ * });
67
+ * ```
68
+ */
69
+ declare function createTestWrapper(options?: Omit<TestWrapperProps, "children">): ({ children }: {
70
+ children: React.ReactNode;
71
+ }) => React.ReactElement;
72
+
73
+ export { type MockSocket, type TestWrapperProps, createMockQueryClient, createMockSocket, createMockSocketContext, createTestWrapper, mockErrorEmit, mockSuccessEmit };
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+ function createMockSocket() {
11
+ const createMock = () => {
12
+ const fn = () => void 0;
13
+ return fn;
14
+ };
15
+ return {
16
+ id: "mock-socket-id",
17
+ connected: true,
18
+ emit: createMock(),
19
+ on: createMock(),
20
+ off: createMock(),
21
+ disconnect: createMock()
22
+ };
23
+ }
24
+ function createMockSocketContext(overrides = {}) {
25
+ const mockSocket = createMockSocket();
26
+ return {
27
+ socket: mockSocket,
28
+ isConnected: true,
29
+ userId: "test-user-id",
30
+ serviceAccess: {},
31
+ connect: () => {
32
+ },
33
+ disconnect: () => {
34
+ },
35
+ ...overrides
36
+ };
37
+ }
38
+ function mockSuccessEmit(data) {
39
+ return (_event, _payload, callback) => {
40
+ callback?.({ success: true, data });
41
+ };
42
+ }
43
+ function mockErrorEmit(error, code) {
44
+ return (_event, _payload, callback) => {
45
+ const response = { success: false, error };
46
+ if (code !== void 0) {
47
+ response.code = code;
48
+ }
49
+ callback?.(response);
50
+ };
51
+ }
52
+ function createMockQueryClient() {
53
+ const { QueryClient } = __require("@tanstack/react-query");
54
+ return new QueryClient({
55
+ defaultOptions: {
56
+ queries: {
57
+ retry: false,
58
+ gcTime: 0
59
+ },
60
+ mutations: {
61
+ retry: false
62
+ }
63
+ }
64
+ });
65
+ }
66
+ function createTestWrapper(options = {}) {
67
+ const socketContext = createMockSocketContext(options.socketContext);
68
+ const queryClient = options.queryClient ?? createMockQueryClient();
69
+ const QuickdrawSocketContext = React.createContext(null);
70
+ return function TestWrapper({ children }) {
71
+ const { QueryClientProvider } = __require("@tanstack/react-query");
72
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(QuickdrawSocketContext.Provider, { value: socketContext, children }) });
73
+ };
74
+ }
75
+
76
+ export { createMockQueryClient, createMockSocket, createMockSocketContext, createTestWrapper, mockErrorEmit, mockSuccessEmit };
77
+ //# sourceMappingURL=testing.js.map
78
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/testing.tsx"],"names":[],"mappings":";;;;;;;;;AAwBO,SAAS,gBAAA,GAA+B;AAE7C,EAAA,MAAM,aAAa,MAAc;AAC/B,IAAA,MAAM,KAAa,MAAM,MAAA;AACzB,IAAA,OAAO,EAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,gBAAA;AAAA,IACJ,SAAA,EAAW,IAAA;AAAA,IACX,MAAM,UAAA,EAAW;AAAA,IACjB,IAAI,UAAA,EAAW;AAAA,IACf,KAAK,UAAA,EAAW;AAAA,IAChB,YAAY,UAAA;AAAW,GACzB;AACF;AAKO,SAAS,uBAAA,CACd,SAAA,GAAkD,EAAC,EACtB;AAC7B,EAAA,MAAM,aAAa,gBAAA,EAAiB;AAEpC,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,UAAA;AAAA,IACR,WAAA,EAAa,IAAA;AAAA,IACb,MAAA,EAAQ,cAAA;AAAA,IACR,eAAe,EAAC;AAAA,IAChB,SAAS,MAAM;AAAA,IAAC,CAAA;AAAA,IAChB,YAAY,MAAM;AAAA,IAAC,CAAA;AAAA,IACnB,GAAG;AAAA,GACL;AACF;AAKO,SAAS,gBAAmB,IAAA,EAIzB;AACR,EAAA,OAAO,CAAC,MAAA,EAAQ,QAAA,EAAU,QAAA,KAAa;AACrC,IAAA,QAAA,GAAW,EAAE,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,EACpC,CAAA;AACF;AAKO,SAAS,aAAA,CACd,OACA,IAAA,EAKQ;AACR,EAAA,OAAO,CAAC,MAAA,EAAQ,QAAA,EAAU,QAAA,KAAa;AACrC,IAAA,MAAM,QAAA,GAAqC,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAM;AACnE,IAAA,IAAI,SAAS,MAAA,EAAW;AACtB,MAAC,SAA6D,IAAA,GAAO,IAAA;AAAA,IACvE;AACA,IAAA,QAAA,GAAW,QAAQ,CAAA;AAAA,EACrB,CAAA;AACF;AAKO,SAAS,qBAAA,GAAqC;AAGnD,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,SAAA,CAAQ,uBAAuB,CAAA;AAIvD,EAAA,OAAO,IAAI,WAAA,CAAY;AAAA,IACrB,cAAA,EAAgB;AAAA,MACd,OAAA,EAAS;AAAA,QACP,KAAA,EAAO,KAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACV;AAAA,MACA,SAAA,EAAW;AAAA,QACT,KAAA,EAAO;AAAA;AACT;AACF,GACD,CAAA;AACH;AAgCO,SAAS,iBAAA,CACd,OAAA,GAA8C,EAAC,EACsB;AACrE,EAAA,MAAM,aAAA,GAAgB,uBAAA,CAAwB,OAAA,CAAQ,aAAa,CAAA;AACnE,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,qBAAA,EAAsB;AAGjE,EAAA,MAAM,sBAAA,GAA+B,oBAAkD,IAAI,CAAA;AAE3F,EAAA,OAAO,SAAS,WAAA,CAAY,EAAE,QAAA,EAAS,EAAsD;AAE3F,IAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,SAAA,CAAQ,uBAAuB,CAAA;AAI/D,IAAA,uBACE,GAAA,CAAC,mBAAA,EAAA,EAAoB,MAAA,EAAQ,WAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,aAAA,EACrC,QAAA,EACH,CAAA,EACF,CAAA;AAAA,EAEJ,CAAA;AACF","file":"testing.js","sourcesContent":["import * as React from \"react\";\nimport type { QueryClient } from \"@tanstack/react-query\";\nimport type { Socket } from \"socket.io-client\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport type { QuickdrawSocketContextValue } from \"./types\";\n\n// Generic mock function type that works with both Jest and Vitest\ntype MockFn = (...args: unknown[]) => unknown;\n\n/**\n * Mock socket for testing client components.\n */\nexport interface MockSocket {\n id: string;\n connected: boolean;\n emit: MockFn;\n on: MockFn;\n off: MockFn;\n disconnect: MockFn;\n}\n\n/**\n * Create a mock socket for testing.\n */\nexport function createMockSocket(): MockSocket {\n // Simple mock function that can be enhanced by the test framework\n const createMock = (): MockFn => {\n const fn: MockFn = () => undefined;\n return fn;\n };\n\n return {\n id: \"mock-socket-id\",\n connected: true,\n emit: createMock(),\n on: createMock(),\n off: createMock(),\n disconnect: createMock(),\n };\n}\n\n/**\n * Create a mock QuickdrawSocketContextValue for testing.\n */\nexport function createMockSocketContext(\n overrides: Partial<QuickdrawSocketContextValue> = {}\n): QuickdrawSocketContextValue {\n const mockSocket = createMockSocket();\n\n return {\n socket: mockSocket as unknown as Socket,\n isConnected: true,\n userId: \"test-user-id\",\n serviceAccess: {},\n connect: () => {},\n disconnect: () => {},\n ...overrides,\n };\n}\n\n/**\n * Mock socket emit that resolves with a successful response.\n */\nexport function mockSuccessEmit<T>(data: T): (\n _event: string,\n _payload: unknown,\n callback?: (response: ServiceResponse<T>) => void\n) => void {\n return (_event, _payload, callback) => {\n callback?.({ success: true, data });\n };\n}\n\n/**\n * Mock socket emit that resolves with an error response.\n */\nexport function mockErrorEmit(\n error: string,\n code?: number\n): (\n _event: string,\n _payload: unknown,\n callback?: (response: ServiceResponse<unknown>) => void\n) => void {\n return (_event, _payload, callback) => {\n const response: ServiceResponse<unknown> = { success: false, error };\n if (code !== undefined) {\n (response as { success: false; error: string; code: number }).code = code;\n }\n callback?.(response);\n };\n}\n\n/**\n * Create a mock QueryClient for testing.\n */\nexport function createMockQueryClient(): QueryClient {\n // Dynamic import to avoid bundling issues\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { QueryClient } = require(\"@tanstack/react-query\") as {\n QueryClient: new (options?: unknown) => QueryClient;\n };\n\n return new QueryClient({\n defaultOptions: {\n queries: {\n retry: false,\n gcTime: 0,\n },\n mutations: {\n retry: false,\n },\n },\n });\n}\n\n/**\n * Test wrapper component that provides the necessary context.\n */\nexport interface TestWrapperProps {\n children: React.ReactNode;\n socketContext?: Partial<QuickdrawSocketContextValue>;\n queryClient?: QueryClient;\n}\n\n/**\n * Create a test wrapper for rendering components with quickdraw context.\n *\n * @example\n * ```tsx\n * import { render } from '@testing-library/react';\n * import { createTestWrapper, createMockSocketContext, mockSuccessEmit } from '@quickdraw/core/client/testing';\n *\n * test('creates chat', async () => {\n * const mockSocket = createMockSocket();\n * mockSocket.emit.mockImplementation(mockSuccessEmit({ id: 'chat-123' }));\n *\n * const wrapper = createTestWrapper({\n * socketContext: { socket: mockSocket },\n * });\n *\n * const { getByText } = render(<CreateChatButton />, { wrapper });\n * // ...\n * });\n * ```\n */\nexport function createTestWrapper(\n options: Omit<TestWrapperProps, \"children\"> = {}\n): ({ children }: { children: React.ReactNode }) => React.ReactElement {\n const socketContext = createMockSocketContext(options.socketContext);\n const queryClient = options.queryClient ?? createMockQueryClient();\n\n // Create context for testing\n const QuickdrawSocketContext = React.createContext<QuickdrawSocketContextValue | null>(null);\n\n return function TestWrapper({ children }: { children: React.ReactNode }): React.ReactElement {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { QueryClientProvider } = require(\"@tanstack/react-query\") as {\n QueryClientProvider: React.ComponentType<{ client: QueryClient; children: React.ReactNode }>;\n };\n\n return (\n <QueryClientProvider client={queryClient}>\n <QuickdrawSocketContext.Provider value={socketContext}>\n {children}\n </QuickdrawSocketContext.Provider>\n </QueryClientProvider>\n );\n };\n}\n"]}
@@ -0,0 +1,336 @@
1
+ import { a as QuickdrawSocket, P as PrismaDelegate, b as BaseServiceOptions, I as InstallAdminMethodsOptions, S as ServiceRegistryInstance, B as BaseServiceInstance, c as QuickdrawServerOptions, Q as QuickdrawServerResult } from '../types-OhRxDBID.js';
2
+ import { z } from 'zod';
3
+ import { ACL, Logger, AccessLevel, ServiceMethodContext, ServiceMethodDefinition } from '../shared/index.js';
4
+ import { Server } from 'socket.io';
5
+ import 'http';
6
+
7
+ /**
8
+ * Base class for all quickdraw services.
9
+ *
10
+ * Provides:
11
+ * - Typed CRUD operations with auto-emit to subscribers
12
+ * - Real-time subscription management
13
+ * - ACL-based access control (service-level and entry-level)
14
+ * - Public method definition with type inference
15
+ * - Admin method scaffolding
16
+ *
17
+ * @typeParam TEntity - The entity type (e.g., Prisma model type)
18
+ * @typeParam TCreateInput - The create input type
19
+ * @typeParam TUpdateInput - The update input type
20
+ * @typeParam TServiceMethods - Service method definitions map
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * class ChatService extends BaseService<
25
+ * Chat,
26
+ * Prisma.ChatCreateInput,
27
+ * Prisma.ChatUpdateInput,
28
+ * ChatServiceMethods
29
+ * > {
30
+ * constructor(prisma: PrismaClient) {
31
+ * super({
32
+ * serviceName: 'chatService',
33
+ * hasEntryACL: true,
34
+ * });
35
+ * this.setDelegate(prisma.chat);
36
+ * }
37
+ * }
38
+ * ```
39
+ */
40
+ declare abstract class BaseService<TEntity extends {
41
+ id: string;
42
+ }, TCreateInput extends Record<string, unknown>, TUpdateInput extends Record<string, unknown>, TServiceMethods extends Record<string, {
43
+ payload: unknown;
44
+ response: unknown;
45
+ }> = Record<string, {
46
+ payload: unknown;
47
+ response: unknown;
48
+ }>> {
49
+ readonly serviceName: string;
50
+ protected readonly hasEntryACL: boolean;
51
+ protected readonly defaultACL: ACL;
52
+ protected readonly logger: Logger;
53
+ protected readonly subscribers: Map<string, Set<QuickdrawSocket>>;
54
+ protected delegate: PrismaDelegate<TEntity, TCreateInput, TUpdateInput> | undefined;
55
+ private readonly publicMethods;
56
+ constructor(options: BaseServiceOptions);
57
+ /**
58
+ * Set the Prisma delegate for this service.
59
+ * Must be called in the constructor of derived classes.
60
+ */
61
+ protected setDelegate(delegate: PrismaDelegate<TEntity, TCreateInput, TUpdateInput>): void;
62
+ /**
63
+ * Get the delegate, throwing if not set.
64
+ */
65
+ protected getDelegate(): PrismaDelegate<TEntity, TCreateInput, TUpdateInput>;
66
+ /**
67
+ * Subscribe a socket to an entity's updates.
68
+ * Returns the current entity data if access is granted, null otherwise.
69
+ */
70
+ subscribe(entryId: string, socket: QuickdrawSocket, requiredLevel?: AccessLevel): Promise<TEntity | null>;
71
+ /**
72
+ * Unsubscribe a socket from an entity's updates.
73
+ */
74
+ unsubscribe(entryId: string, socket: QuickdrawSocket): void;
75
+ /**
76
+ * Remove a socket from all subscriptions (called on disconnect).
77
+ */
78
+ unsubscribeSocket(socket: QuickdrawSocket): void;
79
+ /**
80
+ * Emit an update to all subscribers of an entity.
81
+ */
82
+ protected emitUpdate(entryId: string, data: Partial<TEntity>): void;
83
+ /**
84
+ * Check if a user has access to subscribe to an entity.
85
+ * Override in derived classes for custom logic.
86
+ */
87
+ protected checkSubscriptionAccess(userId: string, entryId: string, requiredLevel: AccessLevel, socket: QuickdrawSocket): Promise<boolean>;
88
+ /**
89
+ * Check if a socket has service-level access.
90
+ */
91
+ protected hasServiceAccess(socket: QuickdrawSocket, requiredLevel: AccessLevel): boolean;
92
+ /**
93
+ * Check if a user has access to an entity.
94
+ * Override in derived classes for custom logic (e.g., self-access).
95
+ */
96
+ protected checkAccess(_userId: string, _entryId: string, _requiredLevel: AccessLevel, _socket: QuickdrawSocket): boolean;
97
+ /**
98
+ * Check entry-level ACL stored on the entity.
99
+ */
100
+ protected checkEntryACL(userId: string, entryId: string, requiredLevel: AccessLevel): Promise<boolean>;
101
+ /**
102
+ * Compare access levels.
103
+ */
104
+ protected isLevelSufficient(userLevel: AccessLevel, requiredLevel: AccessLevel): boolean;
105
+ /**
106
+ * Ensure access for a public method.
107
+ * Throws if access is denied.
108
+ */
109
+ ensureAccessForMethod(requiredLevel: AccessLevel, socket: QuickdrawSocket, entryId?: string): Promise<void>;
110
+ /**
111
+ * Find an entity by ID.
112
+ */
113
+ protected findById(id: string): Promise<TEntity | null>;
114
+ /**
115
+ * Create an entity and emit to subscribers.
116
+ */
117
+ protected create(data: TCreateInput): Promise<TEntity>;
118
+ /**
119
+ * Update an entity and emit to subscribers.
120
+ */
121
+ protected update(id: string, data: TUpdateInput): Promise<TEntity | null>;
122
+ /**
123
+ * Delete an entity and emit deletion event to subscribers.
124
+ */
125
+ protected delete(id: string): Promise<boolean>;
126
+ /**
127
+ * Define a public method that will be exposed via Socket.io.
128
+ *
129
+ * @typeParam K - Method name from the service methods map
130
+ */
131
+ protected defineMethod<K extends keyof TServiceMethods & string>(name: K, access: AccessLevel, handler: (payload: TServiceMethods[K]["payload"], context: ServiceMethodContext) => Promise<TServiceMethods[K]["response"]>, options?: {
132
+ schema?: z.ZodType<TServiceMethods[K]["payload"]>;
133
+ resolveEntryId?: (payload: TServiceMethods[K]["payload"]) => string | null;
134
+ }): ServiceMethodDefinition<TServiceMethods[K]["payload"], TServiceMethods[K]["response"]>;
135
+ /**
136
+ * Get all public methods for registry discovery.
137
+ */
138
+ getPublicMethods(): ServiceMethodDefinition<unknown, unknown>[];
139
+ /**
140
+ * Install standard admin CRUD methods.
141
+ * Call this in derived class constructor to expose admin endpoints.
142
+ */
143
+ protected installAdminMethods(_options: InstallAdminMethodsOptions): void;
144
+ /**
145
+ * Fields that should never be exposed or editable via admin methods.
146
+ */
147
+ protected getDefaultDeniedFields(): string[];
148
+ }
149
+
150
+ /**
151
+ * Central registry for auto-discovering and registering service methods as Socket.io events.
152
+ *
153
+ * Features:
154
+ * - Auto-discovers public methods from registered services
155
+ * - Wires methods to Socket.io events with standardized naming
156
+ * - Handles subscription lifecycle
157
+ * - Provides consistent error handling and logging
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const registry = new ServiceRegistry(io, { logger });
162
+ * registry.registerService('chatService', chatService);
163
+ * registry.registerService('userService', userService);
164
+ * ```
165
+ */
166
+ declare class ServiceRegistry implements ServiceRegistryInstance {
167
+ private readonly io;
168
+ private readonly services;
169
+ private readonly logger;
170
+ constructor(io: Server, options?: {
171
+ logger?: Logger;
172
+ });
173
+ /**
174
+ * Register a service and auto-discover its public methods.
175
+ */
176
+ registerService(serviceName: string, service: BaseServiceInstance): void;
177
+ /**
178
+ * Discover and register all public methods from a service.
179
+ */
180
+ private discoverServiceMethods;
181
+ /**
182
+ * Register a method as a socket event listener.
183
+ */
184
+ private registerMethodListener;
185
+ /**
186
+ * Register subscription listener for a service.
187
+ */
188
+ private registerSubscriptionListener;
189
+ /**
190
+ * Register unsubscription listener for a service.
191
+ */
192
+ private registerUnsubscriptionListener;
193
+ /**
194
+ * Get list of registered service names.
195
+ */
196
+ getServices(): string[];
197
+ /**
198
+ * Get all service instances for lifecycle management.
199
+ */
200
+ getServiceInstances(): BaseServiceInstance[];
201
+ }
202
+
203
+ /**
204
+ * Create a fully configured quickdraw server with one function call.
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * import { createQuickdrawServer } from '@quickdraw/core/server';
209
+ * import { ChatService, UserService } from './services';
210
+ *
211
+ * const { io, httpServer, registry } = createQuickdrawServer({
212
+ * port: 4000,
213
+ * cors: { origin: 'http://localhost:3000' },
214
+ * services: {
215
+ * chatService: new ChatService(prisma),
216
+ * userService: new UserService(prisma),
217
+ * },
218
+ * auth: {
219
+ * authenticate: async (socket, auth) => {
220
+ * // Verify JWT and return userId
221
+ * const payload = await verifyJWT(auth.token);
222
+ * return payload.userId;
223
+ * },
224
+ * },
225
+ * });
226
+ * ```
227
+ */
228
+ declare function createQuickdrawServer(options: QuickdrawServerOptions): QuickdrawServerResult;
229
+
230
+ interface JWTPayload {
231
+ userId: string;
232
+ email?: string;
233
+ exp?: number;
234
+ iat?: number;
235
+ }
236
+ /**
237
+ * Create a JWT token for authentication.
238
+ *
239
+ * @param payload - The payload to encode in the JWT
240
+ * @param secret - The secret key for signing
241
+ * @param expiresIn - Expiration time (default: 7 days)
242
+ */
243
+ declare function createJWT(payload: Omit<JWTPayload, "exp" | "iat">, secret: string, expiresIn?: string): Promise<string>;
244
+ /**
245
+ * Verify and decode a JWT token.
246
+ *
247
+ * @param token - The JWT token to verify
248
+ * @param secret - The secret key for verification
249
+ * @returns The decoded payload, or null if invalid
250
+ */
251
+ declare function verifyJWT(token: string, secret: string): Promise<JWTPayload | null>;
252
+
253
+ /**
254
+ * Generic OAuth provider interface.
255
+ */
256
+ interface OAuthProvider<TUser = unknown> {
257
+ name: string;
258
+ authorizationUrl: string;
259
+ tokenUrl: string;
260
+ userInfoUrl: string;
261
+ scopes: string[];
262
+ /**
263
+ * Parse user info response into a standardized format.
264
+ */
265
+ parseUserInfo: (data: unknown) => TUser;
266
+ }
267
+ /**
268
+ * OAuth configuration for a provider.
269
+ */
270
+ interface OAuthConfig {
271
+ clientId: string;
272
+ clientSecret: string;
273
+ redirectUri: string;
274
+ }
275
+ /**
276
+ * OAuth token response.
277
+ */
278
+ interface OAuthTokenResponse {
279
+ access_token: string;
280
+ token_type: string;
281
+ expires_in?: number;
282
+ refresh_token?: string;
283
+ scope?: string;
284
+ }
285
+ /**
286
+ * Create an OAuth authorization URL.
287
+ *
288
+ * @param provider - The OAuth provider
289
+ * @param config - OAuth configuration
290
+ * @param state - Optional state parameter for CSRF protection
291
+ */
292
+ declare function createOAuthURL(provider: OAuthProvider, config: OAuthConfig, state?: string): string;
293
+ /**
294
+ * Exchange an OAuth authorization code for tokens and user info.
295
+ *
296
+ * @param provider - The OAuth provider
297
+ * @param config - OAuth configuration
298
+ * @param code - The authorization code from the callback
299
+ */
300
+ declare function exchangeOAuthCode<TUser>(provider: OAuthProvider<TUser>, config: OAuthConfig, code: string): Promise<{
301
+ tokens: OAuthTokenResponse;
302
+ user: TUser;
303
+ }>;
304
+
305
+ /**
306
+ * Discord user information.
307
+ */
308
+ interface DiscordUser {
309
+ id: string;
310
+ username: string;
311
+ discriminator: string;
312
+ email: string | null;
313
+ avatar: string | null;
314
+ verified: boolean;
315
+ }
316
+ /**
317
+ * Discord OAuth provider configuration.
318
+ */
319
+ declare const discordProvider: OAuthProvider<DiscordUser>;
320
+
321
+ /**
322
+ * Google user information.
323
+ */
324
+ interface GoogleUser {
325
+ id: string;
326
+ email: string;
327
+ name: string;
328
+ picture: string | null;
329
+ verified_email: boolean;
330
+ }
331
+ /**
332
+ * Google OAuth provider configuration.
333
+ */
334
+ declare const googleProvider: OAuthProvider<GoogleUser>;
335
+
336
+ export { BaseService, BaseServiceInstance, BaseServiceOptions, type DiscordUser, type GoogleUser, InstallAdminMethodsOptions, type JWTPayload, type OAuthConfig, type OAuthProvider, type OAuthTokenResponse, PrismaDelegate, QuickdrawServerOptions, QuickdrawServerResult, QuickdrawSocket, ServiceRegistry, ServiceRegistryInstance, createJWT, createOAuthURL, createQuickdrawServer, discordProvider, exchangeOAuthCode, googleProvider, verifyJWT };