@fitzzero/quickdraw-core 1.0.0 → 1.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.
- package/dist/client/index.js +22 -16
- package/dist/client/index.js.map +1 -1
- package/dist/server/index.d.ts +162 -6
- package/dist/server/index.js +346 -20
- package/dist/server/index.js.map +1 -1
- package/dist/server/testing.d.ts +2 -2
- package/dist/server/testing.js +79 -16
- package/dist/server/testing.js.map +1 -1
- package/dist/shared/index.d.ts +49 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/{types-CDhCEpTU.d.ts → types-D7oHWh4O.d.ts} +29 -1
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -82,13 +82,12 @@ function QuickdrawProvider({
|
|
|
82
82
|
const subscriptionRegistryRef = React.useRef(
|
|
83
83
|
createSubscriptionRegistry()
|
|
84
84
|
);
|
|
85
|
-
const
|
|
86
|
-
React.
|
|
87
|
-
authTokenRef.current = authToken;
|
|
88
|
-
}, [authToken]);
|
|
85
|
+
const prevAuthTokenRef = React.useRef(authToken);
|
|
86
|
+
const socketRef = React.useRef(null);
|
|
89
87
|
const connect = React.useCallback(
|
|
90
88
|
(token) => {
|
|
91
|
-
|
|
89
|
+
if (socketRef.current) return;
|
|
90
|
+
const authToUse = token;
|
|
92
91
|
subscriptionRegistryRef.current.clear();
|
|
93
92
|
subscriptionRegistryRef.current = createSubscriptionRegistry();
|
|
94
93
|
const newSocket = io(serverUrl, {
|
|
@@ -113,39 +112,46 @@ function QuickdrawProvider({
|
|
|
113
112
|
newSocket.on("connect_error", (error) => {
|
|
114
113
|
console.error("Socket connection error:", error.message);
|
|
115
114
|
});
|
|
115
|
+
socketRef.current = newSocket;
|
|
116
116
|
setSocket(newSocket);
|
|
117
117
|
},
|
|
118
118
|
[serverUrl]
|
|
119
119
|
);
|
|
120
120
|
const disconnect = React.useCallback(() => {
|
|
121
|
-
if (
|
|
121
|
+
if (socketRef.current) {
|
|
122
122
|
subscriptionRegistryRef.current.clear();
|
|
123
|
-
|
|
123
|
+
socketRef.current.disconnect();
|
|
124
|
+
socketRef.current = null;
|
|
124
125
|
setSocket(null);
|
|
125
126
|
setIsConnected(false);
|
|
126
127
|
setUserId(null);
|
|
127
128
|
setServiceAccess({});
|
|
128
129
|
}
|
|
129
|
-
}, [
|
|
130
|
+
}, []);
|
|
130
131
|
React.useEffect(() => {
|
|
131
|
-
if (autoConnect && !
|
|
132
|
-
connect();
|
|
132
|
+
if (autoConnect && !socketRef.current) {
|
|
133
|
+
connect(authToken);
|
|
133
134
|
}
|
|
134
135
|
return () => {
|
|
135
|
-
if (
|
|
136
|
+
if (socketRef.current) {
|
|
136
137
|
subscriptionRegistryRef.current.clear();
|
|
137
|
-
|
|
138
|
+
socketRef.current.disconnect();
|
|
139
|
+
socketRef.current = null;
|
|
138
140
|
}
|
|
139
141
|
};
|
|
140
|
-
}, [autoConnect]);
|
|
142
|
+
}, [autoConnect, authToken]);
|
|
141
143
|
React.useEffect(() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
const prevToken = prevAuthTokenRef.current;
|
|
145
|
+
prevAuthTokenRef.current = authToken;
|
|
146
|
+
if (authToken !== prevToken) {
|
|
147
|
+
if (socketRef.current) {
|
|
148
|
+
disconnect();
|
|
149
|
+
}
|
|
144
150
|
if (authToken) {
|
|
145
151
|
connect(authToken);
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
|
-
}, [authToken,
|
|
154
|
+
}, [authToken, disconnect, connect]);
|
|
149
155
|
const contextValue = React.useMemo(
|
|
150
156
|
() => ({
|
|
151
157
|
socket,
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +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":";;;;;;AAqBA,SAAS,0BAAA,GAAmD;AAC1D,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAA+B;AAEzD,EAAA,OAAO;AAAA,IACL,QAAQ,GAAA,EAAa;AACnB,MAAA,MAAM,QAAA,GAAW,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA;AACtC,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,QAAA,CAAS,QAAA,EAAA;AACT,QAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,QAAA,EAAS;AAAA,MACzC;AAEA,MAAA,MAAM,KAAA,GAA2B,EAAE,QAAA,EAAU,CAAA,EAAE;AAC/C,MAAA,aAAA,CAAc,GAAA,CAAI,KAAK,KAAK,CAAA;AAC5B,MAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,KAAA,EAAM;AAAA,IAC9B,CAAA;AAAA,IAEA,QAAQ,GAAA,EAAa;AACnB,MAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA;AACnC,MAAA,IAAI,CAAC,OAAO,OAAO,KAAA;AAEnB,MAAA,KAAA,CAAM,QAAA,EAAA;AACN,MAAA,IAAI,KAAA,CAAM,YAAY,CAAA,EAAG;AACvB,QAAA,KAAA,CAAM,OAAA,IAAU;AAChB,QAAA,aAAA,CAAc,OAAO,GAAG,CAAA;AACxB,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,OAAO,KAAA;AAAA,IACT,CAAA;AAAA,IAEA,UAAA,CAAW,KAAa,OAAA,EAAqB;AAC3C,MAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA;AACnC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,KAAA,CAAM,OAAA,GAAU,OAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAAA,IAEA,KAAA,GAAQ;AACN,MAAA,KAAA,MAAW,KAAA,IAAS,aAAA,CAAc,MAAA,EAAO,EAAG;AAC1C,QAAA,KAAA,CAAM,OAAA,IAAU;AAAA,MAClB;AACA,MAAA,aAAA,CAAc,KAAA,EAAM;AAAA,IACtB;AAAA,GACF;AACF;AAMA,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;AAWA,SAAS,iBAAA,GAAiC;AACxC,EAAA,OAAO,IAAI,WAAA,CAAY;AAAA,IACrB,cAAA,EAAgB;AAAA,MACd,OAAA,EAAS;AAAA,QACP,SAAA,EAAW,MAAO,EAAA,GAAK,CAAA;AAAA;AAAA,QACvB,KAAA,EAAO;AAAA,OACT;AAAA,MACA,SAAA,EAAW;AAAA,QACT,KAAA,EAAO;AAAA;AACT;AACF,GACD,CAAA;AACH;AAoBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA,GAAc,CAAC,CAAC;AAClB,CAAA,EAA+C;AAE7C,EAAA,MAAM,CAAC,kBAAkB,CAAA,GAAU,KAAA,CAAA,QAAA,CAAS,MAAM,mBAAmB,CAAA;AACrE,EAAA,MAAM,oBAAoB,WAAA,IAAe,kBAAA;AAEzC,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,uBAAA,GAAgC,KAAA,CAAA,MAAA;AAAA,IACpC,0BAAA;AAA2B,GAC7B;AAGA,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;AAGxC,MAAA,uBAAA,CAAwB,QAAQ,KAAA,EAAM;AACtC,MAAA,uBAAA,CAAwB,UAAU,0BAAA,EAA2B;AAE7D,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;AAEpB,QAAA,uBAAA,CAAwB,QAAQ,KAAA,EAAM;AAAA,MACxC,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;AAEV,MAAA,uBAAA,CAAwB,QAAQ,KAAA,EAAM;AACtC,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,uBAAA,CAAwB,QAAQ,KAAA,EAAM;AACtC,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,UAAA;AAAA,MACA,sBAAsB,uBAAA,CAAwB;AAAA,KAChD,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,iBAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,YAAA,EACrC,QAAA,EACH,CAAA,EACF,CAAA;AAEJ;ACzNO,SAAS,UAAA,CACd,WAAA,EACA,UAAA,EACA,OAAA,EACuC;AACvC,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,CAAA,EAAG,OAAA,EAAS,OAAA,IAAW,GAAK,CAAA;AAE5B,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;AAqBO,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,CAAA,EAAG,OAAA,EAAS,OAAA,IAAW,GAAK,CAAA;AAE5B,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,UAAA,EAAY,SAAS,OAAO;AAAA,GACjE;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;AC5JO,SAAS,eAAA,CACd,WAAA,EACA,OAAA,EACA,OAAA,GAAyC,EAAC,EACZ;AAC9B,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,oBAAA,KAAyB,kBAAA,EAAmB;AACzE,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,GAAiBA,KAAA,CAAA,OAAA;AAAA,IACrB,MAAM,CAAC,WAAA,EAAa,cAAA,EAAgB,OAAO,CAAA;AAAA,IAC3C,CAAC,aAAa,OAAO;AAAA,GACvB;AAGA,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,EAAE,KAAA,EAAM,GAAI,oBAAA,CAAqB,QAAQ,eAAe,CAAA;AAE9D,IAAA,IAAI,CAAC,KAAA,EAAO;AAEV,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,OAAO,MAAM;AACX,QAAA,oBAAA,CAAqB,QAAQ,eAAe,CAAA;AAAA,MAC9C,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;AAEA,IAAA,oBAAA,CAAqB,UAAA,CAAW,iBAAiB,OAAO,CAAA;AAExD,IAAA,OAAO,MAAM;AACX,MAAA,oBAAA,CAAqB,QAAQ,eAAe,CAAA;AAAA,IAC9C,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,QAAA;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;ACnIO,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 SubscriptionRegistry,\n SubscriptionEntry,\n} from \"./types\";\n\n// ============================================================================\n// Subscription Registry Implementation\n// ============================================================================\n\n/**\n * Creates a new subscription registry instance.\n * Each socket connection should have its own registry.\n */\nfunction createSubscriptionRegistry(): SubscriptionRegistry {\n const subscriptions = new Map<string, SubscriptionEntry>();\n\n return {\n acquire(key: string) {\n const existing = subscriptions.get(key);\n if (existing) {\n existing.refCount++;\n return { isNew: false, entry: existing };\n }\n\n const entry: SubscriptionEntry = { refCount: 1 };\n subscriptions.set(key, entry);\n return { isNew: true, entry };\n },\n\n release(key: string) {\n const entry = subscriptions.get(key);\n if (!entry) return false;\n\n entry.refCount--;\n if (entry.refCount <= 0) {\n entry.cleanup?.();\n subscriptions.delete(key);\n return true;\n }\n return false;\n },\n\n setCleanup(key: string, cleanup: () => void) {\n const entry = subscriptions.get(key);\n if (entry) {\n entry.cleanup = cleanup;\n }\n },\n\n clear() {\n for (const entry of subscriptions.values()) {\n entry.cleanup?.();\n }\n subscriptions.clear();\n },\n };\n}\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\n/**\n * Creates a QueryClient instance.\n * Using a factory function instead of module-level instantiation\n * to avoid SSR hydration issues.\n */\nfunction createQueryClient(): QueryClient {\n return 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/**\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,\n authToken,\n autoConnect = !!authToken,\n}: QuickdrawProviderProps): React.ReactElement {\n // Create QueryClient lazily to avoid SSR issues\n const [defaultQueryClient] = React.useState(() => createQueryClient());\n const actualQueryClient = queryClient ?? defaultQueryClient;\n\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 // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(\n createSubscriptionRegistry()\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 // Clear old subscriptions when creating new socket\n subscriptionRegistryRef.current.clear();\n subscriptionRegistryRef.current = createSubscriptionRegistry();\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 // Clear subscriptions on disconnect - they'll be re-established on reconnect\n subscriptionRegistryRef.current.clear();\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 // Clear subscriptions before disconnecting\n subscriptionRegistryRef.current.clear();\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 subscriptionRegistryRef.current.clear();\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 subscriptionRegistry: subscriptionRegistryRef.current,\n }),\n [socket, isConnected, userId, serviceAccess, connect, disconnect]\n );\n\n return (\n <QueryClientProvider client={actualQueryClient}>\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 * This hook supports two usage patterns:\n *\n * 1. **Simple usage** - Just pass service/method names with explicit payload/response types:\n * ```tsx\n * const createChat = useService<{ title: string }, { id: string }>(\n * 'chatService',\n * 'createChat'\n * );\n * ```\n *\n * 2. **Type-safe usage** - Pass a service methods map for full type inference:\n * ```tsx\n * // In your app, create a typed wrapper:\n * function useTypedService<\n * TService extends keyof ServiceMethodsMap,\n * TMethod extends keyof ServiceMethodsMap[TService] & string,\n * >(serviceName: TService, methodName: TMethod, options?: ...) {\n * type Methods = ServiceMethodsMap[TService];\n * type Payload = Methods[TMethod] extends { payload: infer P } ? P : never;\n * type Response = Methods[TMethod] extends { response: infer R } ? R : never;\n * return useService<Payload, Response>(serviceName, methodName, options);\n * }\n * ```\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n */\nexport function useService<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>\n): UseServiceResult<TPayload, TResponse> {\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 }, options?.timeout ?? 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<\n * { title: string },\n * { id: string }\n * >('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 }, options?.timeout ?? 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, options?.timeout]\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/**\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 (via context-based registry)\n * - Automatic reconnection handling\n * - HMR/Fast Refresh safe (no module-level state)\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, subscriptionRegistry } = 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 = React.useMemo(\n () => [serviceName, \"subscription\", entryId],\n [serviceName, entryId]\n );\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 // Try to acquire subscription (with deduplication)\n const { isNew } = subscriptionRegistry.acquire(subscriptionKey);\n\n if (!isNew) {\n // Already subscribed by another component, just track ref count\n setIsSubscribed(true);\n return () => {\n subscriptionRegistry.release(subscriptionKey);\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 // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n };\n\n subscriptionRegistry.setCleanup(subscriptionKey, cleanup);\n\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n subscriptionKey,\n enabled,\n entryId,\n serviceName,\n requiredLevel,\n queryClient,\n queryKey,\n subscriptionRegistry,\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"]}
|
|
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":"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","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 SubscriptionRegistry,\n SubscriptionEntry,\n} from \"./types\";\n\n// ============================================================================\n// Subscription Registry Implementation\n// ============================================================================\n\n/**\n * Creates a new subscription registry instance.\n * Each socket connection should have its own registry.\n */\nfunction createSubscriptionRegistry(): SubscriptionRegistry {\n const subscriptions = new Map<string, SubscriptionEntry>();\n\n return {\n acquire(key: string) {\n const existing = subscriptions.get(key);\n if (existing) {\n existing.refCount++;\n return { isNew: false, entry: existing };\n }\n\n const entry: SubscriptionEntry = { refCount: 1 };\n subscriptions.set(key, entry);\n return { isNew: true, entry };\n },\n\n release(key: string) {\n const entry = subscriptions.get(key);\n if (!entry) return false;\n\n entry.refCount--;\n if (entry.refCount <= 0) {\n entry.cleanup?.();\n subscriptions.delete(key);\n return true;\n }\n return false;\n },\n\n setCleanup(key: string, cleanup: () => void) {\n const entry = subscriptions.get(key);\n if (entry) {\n entry.cleanup = cleanup;\n }\n },\n\n clear() {\n for (const entry of subscriptions.values()) {\n entry.cleanup?.();\n }\n subscriptions.clear();\n },\n };\n}\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\n/**\n * Creates a QueryClient instance.\n * Using a factory function instead of module-level instantiation\n * to avoid SSR hydration issues.\n */\nfunction createQueryClient(): QueryClient {\n return 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/**\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,\n authToken,\n autoConnect = !!authToken,\n}: QuickdrawProviderProps): React.ReactElement {\n // Create QueryClient lazily to avoid SSR issues\n const [defaultQueryClient] = React.useState(() => createQueryClient());\n const actualQueryClient = queryClient ?? defaultQueryClient;\n\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 // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(\n createSubscriptionRegistry()\n );\n\n // Track previous authToken to detect changes\n const prevAuthTokenRef = React.useRef<string | undefined>(authToken);\n\n // Track socket synchronously to prevent double connections from concurrent effects\n const socketRef = React.useRef<Socket | null>(null);\n\n const connect = React.useCallback(\n (token?: string) => {\n // Prevent double connections - check ref synchronously since state updates are async\n if (socketRef.current) return;\n\n const authToUse = token;\n\n // Clear old subscriptions when creating new socket\n subscriptionRegistryRef.current.clear();\n subscriptionRegistryRef.current = createSubscriptionRegistry();\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 // Clear subscriptions on disconnect - they'll be re-established on reconnect\n subscriptionRegistryRef.current.clear();\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 socketRef.current = newSocket;\n setSocket(newSocket);\n },\n [serverUrl]\n );\n\n const disconnect = React.useCallback(() => {\n if (socketRef.current) {\n // Clear subscriptions before disconnecting\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n setSocket(null);\n setIsConnected(false);\n setUserId(null);\n setServiceAccess({});\n }\n }, []);\n\n // Auto-connect on mount if enabled\n React.useEffect(() => {\n if (autoConnect && !socketRef.current) {\n connect(authToken);\n }\n\n return () => {\n if (socketRef.current) {\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoConnect, authToken]);\n\n // Reconnect when authToken changes\n React.useEffect(() => {\n const prevToken = prevAuthTokenRef.current;\n \n // Update ref BEFORE any logic so next render has correct previous value\n prevAuthTokenRef.current = authToken;\n \n // If token actually changed, reconnect\n if (authToken !== prevToken) {\n if (socketRef.current) {\n disconnect();\n }\n if (authToken) {\n connect(authToken);\n }\n }\n }, [authToken, disconnect, connect]);\n\n const contextValue = React.useMemo<QuickdrawSocketContextValue>(\n () => ({\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n subscriptionRegistry: subscriptionRegistryRef.current,\n }),\n [socket, isConnected, userId, serviceAccess, connect, disconnect]\n );\n\n return (\n <QueryClientProvider client={actualQueryClient}>\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 * This hook supports two usage patterns:\n *\n * 1. **Simple usage** - Just pass service/method names with explicit payload/response types:\n * ```tsx\n * const createChat = useService<{ title: string }, { id: string }>(\n * 'chatService',\n * 'createChat'\n * );\n * ```\n *\n * 2. **Type-safe usage** - Pass a service methods map for full type inference:\n * ```tsx\n * // In your app, create a typed wrapper:\n * function useTypedService<\n * TService extends keyof ServiceMethodsMap,\n * TMethod extends keyof ServiceMethodsMap[TService] & string,\n * >(serviceName: TService, methodName: TMethod, options?: ...) {\n * type Methods = ServiceMethodsMap[TService];\n * type Payload = Methods[TMethod] extends { payload: infer P } ? P : never;\n * type Response = Methods[TMethod] extends { response: infer R } ? R : never;\n * return useService<Payload, Response>(serviceName, methodName, options);\n * }\n * ```\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n */\nexport function useService<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>\n): UseServiceResult<TPayload, TResponse> {\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 }, options?.timeout ?? 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<\n * { title: string },\n * { id: string }\n * >('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 }, options?.timeout ?? 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, options?.timeout]\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/**\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 (via context-based registry)\n * - Automatic reconnection handling\n * - HMR/Fast Refresh safe (no module-level state)\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, subscriptionRegistry } = 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 = React.useMemo(\n () => [serviceName, \"subscription\", entryId],\n [serviceName, entryId]\n );\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 // Try to acquire subscription (with deduplication)\n const { isNew } = subscriptionRegistry.acquire(subscriptionKey);\n\n if (!isNew) {\n // Already subscribed by another component, just track ref count\n setIsSubscribed(true);\n return () => {\n subscriptionRegistry.release(subscriptionKey);\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 // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n };\n\n subscriptionRegistry.setCleanup(subscriptionKey, cleanup);\n\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n subscriptionKey,\n enabled,\n entryId,\n serviceName,\n requiredLevel,\n queryClient,\n queryKey,\n subscriptionRegistry,\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"]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as QuickdrawSocket, P as PrismaDelegate, b as BaseServiceOptions, I as InstallAdminMethodsOptions, A as AdminListPayload, c as AdminListResponse, d as AdminSetACLPayload, e as AdminSubscribersResponse, S as ServiceRegistryInstance, B as BaseServiceInstance, f as QuickdrawServerOptions, Q as QuickdrawServerResult } from '../types-
|
|
1
|
+
import { a as QuickdrawSocket, P as PrismaDelegate, b as BaseServiceOptions, I as InstallAdminMethodsOptions, A as AdminListPayload, c as AdminListResponse, d as AdminSetACLPayload, e as AdminSubscribersResponse, S as ServiceRegistryInstance, B as BaseServiceInstance, f as QuickdrawServerOptions, Q as QuickdrawServerResult } from '../types-D7oHWh4O.js';
|
|
2
2
|
import { Server, Socket } from 'socket.io';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { ACL, Logger, AccessLevel, ServiceMethodContext, ServiceMethodDefinition } from '../shared/index.js';
|
|
4
|
+
import { ACL, Logger, AccessLevel, ServiceMethodContext, ServiceMethodDefinition, AdminServiceMeta, AdminFieldConfig } from '../shared/index.js';
|
|
5
5
|
import 'http';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -54,6 +54,7 @@ declare abstract class BaseService<TEntity extends {
|
|
|
54
54
|
protected delegate: PrismaDelegate<TEntity, TCreateInput, TUpdateInput> | undefined;
|
|
55
55
|
protected io: Server | null;
|
|
56
56
|
private readonly publicMethods;
|
|
57
|
+
private adminMeta;
|
|
57
58
|
constructor(options: BaseServiceOptions);
|
|
58
59
|
/**
|
|
59
60
|
* Set the Prisma delegate for this service.
|
|
@@ -242,10 +243,22 @@ declare abstract class BaseService<TEntity extends {
|
|
|
242
243
|
* reemit: "Admin",
|
|
243
244
|
* unsubscribeAll: "Admin",
|
|
244
245
|
* },
|
|
246
|
+
* schema: createEntitySchema,
|
|
247
|
+
* displayName: "Entities",
|
|
245
248
|
* });
|
|
246
249
|
* ```
|
|
247
250
|
*/
|
|
248
251
|
protected installAdminMethods(options: InstallAdminMethodsOptions): void;
|
|
252
|
+
/**
|
|
253
|
+
* Convert service name to display name.
|
|
254
|
+
* e.g., "chatService" -> "Chats", "userService" -> "Users"
|
|
255
|
+
*/
|
|
256
|
+
private toDisplayName;
|
|
257
|
+
/**
|
|
258
|
+
* Get admin metadata for this service.
|
|
259
|
+
* Returns the configured metadata or throws if not configured.
|
|
260
|
+
*/
|
|
261
|
+
getAdminMeta(): AdminServiceMeta;
|
|
249
262
|
/**
|
|
250
263
|
* Admin method: List entities with pagination and filters.
|
|
251
264
|
*/
|
|
@@ -315,15 +328,28 @@ declare abstract class BaseService<TEntity extends {
|
|
|
315
328
|
* registry.registerService('userService', userService);
|
|
316
329
|
* ```
|
|
317
330
|
*/
|
|
331
|
+
interface ServiceRegistryOptions {
|
|
332
|
+
logger?: Logger;
|
|
333
|
+
/**
|
|
334
|
+
* Automatic method logging configuration.
|
|
335
|
+
* @default { enabled: true, logPayloads: false, logResponses: false }
|
|
336
|
+
*/
|
|
337
|
+
methodLogging?: {
|
|
338
|
+
enabled?: boolean;
|
|
339
|
+
logPayloads?: boolean;
|
|
340
|
+
logResponses?: boolean;
|
|
341
|
+
};
|
|
342
|
+
}
|
|
318
343
|
declare class ServiceRegistry implements ServiceRegistryInstance {
|
|
319
344
|
private readonly io;
|
|
320
345
|
private readonly services;
|
|
321
346
|
private readonly logger;
|
|
347
|
+
private readonly methodLoggingEnabled;
|
|
348
|
+
private readonly logPayloads;
|
|
349
|
+
private readonly logResponses;
|
|
322
350
|
private connectionHandlerInstalled;
|
|
323
351
|
private readonly methodRegistry;
|
|
324
|
-
constructor(io: Server, options?:
|
|
325
|
-
logger?: Logger;
|
|
326
|
-
});
|
|
352
|
+
constructor(io: Server, options?: ServiceRegistryOptions);
|
|
327
353
|
/**
|
|
328
354
|
* Install the single connection handler that wires all registered services.
|
|
329
355
|
* This is called lazily on first service registration.
|
|
@@ -745,4 +771,134 @@ interface GoogleUser {
|
|
|
745
771
|
*/
|
|
746
772
|
declare const googleProvider: OAuthProvider<GoogleUser>;
|
|
747
773
|
|
|
748
|
-
|
|
774
|
+
/**
|
|
775
|
+
* Environment variable validation utilities.
|
|
776
|
+
* Helps ensure required environment variables are set before the application starts.
|
|
777
|
+
*/
|
|
778
|
+
interface ValidateEnvOptions {
|
|
779
|
+
/**
|
|
780
|
+
* List of required environment variable names.
|
|
781
|
+
* Validation will fail if any of these are missing or empty.
|
|
782
|
+
*/
|
|
783
|
+
required: string[];
|
|
784
|
+
/**
|
|
785
|
+
* Only enforce validation in production environment.
|
|
786
|
+
* @default true
|
|
787
|
+
*/
|
|
788
|
+
productionOnly?: boolean;
|
|
789
|
+
/**
|
|
790
|
+
* Custom error message prefix.
|
|
791
|
+
* @default "Missing required environment variables"
|
|
792
|
+
*/
|
|
793
|
+
errorPrefix?: string;
|
|
794
|
+
}
|
|
795
|
+
interface EnvValidationResult {
|
|
796
|
+
success: boolean;
|
|
797
|
+
missing: string[];
|
|
798
|
+
message?: string;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Validate that required environment variables are set.
|
|
802
|
+
* Throws an error if any required variables are missing.
|
|
803
|
+
*
|
|
804
|
+
* @param options - Validation configuration
|
|
805
|
+
* @throws {Error} If required environment variables are missing
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* ```typescript
|
|
809
|
+
* // Validate required vars on startup
|
|
810
|
+
* validateEnv({
|
|
811
|
+
* required: ['JWT_SECRET', 'DATABASE_URL', 'CLIENT_URL'],
|
|
812
|
+
* });
|
|
813
|
+
* ```
|
|
814
|
+
*
|
|
815
|
+
* @example
|
|
816
|
+
* ```typescript
|
|
817
|
+
* // Only validate in production
|
|
818
|
+
* validateEnv({
|
|
819
|
+
* required: ['JWT_SECRET', 'DATABASE_URL'],
|
|
820
|
+
* productionOnly: true,
|
|
821
|
+
* });
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
declare function validateEnv(options: ValidateEnvOptions): void;
|
|
825
|
+
/**
|
|
826
|
+
* Check environment variables without throwing.
|
|
827
|
+
* Returns a result object with validation status and missing variables.
|
|
828
|
+
*
|
|
829
|
+
* @param options - Validation configuration
|
|
830
|
+
* @returns Validation result with missing variables list
|
|
831
|
+
*
|
|
832
|
+
* @example
|
|
833
|
+
* ```typescript
|
|
834
|
+
* const result = checkEnv({ required: ['JWT_SECRET'] });
|
|
835
|
+
* if (!result.success) {
|
|
836
|
+
* console.warn('Missing env vars:', result.missing);
|
|
837
|
+
* }
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
declare function checkEnv(options: ValidateEnvOptions): EnvValidationResult;
|
|
841
|
+
/**
|
|
842
|
+
* Get an environment variable or throw if it's missing.
|
|
843
|
+
* Useful for individual variable access with validation.
|
|
844
|
+
*
|
|
845
|
+
* @param name - Environment variable name
|
|
846
|
+
* @param defaultValue - Optional default value (if provided, won't throw)
|
|
847
|
+
* @returns The environment variable value
|
|
848
|
+
* @throws {Error} If variable is missing and no default provided
|
|
849
|
+
*
|
|
850
|
+
* @example
|
|
851
|
+
* ```typescript
|
|
852
|
+
* const jwtSecret = requireEnv('JWT_SECRET');
|
|
853
|
+
* const port = requireEnv('PORT', '4000');
|
|
854
|
+
* ```
|
|
855
|
+
*/
|
|
856
|
+
declare function requireEnv(name: string, defaultValue?: string): string;
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Options for customizing field generation.
|
|
860
|
+
*/
|
|
861
|
+
interface ZodToAdminFieldsOptions {
|
|
862
|
+
/** Fields to exclude from the result */
|
|
863
|
+
hiddenFields?: string[];
|
|
864
|
+
/** Override which fields appear in table */
|
|
865
|
+
tableColumns?: string[];
|
|
866
|
+
/** Override specific field configurations */
|
|
867
|
+
fieldOverrides?: Partial<Record<string, Partial<AdminFieldConfig>>>;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Convert a Zod object schema to AdminFieldConfig array.
|
|
871
|
+
*
|
|
872
|
+
* @param schema - A Zod object schema (z.object({ ... }))
|
|
873
|
+
* @param options - Optional customization
|
|
874
|
+
* @returns Array of AdminFieldConfig for each field
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* ```typescript
|
|
878
|
+
* const userSchema = z.object({
|
|
879
|
+
* id: z.string(),
|
|
880
|
+
* name: z.string().optional(),
|
|
881
|
+
* email: z.string().email(),
|
|
882
|
+
* role: z.enum(["user", "admin"]),
|
|
883
|
+
* createdAt: z.date(),
|
|
884
|
+
* });
|
|
885
|
+
*
|
|
886
|
+
* const fields = zodToAdminFields(userSchema);
|
|
887
|
+
* // Returns field configs with appropriate types, labels, editability
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
declare function zodToAdminFields(schema: z.ZodTypeAny, options?: ZodToAdminFieldsOptions): AdminFieldConfig[];
|
|
891
|
+
/**
|
|
892
|
+
* Generate AdminFieldConfig for common entity fields that aren't in the create schema.
|
|
893
|
+
* These are typically auto-generated fields like id, createdAt, updatedAt.
|
|
894
|
+
*/
|
|
895
|
+
declare function getDefaultEntityFields(): AdminFieldConfig[];
|
|
896
|
+
/**
|
|
897
|
+
* Merge schema-derived fields with default entity fields.
|
|
898
|
+
* Default fields come first (id, createdAt, updatedAt), then schema fields.
|
|
899
|
+
*/
|
|
900
|
+
declare function mergeWithDefaultFields(schemaFields: AdminFieldConfig[], options?: {
|
|
901
|
+
includeDefaults?: string[];
|
|
902
|
+
}): AdminFieldConfig[];
|
|
903
|
+
|
|
904
|
+
export { AdminListPayload, AdminListResponse, AdminSetACLPayload, AdminSubscribersResponse, BaseService, BaseServiceInstance, BaseServiceOptions, type DiscordUser, type EnvValidationResult, type GoogleUser, InstallAdminMethodsOptions, type JWTPayload, type OAuthConfig, type OAuthProvider, type OAuthTokenResponse, PrismaDelegate, QuickdrawServerOptions, QuickdrawServerResult, QuickdrawSocket, type RateLimitOptions, type RateLimiter, type RedisAdapterOptions, type RedisAdapterResult, ServiceRegistry, ServiceRegistryInstance, type ServiceRegistryOptions, type ValidateEnvOptions, type ZodToAdminFieldsOptions, applyRateLimitMiddleware, checkEnv, createJWT, createOAuthURL, createQuickdrawServer, createRateLimiter, createTieredRateLimiter, discordProvider, exchangeOAuthCode, getDefaultEntityFields, googleProvider, isRedisAdapterAvailable, mergeWithDefaultFields, requireEnv, setupRedisAdapter, validateEnv, verifyJWT, zodToAdminFields };
|