@luckystack/sync 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +104 -0
- package/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/client.d.ts +126 -0
- package/dist/client.js +537 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +1203 -0
- package/dist/index.js.map +1 -0
- package/docs/callback-registration.md +257 -0
- package/docs/error-states.md +252 -0
- package/docs/ignore-self.md +162 -0
- package/docs/room-fanout.md +233 -0
- package/docs/server-vs-client-handlers.md +321 -0
- package/docs/streaming.md +349 -0
- package/docs/sync-request.md +284 -0
- package/docs/version-policy.md +362 -0
- package/package.json +75 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
// src/syncRequest.ts
|
|
2
|
+
import {
|
|
3
|
+
getLogger,
|
|
4
|
+
getProjectConfig,
|
|
5
|
+
notify,
|
|
6
|
+
incrementResponseIndex,
|
|
7
|
+
socket,
|
|
8
|
+
waitForSocket,
|
|
9
|
+
enqueueSyncRequest,
|
|
10
|
+
isOnline,
|
|
11
|
+
normalizeErrorResponseCore,
|
|
12
|
+
parseServiceRouteName,
|
|
13
|
+
buildSyncProgressEventName,
|
|
14
|
+
buildSyncResponseEventName,
|
|
15
|
+
socketEventNames
|
|
16
|
+
} from "@luckystack/core/client";
|
|
17
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
18
|
+
var shouldLogDev = () => getProjectConfig().logging.devLogs;
|
|
19
|
+
var shouldNotifyDev = () => getProjectConfig().logging.devNotifications;
|
|
20
|
+
var shouldLogSocketStatus = () => getProjectConfig().logging.socketStatus;
|
|
21
|
+
var shouldLogStream = () => getProjectConfig().logging.stream;
|
|
22
|
+
var syncEvents = {};
|
|
23
|
+
var syncStreamEvents = {};
|
|
24
|
+
var noop = () => null;
|
|
25
|
+
var activeLifecycleHandlers = null;
|
|
26
|
+
var canSendNow = (socketInstance) => {
|
|
27
|
+
if (!socketInstance.connected) return false;
|
|
28
|
+
return isOnline();
|
|
29
|
+
};
|
|
30
|
+
var createQueueId = () => {
|
|
31
|
+
return `${String(Date.now())}-${String(Math.random())}`;
|
|
32
|
+
};
|
|
33
|
+
var getCallbacksForRoute = (route) => {
|
|
34
|
+
syncEvents[route] ??= [];
|
|
35
|
+
return syncEvents[route];
|
|
36
|
+
};
|
|
37
|
+
var getStreamCallbacksForRoute = (route) => {
|
|
38
|
+
syncStreamEvents[route] ??= [];
|
|
39
|
+
return syncStreamEvents[route];
|
|
40
|
+
};
|
|
41
|
+
var triggerSyncCallbacks = (name, clientOutput, serverOutput) => {
|
|
42
|
+
const callbacks = syncEvents[name] ?? [];
|
|
43
|
+
if (callbacks.length === 0) {
|
|
44
|
+
if (shouldLogDev()) {
|
|
45
|
+
getLogger().warn(`Sync event ${name} has no registered callback on this page`);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const callback of callbacks) {
|
|
50
|
+
callback({ clientOutput, serverOutput });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var triggerSyncStreamCallbacks = (name, stream) => {
|
|
54
|
+
const callbacks = syncStreamEvents[name] ?? [];
|
|
55
|
+
if (callbacks.length === 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
for (const callback of callbacks) {
|
|
59
|
+
callback({ stream });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var normalizeSyncError = ({
|
|
63
|
+
response,
|
|
64
|
+
fallbackErrorCode
|
|
65
|
+
}) => {
|
|
66
|
+
const normalized = normalizeErrorResponseCore({
|
|
67
|
+
response,
|
|
68
|
+
fallbackErrorCode,
|
|
69
|
+
resolveMessage: ({ errorCode }) => {
|
|
70
|
+
if (typeof response.message === "string" && response.message.trim().length > 0) {
|
|
71
|
+
return response.message;
|
|
72
|
+
}
|
|
73
|
+
return errorCode;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
status: "error",
|
|
78
|
+
message: normalized.message,
|
|
79
|
+
errorCode: normalized.errorCode,
|
|
80
|
+
errorParams: normalized.errorParams,
|
|
81
|
+
httpStatus: normalized.httpStatus
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var syncRequestInternal = (params) => {
|
|
85
|
+
const runtimeParams = params;
|
|
86
|
+
const { name, version, receiver, ignoreSelf, onStream, offlineDropPolicy, signal: externalSignal } = runtimeParams;
|
|
87
|
+
const payloadData = runtimeParams.data;
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
void (async () => {
|
|
90
|
+
if (externalSignal?.aborted) {
|
|
91
|
+
resolve(normalizeSyncError({
|
|
92
|
+
response: { status: "error", errorCode: "request.aborted" },
|
|
93
|
+
fallbackErrorCode: "request.aborted"
|
|
94
|
+
}));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!name || typeof name !== "string") {
|
|
98
|
+
if (shouldLogDev()) {
|
|
99
|
+
getLogger().error("Invalid name for syncRequest");
|
|
100
|
+
}
|
|
101
|
+
if (shouldNotifyDev()) {
|
|
102
|
+
notify.error({ key: "sync.invalidName" });
|
|
103
|
+
}
|
|
104
|
+
resolve(normalizeSyncError({
|
|
105
|
+
response: { status: "error", errorCode: "sync.invalidName" },
|
|
106
|
+
fallbackErrorCode: "sync.invalidName"
|
|
107
|
+
}));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const parsedRoute = parseServiceRouteName(name);
|
|
111
|
+
if (parsedRoute.status === "error") {
|
|
112
|
+
if (shouldLogDev()) {
|
|
113
|
+
getLogger().error(`[syncRequest] Invalid service route name '${name}'`, void 0, { reason: parsedRoute.reason });
|
|
114
|
+
}
|
|
115
|
+
if (shouldNotifyDev()) {
|
|
116
|
+
notify.error({ key: "routing.invalidServiceRouteName" });
|
|
117
|
+
}
|
|
118
|
+
resolve(normalizeSyncError({
|
|
119
|
+
response: {
|
|
120
|
+
status: "error",
|
|
121
|
+
errorCode: "routing.invalidServiceRouteName",
|
|
122
|
+
errorParams: [{ key: "name", value: name }]
|
|
123
|
+
},
|
|
124
|
+
fallbackErrorCode: "routing.invalidServiceRouteName"
|
|
125
|
+
}));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const sanitizedName = parsedRoute.normalizedRouteName;
|
|
129
|
+
const data = payloadData && typeof payloadData === "object" ? payloadData : {};
|
|
130
|
+
if (!version || typeof version !== "string") {
|
|
131
|
+
if (shouldLogDev()) {
|
|
132
|
+
getLogger().error("Invalid version for syncRequest");
|
|
133
|
+
}
|
|
134
|
+
if (shouldNotifyDev()) {
|
|
135
|
+
notify.error({ key: "sync.invalidVersion" });
|
|
136
|
+
}
|
|
137
|
+
resolve(normalizeSyncError({
|
|
138
|
+
response: { status: "error", errorCode: "sync.invalidVersion" },
|
|
139
|
+
fallbackErrorCode: "sync.invalidVersion"
|
|
140
|
+
}));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const normalizedReceiver = typeof receiver === "string" ? receiver.trim() : "";
|
|
144
|
+
if (!normalizedReceiver) {
|
|
145
|
+
if (shouldLogDev()) {
|
|
146
|
+
getLogger().error("You need to provide a receiver for syncRequest, this can be either 'all' to trigger all sockets which we do not recommend or it can be any value such as a code e.g 'Ag2cg4'. this works together with the joinRoom and leaveRoom function");
|
|
147
|
+
}
|
|
148
|
+
if (shouldNotifyDev()) {
|
|
149
|
+
notify.error({ key: "sync.missingReceiver" });
|
|
150
|
+
}
|
|
151
|
+
resolve(normalizeSyncError({
|
|
152
|
+
response: { status: "error", errorCode: "sync.missingReceiver" },
|
|
153
|
+
fallbackErrorCode: "sync.missingReceiver"
|
|
154
|
+
}));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!await waitForSocket()) {
|
|
158
|
+
resolve(normalizeSyncError({
|
|
159
|
+
response: { status: "error", errorCode: "sync.ioUnavailable" },
|
|
160
|
+
fallbackErrorCode: "sync.ioUnavailable"
|
|
161
|
+
}));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!socket) {
|
|
165
|
+
resolve(normalizeSyncError({
|
|
166
|
+
response: { status: "error", errorCode: "sync.ioUnavailable" },
|
|
167
|
+
fallbackErrorCode: "sync.ioUnavailable"
|
|
168
|
+
}));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const fullName = `sync/${sanitizedName}/${version}`;
|
|
172
|
+
let queueId = null;
|
|
173
|
+
const runRequest = (socketInstance) => {
|
|
174
|
+
if (!canSendNow(socketInstance)) {
|
|
175
|
+
queueId ??= createQueueId();
|
|
176
|
+
const enqueued = enqueueSyncRequest({
|
|
177
|
+
id: queueId,
|
|
178
|
+
key: fullName,
|
|
179
|
+
run: (s) => {
|
|
180
|
+
runRequest(s);
|
|
181
|
+
},
|
|
182
|
+
createdAt: Date.now(),
|
|
183
|
+
dropPolicy: offlineDropPolicy
|
|
184
|
+
});
|
|
185
|
+
if (!enqueued) {
|
|
186
|
+
resolve(normalizeSyncError({
|
|
187
|
+
response: { status: "error", errorCode: "offline.queueFull" },
|
|
188
|
+
fallbackErrorCode: "offline.queueFull"
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const tempIndex = incrementResponseIndex();
|
|
194
|
+
let cleanupProgressListener = null;
|
|
195
|
+
if (shouldLogDev()) {
|
|
196
|
+
getLogger().debug(`Client Sync Request(${String(tempIndex)})`, { syncName: sanitizedName, data, receiver: normalizedReceiver, ignoreSelf });
|
|
197
|
+
}
|
|
198
|
+
if (typeof onStream === "function") {
|
|
199
|
+
const progressEventName = buildSyncProgressEventName(tempIndex);
|
|
200
|
+
const progressListener = (streamPayload) => {
|
|
201
|
+
if (shouldLogStream()) {
|
|
202
|
+
getLogger().debug(`Server Sync Stream(${String(tempIndex)})`, { syncName: sanitizedName, streamPayload });
|
|
203
|
+
}
|
|
204
|
+
onStream(streamPayload);
|
|
205
|
+
};
|
|
206
|
+
socketInstance.on(progressEventName, progressListener);
|
|
207
|
+
cleanupProgressListener = () => {
|
|
208
|
+
socketInstance.off(progressEventName, progressListener);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const syncCb = `${sanitizedName}/${version}`;
|
|
212
|
+
socketInstance.emit(socketEventNames.sync, { name: fullName, data, cb: syncCb, receiver: normalizedReceiver, responseIndex: tempIndex, ignoreSelf });
|
|
213
|
+
let cleanupExternalAbort = null;
|
|
214
|
+
if (externalSignal) {
|
|
215
|
+
const externalAbortHandler = () => {
|
|
216
|
+
socketInstance.emit(socketEventNames.syncCancel, { cb: syncCb });
|
|
217
|
+
cleanupProgressListener?.();
|
|
218
|
+
cleanupProgressListener = null;
|
|
219
|
+
cleanupExternalAbort?.();
|
|
220
|
+
cleanupExternalAbort = null;
|
|
221
|
+
resolve(normalizeSyncError({
|
|
222
|
+
response: { status: "error", errorCode: "request.aborted" },
|
|
223
|
+
fallbackErrorCode: "request.aborted"
|
|
224
|
+
}));
|
|
225
|
+
};
|
|
226
|
+
externalSignal.addEventListener("abort", externalAbortHandler);
|
|
227
|
+
cleanupExternalAbort = () => {
|
|
228
|
+
externalSignal.removeEventListener("abort", externalAbortHandler);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
socketInstance.once(buildSyncResponseEventName(tempIndex), (responseData) => {
|
|
232
|
+
cleanupProgressListener?.();
|
|
233
|
+
cleanupExternalAbort?.();
|
|
234
|
+
cleanupExternalAbort = null;
|
|
235
|
+
if (responseData.status === "error") {
|
|
236
|
+
const normalizedError = normalizeSyncError({
|
|
237
|
+
response: responseData,
|
|
238
|
+
fallbackErrorCode: "sync.failedRequest"
|
|
239
|
+
});
|
|
240
|
+
if (shouldLogDev()) {
|
|
241
|
+
getLogger().error(`Sync ${sanitizedName} failed`, void 0, { message: normalizedError.message });
|
|
242
|
+
}
|
|
243
|
+
if (shouldNotifyDev()) {
|
|
244
|
+
notify.error({
|
|
245
|
+
key: "sync.failedRequest",
|
|
246
|
+
params: [
|
|
247
|
+
{ key: "name", value: sanitizedName },
|
|
248
|
+
{ key: "message", value: normalizedError.message }
|
|
249
|
+
]
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
resolve(normalizedError);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (responseData.status !== "success") {
|
|
256
|
+
resolve(normalizeSyncError({
|
|
257
|
+
response: responseData,
|
|
258
|
+
fallbackErrorCode: "sync.invalidServerResponse"
|
|
259
|
+
}));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const result = responseData.result && typeof responseData.result === "object" ? responseData.result : {};
|
|
263
|
+
resolve({
|
|
264
|
+
status: "success",
|
|
265
|
+
message: typeof responseData.message === "string" && responseData.message.trim().length > 0 ? responseData.message : `sync ${sanitizedName} success`,
|
|
266
|
+
result
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
runRequest(socket);
|
|
271
|
+
})();
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
function syncRequest(params) {
|
|
275
|
+
return syncRequestInternal(params);
|
|
276
|
+
}
|
|
277
|
+
var useSyncEvents = () => {
|
|
278
|
+
const localRegistryRef = useRef(/* @__PURE__ */ new Map());
|
|
279
|
+
const localStreamRegistryRef = useRef(/* @__PURE__ */ new Map());
|
|
280
|
+
const upsertSyncEventCallback = useCallback((params) => {
|
|
281
|
+
if (typeof params.version !== "string") {
|
|
282
|
+
if (shouldLogDev()) {
|
|
283
|
+
getLogger().error("Invalid version for upsertSyncEventCallback");
|
|
284
|
+
}
|
|
285
|
+
if (shouldNotifyDev()) {
|
|
286
|
+
notify.error({ key: "sync.invalidVersion" });
|
|
287
|
+
}
|
|
288
|
+
return noop;
|
|
289
|
+
}
|
|
290
|
+
if (typeof params.callback !== "function") {
|
|
291
|
+
if (shouldLogDev()) {
|
|
292
|
+
getLogger().error("Invalid callback for upsertSyncEventCallback");
|
|
293
|
+
}
|
|
294
|
+
if (shouldNotifyDev()) {
|
|
295
|
+
notify.error({ key: "sync.invalidCallback" });
|
|
296
|
+
}
|
|
297
|
+
return noop;
|
|
298
|
+
}
|
|
299
|
+
const routeName = params.name;
|
|
300
|
+
const parsedRoute = parseServiceRouteName(routeName);
|
|
301
|
+
if (parsedRoute.status === "error") {
|
|
302
|
+
if (shouldLogDev()) {
|
|
303
|
+
getLogger().error(`Invalid name for upsertSyncEventCallback`, void 0, { routeName, reason: parsedRoute.reason });
|
|
304
|
+
}
|
|
305
|
+
if (shouldNotifyDev()) {
|
|
306
|
+
notify.error({ key: "routing.invalidServiceRouteName" });
|
|
307
|
+
}
|
|
308
|
+
return noop;
|
|
309
|
+
}
|
|
310
|
+
const sanitizedName = parsedRoute.normalizedRouteName;
|
|
311
|
+
const routeVersion = params.version;
|
|
312
|
+
const fullName = `sync/${sanitizedName}/${routeVersion}`;
|
|
313
|
+
const callback = ({ clientOutput, serverOutput }) => {
|
|
314
|
+
params.callback({
|
|
315
|
+
clientOutput,
|
|
316
|
+
serverOutput
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
const callbacks = getCallbacksForRoute(fullName);
|
|
320
|
+
const previousForRoute = localRegistryRef.current.get(fullName);
|
|
321
|
+
if (previousForRoute) {
|
|
322
|
+
syncEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);
|
|
323
|
+
}
|
|
324
|
+
const nextCallbacks = getCallbacksForRoute(fullName);
|
|
325
|
+
if (nextCallbacks.includes(callback)) {
|
|
326
|
+
if (shouldLogDev()) {
|
|
327
|
+
getLogger().warn(`[SyncEvents] Duplicate callback registration was ignored`, { fullName });
|
|
328
|
+
}
|
|
329
|
+
localRegistryRef.current.set(fullName, callback);
|
|
330
|
+
return () => {
|
|
331
|
+
const current = getCallbacksForRoute(fullName);
|
|
332
|
+
syncEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
333
|
+
if (localRegistryRef.current.get(fullName) === callback) {
|
|
334
|
+
localRegistryRef.current.delete(fullName);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
nextCallbacks.push(callback);
|
|
339
|
+
syncEvents[fullName] = nextCallbacks;
|
|
340
|
+
localRegistryRef.current.set(fullName, callback);
|
|
341
|
+
return () => {
|
|
342
|
+
const current = getCallbacksForRoute(fullName);
|
|
343
|
+
syncEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
344
|
+
if (localRegistryRef.current.get(fullName) === callback) {
|
|
345
|
+
localRegistryRef.current.delete(fullName);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}, []);
|
|
349
|
+
const upsertSyncEventStreamCallback = useCallback((params) => {
|
|
350
|
+
if (typeof params.version !== "string") {
|
|
351
|
+
if (shouldLogDev()) {
|
|
352
|
+
getLogger().error("Invalid version for upsertSyncEventStreamCallback");
|
|
353
|
+
}
|
|
354
|
+
if (shouldNotifyDev()) {
|
|
355
|
+
notify.error({ key: "sync.invalidVersion" });
|
|
356
|
+
}
|
|
357
|
+
return noop;
|
|
358
|
+
}
|
|
359
|
+
if (typeof params.callback !== "function") {
|
|
360
|
+
if (shouldLogDev()) {
|
|
361
|
+
getLogger().error("Invalid callback for upsertSyncEventStreamCallback");
|
|
362
|
+
}
|
|
363
|
+
if (shouldNotifyDev()) {
|
|
364
|
+
notify.error({ key: "sync.invalidCallback" });
|
|
365
|
+
}
|
|
366
|
+
return noop;
|
|
367
|
+
}
|
|
368
|
+
const routeName = params.name;
|
|
369
|
+
const parsedRoute = parseServiceRouteName(routeName);
|
|
370
|
+
if (parsedRoute.status === "error") {
|
|
371
|
+
if (shouldLogDev()) {
|
|
372
|
+
getLogger().error(`Invalid name for upsertSyncEventStreamCallback`, void 0, { routeName, reason: parsedRoute.reason });
|
|
373
|
+
}
|
|
374
|
+
if (shouldNotifyDev()) {
|
|
375
|
+
notify.error({ key: "routing.invalidServiceRouteName" });
|
|
376
|
+
}
|
|
377
|
+
return noop;
|
|
378
|
+
}
|
|
379
|
+
const sanitizedName = parsedRoute.normalizedRouteName;
|
|
380
|
+
const routeVersion = params.version;
|
|
381
|
+
const fullName = `sync/${sanitizedName}/${routeVersion}`;
|
|
382
|
+
const typedCallback = params.callback;
|
|
383
|
+
const callback = ({ stream }) => {
|
|
384
|
+
typedCallback({
|
|
385
|
+
stream
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
const callbacks = getStreamCallbacksForRoute(fullName);
|
|
389
|
+
const previousForRoute = localStreamRegistryRef.current.get(fullName);
|
|
390
|
+
if (previousForRoute) {
|
|
391
|
+
syncStreamEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);
|
|
392
|
+
}
|
|
393
|
+
const nextCallbacks = getStreamCallbacksForRoute(fullName);
|
|
394
|
+
if (nextCallbacks.includes(callback)) {
|
|
395
|
+
if (shouldLogDev()) {
|
|
396
|
+
getLogger().warn(`[SyncEvents] Duplicate stream callback registration was ignored`, { fullName });
|
|
397
|
+
}
|
|
398
|
+
localStreamRegistryRef.current.set(fullName, callback);
|
|
399
|
+
return () => {
|
|
400
|
+
const current = getStreamCallbacksForRoute(fullName);
|
|
401
|
+
syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
402
|
+
if (localStreamRegistryRef.current.get(fullName) === callback) {
|
|
403
|
+
localStreamRegistryRef.current.delete(fullName);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
nextCallbacks.push(callback);
|
|
408
|
+
syncStreamEvents[fullName] = nextCallbacks;
|
|
409
|
+
localStreamRegistryRef.current.set(fullName, callback);
|
|
410
|
+
return () => {
|
|
411
|
+
const current = getStreamCallbacksForRoute(fullName);
|
|
412
|
+
syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
413
|
+
if (localStreamRegistryRef.current.get(fullName) === callback) {
|
|
414
|
+
localStreamRegistryRef.current.delete(fullName);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}, []);
|
|
418
|
+
useEffect(() => {
|
|
419
|
+
const localRegistry = localRegistryRef.current;
|
|
420
|
+
const localStreamRegistry = localStreamRegistryRef.current;
|
|
421
|
+
return () => {
|
|
422
|
+
for (const [fullName, callback] of localRegistry.entries()) {
|
|
423
|
+
const current = getCallbacksForRoute(fullName);
|
|
424
|
+
syncEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
425
|
+
}
|
|
426
|
+
for (const [fullName, callback] of localStreamRegistry.entries()) {
|
|
427
|
+
const current = getStreamCallbacksForRoute(fullName);
|
|
428
|
+
syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);
|
|
429
|
+
}
|
|
430
|
+
localRegistry.clear();
|
|
431
|
+
localStreamRegistry.clear();
|
|
432
|
+
};
|
|
433
|
+
}, []);
|
|
434
|
+
return { upsertSyncEventCallback, upsertSyncEventStreamCallback };
|
|
435
|
+
};
|
|
436
|
+
var useSyncEventTrigger = () => {
|
|
437
|
+
const triggerSyncEvent = useCallback((name, clientOutput = {}, serverOutput = {}) => {
|
|
438
|
+
triggerSyncCallbacks(name, clientOutput, serverOutput);
|
|
439
|
+
}, []);
|
|
440
|
+
const triggerSyncStreamEvent = useCallback((name, stream) => {
|
|
441
|
+
triggerSyncStreamCallbacks(name, stream);
|
|
442
|
+
}, []);
|
|
443
|
+
return { triggerSyncEvent, triggerSyncStreamEvent };
|
|
444
|
+
};
|
|
445
|
+
var buildConnectHandler = ({ setSocketStatus }) => () => {
|
|
446
|
+
if (shouldLogSocketStatus()) getLogger().info("Connected to server");
|
|
447
|
+
setSocketStatus((prev) => ({ ...prev, self: { ...prev.self, status: "CONNECTED" } }));
|
|
448
|
+
};
|
|
449
|
+
var buildDisconnectHandler = ({ setSocketStatus }) => () => {
|
|
450
|
+
setSocketStatus((prev) => ({ ...prev, self: { ...prev.self, status: "DISCONNECTED" } }));
|
|
451
|
+
if (shouldLogSocketStatus()) getLogger().info("Disconnected, trying to reconnect...");
|
|
452
|
+
};
|
|
453
|
+
var buildReconnectAttemptHandler = ({ setSocketStatus }) => (attempt) => {
|
|
454
|
+
setSocketStatus((prev) => ({ ...prev, self: { ...prev.self, status: "RECONNECTING", reconnectAttempt: attempt } }));
|
|
455
|
+
if (shouldLogSocketStatus()) getLogger().info(`Reconnecting attempt ${String(attempt)}...`);
|
|
456
|
+
};
|
|
457
|
+
var buildUserAfkHandler = ({
|
|
458
|
+
setSocketStatus,
|
|
459
|
+
sessionRef
|
|
460
|
+
}) => ({ userId, endTime }) => {
|
|
461
|
+
if (sessionRef.current !== null && userId === sessionRef.current.id) {
|
|
462
|
+
setSocketStatus((prev) => ({
|
|
463
|
+
...prev,
|
|
464
|
+
self: { status: "DISCONNECTED", reconnectAttempt: void 0, endTime }
|
|
465
|
+
}));
|
|
466
|
+
} else {
|
|
467
|
+
setSocketStatus((prev) => ({
|
|
468
|
+
...prev,
|
|
469
|
+
[userId]: { status: "DISCONNECTED", endTime }
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var buildUserBackHandler = ({ setSocketStatus }) => ({ userId }) => {
|
|
474
|
+
if (shouldLogSocketStatus()) getLogger().debug("userBack", { userId });
|
|
475
|
+
setSocketStatus((prev) => ({
|
|
476
|
+
...prev,
|
|
477
|
+
[userId]: { status: "CONNECTED", endTime: void 0 }
|
|
478
|
+
}));
|
|
479
|
+
};
|
|
480
|
+
var buildConnectErrorHandler = ({ setSocketStatus }) => (err) => {
|
|
481
|
+
if (shouldLogSocketStatus()) getLogger().debug("connect_error", { err });
|
|
482
|
+
setSocketStatus((prev) => ({
|
|
483
|
+
...prev,
|
|
484
|
+
self: { ...prev.self, status: "DISCONNECTED", reconnectAttempt: void 0 }
|
|
485
|
+
}));
|
|
486
|
+
if (shouldLogDev()) getLogger().error(`Connection error`, err);
|
|
487
|
+
if (shouldNotifyDev()) notify.error({ key: "common.connectionError" });
|
|
488
|
+
};
|
|
489
|
+
var initSyncRequest = async ({
|
|
490
|
+
setSocketStatus,
|
|
491
|
+
sessionRef
|
|
492
|
+
}) => {
|
|
493
|
+
if (!await waitForSocket()) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (!socket) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (!sessionRef) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (activeLifecycleHandlers) {
|
|
503
|
+
socket.off(socketEventNames.connect, activeLifecycleHandlers.connect);
|
|
504
|
+
socket.off(socketEventNames.disconnect, activeLifecycleHandlers.disconnect);
|
|
505
|
+
socket.off(socketEventNames.reconnectAttempt, activeLifecycleHandlers.reconnectAttempt);
|
|
506
|
+
socket.off(socketEventNames.userAfk, activeLifecycleHandlers.userAfk);
|
|
507
|
+
socket.off(socketEventNames.userBack, activeLifecycleHandlers.userBack);
|
|
508
|
+
socket.off(socketEventNames.connectError, activeLifecycleHandlers.connectError);
|
|
509
|
+
}
|
|
510
|
+
const connect = buildConnectHandler({ setSocketStatus });
|
|
511
|
+
const disconnect = buildDisconnectHandler({ setSocketStatus });
|
|
512
|
+
const reconnectAttempt = buildReconnectAttemptHandler({ setSocketStatus });
|
|
513
|
+
const userAfk = buildUserAfkHandler({ setSocketStatus, sessionRef });
|
|
514
|
+
const userBack = buildUserBackHandler({ setSocketStatus });
|
|
515
|
+
const connectError = buildConnectErrorHandler({ setSocketStatus });
|
|
516
|
+
activeLifecycleHandlers = {
|
|
517
|
+
connect,
|
|
518
|
+
disconnect,
|
|
519
|
+
reconnectAttempt,
|
|
520
|
+
userAfk,
|
|
521
|
+
userBack,
|
|
522
|
+
connectError
|
|
523
|
+
};
|
|
524
|
+
socket.on(socketEventNames.connect, connect);
|
|
525
|
+
socket.on(socketEventNames.disconnect, disconnect);
|
|
526
|
+
socket.on(socketEventNames.reconnectAttempt, reconnectAttempt);
|
|
527
|
+
socket.on(socketEventNames.userAfk, userAfk);
|
|
528
|
+
socket.on(socketEventNames.userBack, userBack);
|
|
529
|
+
socket.on(socketEventNames.connectError, connectError);
|
|
530
|
+
};
|
|
531
|
+
export {
|
|
532
|
+
initSyncRequest,
|
|
533
|
+
syncRequest,
|
|
534
|
+
useSyncEventTrigger,
|
|
535
|
+
useSyncEvents
|
|
536
|
+
};
|
|
537
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/syncRequest.ts"],"sourcesContent":["//? All cross-package imports go through `@luckystack/core/client` (the\r\n//? browser-safe subpath) — never the main `@luckystack/core` barrel which\r\n//? re-exports server-only modules (paths.ts, db.ts, redis.ts) that must\r\n//? not enter a Vite client bundle.\r\nimport type {\r\n BaseSessionLayout as SessionLayout,\r\n StreamPayload,\r\n SyncTypeMap,\r\n statusContent,\r\n} from \"@luckystack/core/client\";\r\nimport {\r\n getLogger,\r\n getProjectConfig,\r\n notify,\r\n incrementResponseIndex,\r\n socket,\r\n waitForSocket,\r\n enqueueSyncRequest,\r\n isOnline,\r\n normalizeErrorResponseCore,\r\n parseServiceRouteName,\r\n buildSyncProgressEventName,\r\n buildSyncResponseEventName,\r\n socketEventNames,\r\n} from \"@luckystack/core/client\";\r\nimport { Dispatch, RefObject, SetStateAction, useCallback, useEffect, useRef } from \"react\";\r\nimport { Socket } from \"socket.io-client\";\r\n\r\nexport type SyncRequestStreamEvent<T extends StreamPayload = StreamPayload> = T;\r\n\r\nexport type SyncRouteStreamEvent<T extends StreamPayload = StreamPayload> = T;\r\n\r\ntype SyncRequestStreamCallback = (event: SyncRequestStreamEvent) => void;\r\ntype SyncEventStreamCallback = (params: { stream: SyncRouteStreamEvent }) => void;\r\n\r\nconst shouldLogDev = () => getProjectConfig().logging.devLogs;\r\nconst shouldNotifyDev = () => getProjectConfig().logging.devNotifications;\r\nconst shouldLogSocketStatus = () => getProjectConfig().logging.socketStatus;\r\nconst shouldLogStream = () => getProjectConfig().logging.stream;\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n// Type Helpers for Sync Requests\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n\r\n// Check if data input is required (i.e., T does NOT allow empty object)\r\n// Unions like {a:1} | {b:1} do NOT allow {}, so data will be required\r\ntype DataRequired<T> = Record<string, never> extends T ? false : true;\r\n\r\ntype UnionToIntersection<U> =\r\n (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void)\r\n ? I\r\n : never;\r\n\r\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n// Global Sync Params\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n\r\n// All possible sync names across all pages\r\ntype SyncRouteRecord = UnionToIntersection<{\r\n [P in keyof SyncTypeMap]: {\r\n [N in keyof SyncTypeMap[P] as P extends 'root'\r\n ? `system/${Extract<N, string>}`\r\n : `${Extract<P, string>}/${Extract<N, string>}`]: SyncTypeMap[P][N]\r\n }\r\n}[keyof SyncTypeMap]>;\r\n\r\ntype SyncFullName = Extract<keyof SyncRouteRecord, string>;\r\ntype VersionsForFullName<F extends SyncFullName> = Extract<keyof SyncRouteRecord[F], string>;\r\n\r\ntype ClientInputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientInput: infer I }\r\n ? I\r\n : never;\r\n\r\ntype ServerOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverOutput: infer O }\r\n ? O\r\n : never;\r\n\r\ntype ClientOutputForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientOutput: infer O }\r\n ? O\r\n : never;\r\n\r\ntype ServerStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { serverStream: infer O }\r\n ? O\r\n : never;\r\n\r\ntype ClientStreamForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> = SyncRouteRecord[F][V] extends { clientStream: infer O }\r\n ? O\r\n : never;\r\n\r\ntype SyncRequestStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n [ServerStreamForFullName<F, V>] extends [never]\r\n ? never\r\n : (event: SyncRequestStreamEvent<Prettify<ServerStreamForFullName<F, V> extends StreamPayload ? ServerStreamForFullName<F, V> : StreamPayload>>) => void;\r\n\r\n//? Recipients see chunks from THREE sources, all on the same wire channel:\r\n//? 1. `_client_v{n}.ts`'s `stream(...)` (per-recipient, runs after _server)\r\n//? 2. `_server_v{n}.ts`'s `broadcastStream(...)` (room-wide fan-out)\r\n//? 3. `_server_v{n}.ts`'s `streamTo(tokens, ...)` (selective fan-out)\r\n//? All three flow into `upsertSyncEventCallback`'s `stream` argument. Both\r\n//? serverStream and clientStream are folded into the union so the callback\r\n//? sees every shape the route can emit. If neither side ever streams, the\r\n//? callback type collapses to never (compile error to register one).\r\ntype CombinedRouteStream<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n | (ClientStreamForFullName<F, V> extends never ? never : ClientStreamForFullName<F, V>)\r\n | (ServerStreamForFullName<F, V> extends never ? never : ServerStreamForFullName<F, V>);\r\n\r\ntype SyncRouteStreamCallbackForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n [CombinedRouteStream<F, V>] extends [never]\r\n ? never\r\n : (params: { stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>> }) => void;\r\n\r\ntype SyncParamsForFullName<\r\n F extends SyncFullName,\r\n V extends VersionsForFullName<F>\r\n> = DataRequired<ClientInputForFullName<F, V>> extends true\r\n ? {\r\n name: F;\r\n version: V;\r\n data: ClientInputForFullName<F, V>;\r\n receiver: string;\r\n ignoreSelf?: boolean;\r\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\r\n /**\r\n * Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a\r\n * specific sync (\"editor cursor move\") pick `'drop-oldest'` while the\r\n * app default stays `'reject'` for safer sends. When omitted, falls back\r\n * to the global config.\r\n */\r\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\r\n /**\r\n * Optional AbortSignal. When aborted the client emits `syncCancel { cb }`\r\n * to the server and resolves locally with\r\n * `{ status: 'error', errorCode: 'request.aborted' }`.\r\n */\r\n signal?: AbortSignal;\r\n }\r\n : {\r\n name: F;\r\n version: V;\r\n data?: ClientInputForFullName<F, V>;\r\n receiver: string;\r\n ignoreSelf?: boolean;\r\n onStream?: SyncRequestStreamCallbackForFullName<F, V>;\r\n /** Per-request override (see typed branch). */\r\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\r\n /** Optional AbortSignal — see typed branch. */\r\n signal?: AbortSignal;\r\n };\r\n\r\ninterface RuntimeSyncParams {\r\n name?: string;\r\n version?: string;\r\n data?: unknown;\r\n receiver?: string;\r\n ignoreSelf?: boolean;\r\n onStream?: SyncRequestStreamCallback;\r\n offlineDropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';\r\n signal?: AbortSignal;\r\n}\r\n\r\ninterface SyncErrorParam { key: string; value: string | number | boolean };\r\n\r\ninterface SyncResponseError {\r\n status: 'error';\r\n message: string;\r\n errorCode: string;\r\n errorParams?: SyncErrorParam[];\r\n httpStatus?: number;\r\n}\r\n\r\ninterface SyncAckResponse {\r\n status?: 'success' | 'error';\r\n message?: string;\r\n result?: unknown;\r\n errorCode?: string;\r\n errorParams?: SyncErrorParam[];\r\n httpStatus?: number;\r\n}\r\n\r\ntype SyncResultForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n [ServerOutputForFullName<F, V>] extends [never]\r\n ? Record<string, never>\r\n : ServerOutputForFullName<F, V>;\r\n\r\ntype SyncRequestResponseForFullName<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n | SyncResponseError\r\n | {\r\n status: 'success';\r\n message: string;\r\n result: SyncResultForFullName<F, V>;\r\n };\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n// Sync Event Callbacks Registry\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n\r\ntype SyncEventCallback = (params: { clientOutput: unknown; serverOutput: unknown }) => void;\r\nconst syncEvents: Partial<Record<string, SyncEventCallback[]>> = {};\r\nconst syncStreamEvents: Partial<Record<string, SyncEventStreamCallback[]>> = {};\r\nconst noop = () => null;\r\n\r\ninterface SyncLifecycleHandlers {\r\n connect: () => void;\r\n disconnect: () => void;\r\n reconnectAttempt: (attempt: number) => void;\r\n userAfk: (payload: { userId: string; endTime?: number }) => void;\r\n userBack: (payload: { userId: string }) => void;\r\n connectError: (err: { message: string }) => void;\r\n}\r\n\r\nlet activeLifecycleHandlers: SyncLifecycleHandlers | null = null;\r\n\r\nconst canSendNow = (socketInstance: Socket) => {\r\n if (!socketInstance.connected) return false;\r\n return isOnline();\r\n};\r\n\r\nconst createQueueId = () => {\r\n return `${String(Date.now())}-${String(Math.random())}`;\r\n};\r\n\r\nconst getCallbacksForRoute = (route: string): SyncEventCallback[] => {\r\n syncEvents[route] ??= [];\r\n return syncEvents[route];\r\n};\r\n\r\nconst getStreamCallbacksForRoute = (route: string): SyncEventStreamCallback[] => {\r\n syncStreamEvents[route] ??= [];\r\n return syncStreamEvents[route];\r\n};\r\n\r\nconst triggerSyncCallbacks = (name: string, clientOutput: unknown, serverOutput: unknown) => {\r\n const callbacks = syncEvents[name] ?? [];\r\n if (callbacks.length === 0) {\r\n if (shouldLogDev()) {\r\n getLogger().warn(`Sync event ${name} has no registered callback on this page`);\r\n }\r\n return;\r\n }\r\n\r\n for (const callback of callbacks) {\r\n callback({ clientOutput, serverOutput });\r\n }\r\n};\r\n\r\nconst triggerSyncStreamCallbacks = (name: string, stream: SyncRouteStreamEvent) => {\r\n const callbacks = syncStreamEvents[name] ?? [];\r\n if (callbacks.length === 0) {\r\n return;\r\n }\r\n\r\n for (const callback of callbacks) {\r\n callback({ stream });\r\n }\r\n};\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n// syncRequest Function Overloads\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n\r\nconst normalizeSyncError = ({\r\n response,\r\n fallbackErrorCode,\r\n}: {\r\n response: SyncAckResponse;\r\n fallbackErrorCode: string;\r\n}): SyncResponseError => {\r\n const normalized = normalizeErrorResponseCore({\r\n response,\r\n fallbackErrorCode,\r\n resolveMessage: ({ errorCode }) => {\r\n if (typeof response.message === 'string' && response.message.trim().length > 0) {\r\n return response.message;\r\n }\r\n\r\n return errorCode;\r\n },\r\n });\r\n\r\n return {\r\n status: 'error',\r\n message: normalized.message,\r\n errorCode: normalized.errorCode,\r\n errorParams: normalized.errorParams,\r\n httpStatus: normalized.httpStatus,\r\n };\r\n};\r\n\r\ntype SyncRequestParamsWithOptions<F extends SyncFullName, V extends VersionsForFullName<F>> =\r\n SyncParamsForFullName<F, V>;\r\n\r\nconst syncRequestInternal = <F extends SyncFullName, V extends VersionsForFullName<F>>(\r\n params: SyncRequestParamsWithOptions<F, V>\r\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> => {\r\n const runtimeParams = params as RuntimeSyncParams;\r\n const { name, version, receiver, ignoreSelf, onStream, offlineDropPolicy, signal: externalSignal } = runtimeParams;\r\n const payloadData = runtimeParams.data;\r\n\r\n type RequestOutput = Prettify<SyncRequestResponseForFullName<F, V>>;\r\n\r\n return new Promise<RequestOutput>((resolve) => {\r\n void (async () => {\r\n //? B1 — if the consumer-supplied signal is already aborted at call\r\n //? time, short-circuit before we even touch the socket.\r\n if (externalSignal?.aborted) {\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'request.aborted' },\r\n fallbackErrorCode: 'request.aborted',\r\n }));\r\n return;\r\n }\r\n if (!name || typeof name !== \"string\") {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid name for syncRequest\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidName' });\r\n }\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'sync.invalidName' },\r\n fallbackErrorCode: 'sync.invalidName',\r\n }));\r\n return;\r\n }\r\n\r\n const parsedRoute = parseServiceRouteName(name);\r\n if (parsedRoute.status === 'error') {\r\n if (shouldLogDev()) {\r\n getLogger().error(`[syncRequest] Invalid service route name '${name}'`, undefined, { reason: parsedRoute.reason });\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'routing.invalidServiceRouteName' });\r\n }\r\n resolve(normalizeSyncError({\r\n response: {\r\n status: 'error',\r\n errorCode: 'routing.invalidServiceRouteName',\r\n errorParams: [{ key: 'name', value: name }],\r\n },\r\n fallbackErrorCode: 'routing.invalidServiceRouteName',\r\n }));\r\n return;\r\n }\r\n\r\n const sanitizedName = parsedRoute.normalizedRouteName;\r\n\r\n const data = payloadData && typeof payloadData === \"object\" ? payloadData : {};\r\n\r\n if (!version || typeof version !== 'string') {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid version for syncRequest\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidVersion' });\r\n }\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'sync.invalidVersion' },\r\n fallbackErrorCode: 'sync.invalidVersion',\r\n }));\r\n return;\r\n }\r\n\r\n const normalizedReceiver = typeof receiver === 'string' ? receiver.trim() : '';\r\n\r\n if (!normalizedReceiver) {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"You need to provide a receiver for syncRequest, this can be either 'all' to trigger all sockets which we do not recommend or it can be any value such as a code e.g 'Ag2cg4'. this works together with the joinRoom and leaveRoom function\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.missingReceiver' });\r\n }\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'sync.missingReceiver' },\r\n fallbackErrorCode: 'sync.missingReceiver',\r\n }));\r\n return;\r\n }\r\n\r\n if (!await waitForSocket()) {\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\r\n fallbackErrorCode: 'sync.ioUnavailable',\r\n }));\r\n return;\r\n }\r\n if (!socket) {\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'sync.ioUnavailable' },\r\n fallbackErrorCode: 'sync.ioUnavailable',\r\n }));\r\n return;\r\n }\r\n\r\n const fullName = `sync/${sanitizedName}/${version}`;\r\n let queueId: string | null = null;\r\n\r\n const runRequest = (socketInstance: Socket) => {\r\n if (!canSendNow(socketInstance)) {\r\n queueId ??= createQueueId();\r\n //? `enqueueSyncRequest` returns `false` when the offline queue is\r\n //? full and `dropPolicy: 'reject'` is active. Resolve with a\r\n //? normalized error envelope so the caller can branch instead of\r\n //? awaiting forever.\r\n const enqueued = enqueueSyncRequest({\r\n id: queueId,\r\n key: fullName,\r\n run: (s) => {\r\n runRequest(s);\r\n },\r\n createdAt: Date.now(),\r\n dropPolicy: offlineDropPolicy,\r\n });\r\n if (!enqueued) {\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'offline.queueFull' },\r\n fallbackErrorCode: 'offline.queueFull',\r\n }));\r\n }\r\n return;\r\n }\r\n\r\n const tempIndex = incrementResponseIndex();\r\n\r\n let cleanupProgressListener: (() => void) | null = null;\r\n\r\n if (shouldLogDev()) {\r\n getLogger().debug(`Client Sync Request(${String(tempIndex)})`, { syncName: sanitizedName, data, receiver: normalizedReceiver, ignoreSelf });\r\n }\r\n\r\n if (typeof onStream === 'function') {\r\n const progressEventName = buildSyncProgressEventName(tempIndex);\r\n const progressListener = (streamPayload: SyncRequestStreamEvent) => {\r\n if (shouldLogStream()) {\r\n getLogger().debug(`Server Sync Stream(${String(tempIndex)})`, { syncName: sanitizedName, streamPayload });\r\n }\r\n\r\n onStream(streamPayload);\r\n };\r\n\r\n socketInstance.on(progressEventName, progressListener);\r\n cleanupProgressListener = () => {\r\n socketInstance.off(progressEventName, progressListener);\r\n };\r\n }\r\n\r\n const syncCb = `${sanitizedName}/${version}`;\r\n socketInstance.emit(socketEventNames.sync, { name: fullName, data, cb: syncCb, receiver: normalizedReceiver, responseIndex: tempIndex, ignoreSelf });\r\n\r\n //? B1 — bridge the consumer's AbortSignal to the server. When the\r\n //? signal fires we emit `syncCancel { cb }` so the server-side\r\n //? handler stops emitting new chunks. We also resolve locally with\r\n //? `request.aborted` so the awaiting caller settles.\r\n let cleanupExternalAbort: (() => void) | null = null;\r\n if (externalSignal) {\r\n const externalAbortHandler = () => {\r\n socketInstance.emit(socketEventNames.syncCancel, { cb: syncCb });\r\n cleanupProgressListener?.();\r\n cleanupProgressListener = null;\r\n cleanupExternalAbort?.();\r\n cleanupExternalAbort = null;\r\n resolve(normalizeSyncError({\r\n response: { status: 'error', errorCode: 'request.aborted' },\r\n fallbackErrorCode: 'request.aborted',\r\n }));\r\n };\r\n externalSignal.addEventListener('abort', externalAbortHandler);\r\n cleanupExternalAbort = () => {\r\n externalSignal.removeEventListener('abort', externalAbortHandler);\r\n };\r\n }\r\n\r\n socketInstance.once(buildSyncResponseEventName(tempIndex), (responseData: SyncAckResponse) => {\r\n cleanupProgressListener?.();\r\n cleanupExternalAbort?.();\r\n cleanupExternalAbort = null;\r\n\r\n if (responseData.status === \"error\") {\r\n const normalizedError = normalizeSyncError({\r\n response: responseData,\r\n fallbackErrorCode: 'sync.failedRequest',\r\n });\r\n\r\n if (shouldLogDev()) {\r\n getLogger().error(`Sync ${sanitizedName} failed`, undefined, { message: normalizedError.message });\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({\r\n key: 'sync.failedRequest',\r\n params: [\r\n { key: 'name', value: sanitizedName },\r\n { key: 'message', value: normalizedError.message },\r\n ],\r\n });\r\n }\r\n resolve(normalizedError);\r\n return;\r\n }\r\n\r\n if (responseData.status !== 'success') {\r\n resolve(normalizeSyncError({\r\n response: responseData,\r\n fallbackErrorCode: 'sync.invalidServerResponse',\r\n }));\r\n return;\r\n }\r\n\r\n const result = responseData.result && typeof responseData.result === 'object'\r\n ? responseData.result\r\n : {};\r\n\r\n //? `RequestOutput.result` is a generic `Record<string, never>` shape\r\n //? that the runtime response can't structurally satisfy from the\r\n //? untyped `responseData.result` (server-side typing lives on the\r\n //? other side of the socket). The cast is the documented socket\r\n //? response boundary.\r\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- socket response boundary\r\n resolve({\r\n status: 'success',\r\n message: typeof responseData.message === 'string' && responseData.message.trim().length > 0\r\n ? responseData.message\r\n : `sync ${sanitizedName} success`,\r\n result,\r\n } as RequestOutput);\r\n });\r\n };\r\n\r\n runRequest(socket);\r\n })();\r\n });\r\n};\r\n\r\nexport function syncRequest<F extends SyncFullName, V extends VersionsForFullName<F>>(\r\n params: SyncRequestParamsWithOptions<F, V>\r\n): Promise<Prettify<SyncRequestResponseForFullName<F, V>>> {\r\n return syncRequestInternal(params);\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n// useSyncEvents Hook - Type-Safe Event Registration\r\n// ═══════════════════════════════════════════════════════════════════════════════\r\n\r\n//? Hoisted to module scope so the emitted `.d.ts` for `useSyncEvents` can\r\n//? reference them. Local interface declarations inside an exported function\r\n//? trigger TS4025 (\"has or is using private name\") under `declaration: true`.\r\ninterface TypedCallbackParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\r\n clientOutput: ClientOutputForFullName<F, V>;\r\n serverOutput: ServerOutputForFullName<F, V>;\r\n}\r\n\r\ninterface UpsertParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\r\n name: F;\r\n version: V;\r\n callback: (params: TypedCallbackParams<F, V>) => void;\r\n}\r\n\r\ninterface UpsertStreamParams<F extends SyncFullName, V extends VersionsForFullName<F>> {\r\n name: F;\r\n version: V;\r\n callback: SyncRouteStreamCallbackForFullName<F, V>;\r\n}\r\n\r\nexport const useSyncEvents = () => {\r\n const localRegistryRef = useRef<Map<string, SyncEventCallback>>(new Map());\r\n const localStreamRegistryRef = useRef<Map<string, SyncEventStreamCallback>>(new Map());\r\n\r\n const upsertSyncEventCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\r\n params: UpsertParams<F, V>\r\n ): (() => void) => {\r\n\r\n if (typeof params.version !== 'string') {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid version for upsertSyncEventCallback\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidVersion' });\r\n }\r\n return noop;\r\n }\r\n\r\n if (typeof params.callback !== 'function') {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid callback for upsertSyncEventCallback\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidCallback' });\r\n }\r\n return noop;\r\n }\r\n\r\n const routeName = params.name;\r\n const parsedRoute = parseServiceRouteName(routeName);\r\n if (parsedRoute.status === 'error') {\r\n if (shouldLogDev()) {\r\n getLogger().error(`Invalid name for upsertSyncEventCallback`, undefined, { routeName, reason: parsedRoute.reason });\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'routing.invalidServiceRouteName' });\r\n }\r\n return noop;\r\n }\r\n const sanitizedName = parsedRoute.normalizedRouteName;\r\n\r\n const routeVersion = params.version;\r\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\r\n const callback: SyncEventCallback = ({ clientOutput, serverOutput }) => {\r\n params.callback({\r\n clientOutput: clientOutput as ClientOutputForFullName<F, V>,\r\n serverOutput: serverOutput as ServerOutputForFullName<F, V>,\r\n });\r\n };\r\n const callbacks = getCallbacksForRoute(fullName);\r\n\r\n const previousForRoute = localRegistryRef.current.get(fullName);\r\n if (previousForRoute) {\r\n syncEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\r\n }\r\n\r\n const nextCallbacks = getCallbacksForRoute(fullName);\r\n // Multiple components can intentionally subscribe to the same sync event.\r\n // Only warn when the exact same callback is registered twice.\r\n if (nextCallbacks.includes(callback)) {\r\n if (shouldLogDev()) {\r\n getLogger().warn(`[SyncEvents] Duplicate callback registration was ignored`, { fullName });\r\n }\r\n\r\n localRegistryRef.current.set(fullName, callback);\r\n\r\n return () => {\r\n const current = getCallbacksForRoute(fullName);\r\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\r\n\r\n if (localRegistryRef.current.get(fullName) === callback) {\r\n localRegistryRef.current.delete(fullName);\r\n }\r\n };\r\n }\r\n\r\n nextCallbacks.push(callback);\r\n syncEvents[fullName] = nextCallbacks;\r\n localRegistryRef.current.set(fullName, callback);\r\n\r\n return () => {\r\n const current = getCallbacksForRoute(fullName);\r\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\r\n\r\n if (localRegistryRef.current.get(fullName) === callback) {\r\n localRegistryRef.current.delete(fullName);\r\n }\r\n };\r\n }, []);\r\n\r\n const upsertSyncEventStreamCallback = useCallback(<F extends SyncFullName, V extends VersionsForFullName<F>>(\r\n params: UpsertStreamParams<F, V>\r\n ): (() => void) => {\r\n\r\n if (typeof params.version !== 'string') {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid version for upsertSyncEventStreamCallback\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidVersion' });\r\n }\r\n return noop;\r\n }\r\n\r\n if (typeof params.callback !== 'function') {\r\n if (shouldLogDev()) {\r\n getLogger().error(\"Invalid callback for upsertSyncEventStreamCallback\");\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'sync.invalidCallback' });\r\n }\r\n return noop;\r\n }\r\n\r\n const routeName = params.name;\r\n const parsedRoute = parseServiceRouteName(routeName);\r\n if (parsedRoute.status === 'error') {\r\n if (shouldLogDev()) {\r\n getLogger().error(`Invalid name for upsertSyncEventStreamCallback`, undefined, { routeName, reason: parsedRoute.reason });\r\n }\r\n if (shouldNotifyDev()) {\r\n notify.error({ key: 'routing.invalidServiceRouteName' });\r\n }\r\n return noop;\r\n }\r\n const sanitizedName = parsedRoute.normalizedRouteName;\r\n\r\n const routeVersion = params.version;\r\n const fullName = `sync/${sanitizedName}/${routeVersion}`;\r\n //? Combined union: both `serverStream` (from `broadcastStream` /\r\n //? `streamTo`) and `clientStream` (from `_client_v{n}.ts`) flow through\r\n //? this callback. The type matches `SyncRouteStreamCallbackForFullName`.\r\n const typedCallback = params.callback as (params: {\r\n stream: SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>;\r\n }) => void;\r\n const callback: SyncEventStreamCallback = ({ stream }) => {\r\n typedCallback({\r\n stream: stream as SyncRouteStreamEvent<Prettify<CombinedRouteStream<F, V> extends StreamPayload ? CombinedRouteStream<F, V> : StreamPayload>>,\r\n });\r\n };\r\n const callbacks = getStreamCallbacksForRoute(fullName);\r\n\r\n const previousForRoute = localStreamRegistryRef.current.get(fullName);\r\n if (previousForRoute) {\r\n syncStreamEvents[fullName] = callbacks.filter((cb) => cb !== previousForRoute);\r\n }\r\n\r\n const nextCallbacks = getStreamCallbacksForRoute(fullName);\r\n if (nextCallbacks.includes(callback)) {\r\n if (shouldLogDev()) {\r\n getLogger().warn(`[SyncEvents] Duplicate stream callback registration was ignored`, { fullName });\r\n }\r\n\r\n localStreamRegistryRef.current.set(fullName, callback);\r\n\r\n return () => {\r\n const current = getStreamCallbacksForRoute(fullName);\r\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\r\n\r\n if (localStreamRegistryRef.current.get(fullName) === callback) {\r\n localStreamRegistryRef.current.delete(fullName);\r\n }\r\n };\r\n }\r\n\r\n nextCallbacks.push(callback);\r\n syncStreamEvents[fullName] = nextCallbacks;\r\n localStreamRegistryRef.current.set(fullName, callback);\r\n\r\n return () => {\r\n const current = getStreamCallbacksForRoute(fullName);\r\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\r\n\r\n if (localStreamRegistryRef.current.get(fullName) === callback) {\r\n localStreamRegistryRef.current.delete(fullName);\r\n }\r\n };\r\n }, []);\r\n\r\n useEffect(() => {\r\n const localRegistry = localRegistryRef.current;\r\n const localStreamRegistry = localStreamRegistryRef.current;\r\n\r\n return () => {\r\n for (const [fullName, callback] of localRegistry.entries()) {\r\n const current = getCallbacksForRoute(fullName);\r\n syncEvents[fullName] = current.filter((cb) => cb !== callback);\r\n }\r\n\r\n for (const [fullName, callback] of localStreamRegistry.entries()) {\r\n const current = getStreamCallbacksForRoute(fullName);\r\n syncStreamEvents[fullName] = current.filter((cb) => cb !== callback);\r\n }\r\n\r\n localRegistry.clear();\r\n localStreamRegistry.clear();\r\n };\r\n }, []);\r\n\r\n return { upsertSyncEventCallback, upsertSyncEventStreamCallback };\r\n}\r\n\r\nexport const useSyncEventTrigger = () => {\r\n const triggerSyncEvent = useCallback((name: string, clientOutput: unknown = {}, serverOutput: unknown = {}) => {\r\n triggerSyncCallbacks(name, clientOutput, serverOutput);\r\n }, []);\r\n\r\n const triggerSyncStreamEvent = useCallback((name: string, stream: SyncRouteStreamEvent) => {\r\n triggerSyncStreamCallbacks(name, stream);\r\n }, []);\r\n\r\n return { triggerSyncEvent, triggerSyncStreamEvent }\r\n}\r\n\r\ntype SocketStatusSetter = Dispatch<\r\n SetStateAction<{\r\n self: statusContent;\r\n [userId: string]: statusContent;\r\n }>\r\n>;\r\n\r\nconst buildConnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\r\n if (shouldLogSocketStatus()) getLogger().info(\"Connected to server\");\r\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"CONNECTED\" } }));\r\n};\r\n\r\nconst buildDisconnectHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => () => {\r\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"DISCONNECTED\" } }));\r\n if (shouldLogSocketStatus()) getLogger().info(\"Disconnected, trying to reconnect...\");\r\n};\r\n\r\nconst buildReconnectAttemptHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (attempt: number) => {\r\n setSocketStatus(prev => ({ ...prev, self: { ...prev.self, status: \"RECONNECTING\", reconnectAttempt: attempt } }));\r\n if (shouldLogSocketStatus()) getLogger().info(`Reconnecting attempt ${String(attempt)}...`);\r\n};\r\n\r\nconst buildUserAfkHandler = ({\r\n setSocketStatus, sessionRef,\r\n}: {\r\n setSocketStatus: SocketStatusSetter;\r\n sessionRef: RefObject<SessionLayout | null>;\r\n}) => ({ userId, endTime }: { userId: string; endTime?: number }) => {\r\n if (sessionRef.current !== null && userId === sessionRef.current.id) {\r\n setSocketStatus(prev => ({\r\n ...prev,\r\n self: { status: \"DISCONNECTED\", reconnectAttempt: undefined, endTime },\r\n }));\r\n } else {\r\n setSocketStatus(prev => ({\r\n ...prev,\r\n [userId]: { status: \"DISCONNECTED\", endTime },\r\n }));\r\n }\r\n};\r\n\r\nconst buildUserBackHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => ({ userId }: { userId: string }) => {\r\n if (shouldLogSocketStatus()) getLogger().debug(\"userBack\", { userId });\r\n setSocketStatus(prev => ({\r\n ...prev,\r\n [userId]: { status: \"CONNECTED\", endTime: undefined },\r\n }));\r\n};\r\n\r\nconst buildConnectErrorHandler = ({ setSocketStatus }: { setSocketStatus: SocketStatusSetter }) => (err: { message: string }) => {\r\n if (shouldLogSocketStatus()) getLogger().debug(\"connect_error\", { err });\r\n setSocketStatus(prev => ({\r\n ...prev,\r\n self: { ...prev.self, status: \"DISCONNECTED\", reconnectAttempt: undefined },\r\n }));\r\n if (shouldLogDev()) getLogger().error(`Connection error`, err);\r\n if (shouldNotifyDev()) notify.error({ key: 'common.connectionError' });\r\n};\r\n\r\nexport const initSyncRequest = async ({\r\n setSocketStatus,\r\n sessionRef\r\n}: {\r\n setSocketStatus: SocketStatusSetter;\r\n sessionRef: RefObject<SessionLayout | null> | null;\r\n}) => {\r\n\r\n if (!await waitForSocket()) { return; }\r\n if (!socket) { return; }\r\n if (!sessionRef) { return; }\r\n\r\n if (activeLifecycleHandlers) {\r\n socket.off(socketEventNames.connect, activeLifecycleHandlers.connect);\r\n socket.off(socketEventNames.disconnect, activeLifecycleHandlers.disconnect);\r\n socket.off(socketEventNames.reconnectAttempt, activeLifecycleHandlers.reconnectAttempt);\r\n socket.off(socketEventNames.userAfk, activeLifecycleHandlers.userAfk);\r\n socket.off(socketEventNames.userBack, activeLifecycleHandlers.userBack);\r\n socket.off(socketEventNames.connectError, activeLifecycleHandlers.connectError);\r\n }\r\n\r\n const connect = buildConnectHandler({ setSocketStatus });\r\n const disconnect = buildDisconnectHandler({ setSocketStatus });\r\n const reconnectAttempt = buildReconnectAttemptHandler({ setSocketStatus });\r\n const userAfk = buildUserAfkHandler({ setSocketStatus, sessionRef });\r\n const userBack = buildUserBackHandler({ setSocketStatus });\r\n const connectError = buildConnectErrorHandler({ setSocketStatus });\r\n\r\n activeLifecycleHandlers = {\r\n connect,\r\n disconnect,\r\n reconnectAttempt,\r\n userAfk,\r\n userBack,\r\n connectError,\r\n };\r\n\r\n socket.on(socketEventNames.connect, connect);\r\n socket.on(socketEventNames.disconnect, disconnect);\r\n socket.on(socketEventNames.reconnectAttempt, reconnectAttempt);\r\n socket.on(socketEventNames.userAfk, userAfk);\r\n socket.on(socketEventNames.userBack, userBack);\r\n socket.on(socketEventNames.connectError, connectError);\r\n\r\n}"],"mappings":";AAUA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA8C,aAAa,WAAW,cAAc;AAUpF,IAAM,eAAe,MAAM,iBAAiB,EAAE,QAAQ;AACtD,IAAM,kBAAkB,MAAM,iBAAiB,EAAE,QAAQ;AACzD,IAAM,wBAAwB,MAAM,iBAAiB,EAAE,QAAQ;AAC/D,IAAM,kBAAkB,MAAM,iBAAiB,EAAE,QAAQ;AAiKzD,IAAM,aAA2D,CAAC;AAClE,IAAM,mBAAuE,CAAC;AAC9E,IAAM,OAAO,MAAM;AAWnB,IAAI,0BAAwD;AAE5D,IAAM,aAAa,CAAC,mBAA2B;AAC7C,MAAI,CAAC,eAAe,UAAW,QAAO;AACtC,SAAO,SAAS;AAClB;AAEA,IAAM,gBAAgB,MAAM;AAC1B,SAAO,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC;AACvD;AAEA,IAAM,uBAAuB,CAAC,UAAuC;AACnE,aAAW,KAAK,MAAM,CAAC;AACvB,SAAO,WAAW,KAAK;AACzB;AAEA,IAAM,6BAA6B,CAAC,UAA6C;AAC/E,mBAAiB,KAAK,MAAM,CAAC;AAC7B,SAAO,iBAAiB,KAAK;AAC/B;AAEA,IAAM,uBAAuB,CAAC,MAAc,cAAuB,iBAA0B;AAC3F,QAAM,YAAY,WAAW,IAAI,KAAK,CAAC;AACvC,MAAI,UAAU,WAAW,GAAG;AAC1B,QAAI,aAAa,GAAG;AAClB,gBAAU,EAAE,KAAK,cAAc,IAAI,0CAA0C;AAAA,IAC/E;AACA;AAAA,EACF;AAEA,aAAW,YAAY,WAAW;AAChC,aAAS,EAAE,cAAc,aAAa,CAAC;AAAA,EACzC;AACF;AAEA,IAAM,6BAA6B,CAAC,MAAc,WAAiC;AACjF,QAAM,YAAY,iBAAiB,IAAI,KAAK,CAAC;AAC7C,MAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,EACF;AAEA,aAAW,YAAY,WAAW;AAChC,aAAS,EAAE,OAAO,CAAC;AAAA,EACrB;AACF;AAMA,IAAM,qBAAqB,CAAC;AAAA,EAC1B;AAAA,EACA;AACF,MAGyB;AACvB,QAAM,aAAa,2BAA2B;AAAA,IAC5C;AAAA,IACA;AAAA,IACA,gBAAgB,CAAC,EAAE,UAAU,MAAM;AACjC,UAAI,OAAO,SAAS,YAAY,YAAY,SAAS,QAAQ,KAAK,EAAE,SAAS,GAAG;AAC9E,eAAO,SAAS;AAAA,MAClB;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,WAAW;AAAA,IACpB,WAAW,WAAW;AAAA,IACtB,aAAa,WAAW;AAAA,IACxB,YAAY,WAAW;AAAA,EACzB;AACF;AAKA,IAAM,sBAAsB,CAC1B,WAC4D;AAC5D,QAAM,gBAAgB;AACtB,QAAM,EAAE,MAAM,SAAS,UAAU,YAAY,UAAU,mBAAmB,QAAQ,eAAe,IAAI;AACrG,QAAM,cAAc,cAAc;AAIlC,SAAO,IAAI,QAAuB,CAAC,YAAY;AAC7C,UAAM,YAAY;AAGhB,UAAI,gBAAgB,SAAS;AAC3B,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,kBAAkB;AAAA,UAC1D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAI,aAAa,GAAG;AAClB,oBAAU,EAAE,MAAM,8BAA8B;AAAA,QAClD;AACA,YAAI,gBAAgB,GAAG;AACrB,iBAAO,MAAM,EAAE,KAAK,mBAAmB,CAAC;AAAA,QAC1C;AACA,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,mBAAmB;AAAA,UAC3D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AAEA,YAAM,cAAc,sBAAsB,IAAI;AAC9C,UAAI,YAAY,WAAW,SAAS;AAClC,YAAI,aAAa,GAAG;AAClB,oBAAU,EAAE,MAAM,6CAA6C,IAAI,KAAK,QAAW,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,QACnH;AACA,YAAI,gBAAgB,GAAG;AACrB,iBAAO,MAAM,EAAE,KAAK,kCAAkC,CAAC;AAAA,QACzD;AACA,gBAAQ,mBAAmB;AAAA,UACzB,UAAU;AAAA,YACR,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,aAAa,CAAC,EAAE,KAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,UAC5C;AAAA,UACA,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AAEA,YAAM,gBAAgB,YAAY;AAElC,YAAM,OAAO,eAAe,OAAO,gBAAgB,WAAW,cAAc,CAAC;AAE7E,UAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,YAAI,aAAa,GAAG;AAClB,oBAAU,EAAE,MAAM,iCAAiC;AAAA,QACrD;AACA,YAAI,gBAAgB,GAAG;AACrB,iBAAO,MAAM,EAAE,KAAK,sBAAsB,CAAC;AAAA,QAC7C;AACA,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,sBAAsB;AAAA,UAC9D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AAEA,YAAM,qBAAqB,OAAO,aAAa,WAAW,SAAS,KAAK,IAAI;AAE5E,UAAI,CAAC,oBAAoB;AACvB,YAAI,aAAa,GAAG;AAClB,oBAAU,EAAE,MAAM,4OAA4O;AAAA,QAChQ;AACA,YAAI,gBAAgB,GAAG;AACrB,iBAAO,MAAM,EAAE,KAAK,uBAAuB,CAAC;AAAA,QAC9C;AACA,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,uBAAuB;AAAA,UAC/D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,cAAc,GAAG;AAC1B,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,qBAAqB;AAAA,UAC7D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AACA,UAAI,CAAC,QAAQ;AACX,gBAAQ,mBAAmB;AAAA,UACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,qBAAqB;AAAA,UAC7D,mBAAmB;AAAA,QACrB,CAAC,CAAC;AACF;AAAA,MACF;AAEA,YAAM,WAAW,QAAQ,aAAa,IAAI,OAAO;AACjD,UAAI,UAAyB;AAE7B,YAAM,aAAa,CAAC,mBAA2B;AAC7C,YAAI,CAAC,WAAW,cAAc,GAAG;AAC/B,sBAAY,cAAc;AAK1B,gBAAM,WAAW,mBAAmB;AAAA,YAClC,IAAI;AAAA,YACJ,KAAK;AAAA,YACL,KAAK,CAAC,MAAM;AACV,yBAAW,CAAC;AAAA,YACd;AAAA,YACA,WAAW,KAAK,IAAI;AAAA,YACpB,YAAY;AAAA,UACd,CAAC;AACD,cAAI,CAAC,UAAU;AACb,oBAAQ,mBAAmB;AAAA,cACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,oBAAoB;AAAA,cAC5D,mBAAmB;AAAA,YACrB,CAAC,CAAC;AAAA,UACJ;AACA;AAAA,QACF;AAEA,cAAM,YAAY,uBAAuB;AAEzC,YAAI,0BAA+C;AAEnD,YAAI,aAAa,GAAG;AAClB,oBAAU,EAAE,MAAM,uBAAuB,OAAO,SAAS,CAAC,KAAK,EAAE,UAAU,eAAe,MAAM,UAAU,oBAAoB,WAAW,CAAC;AAAA,QAC5I;AAEA,YAAI,OAAO,aAAa,YAAY;AAClC,gBAAM,oBAAoB,2BAA2B,SAAS;AAC9D,gBAAM,mBAAmB,CAAC,kBAA0C;AAClE,gBAAI,gBAAgB,GAAG;AACrB,wBAAU,EAAE,MAAM,sBAAsB,OAAO,SAAS,CAAC,KAAK,EAAE,UAAU,eAAe,cAAc,CAAC;AAAA,YAC1G;AAEA,qBAAS,aAAa;AAAA,UACxB;AAEA,yBAAe,GAAG,mBAAmB,gBAAgB;AACrD,oCAA0B,MAAM;AAC9B,2BAAe,IAAI,mBAAmB,gBAAgB;AAAA,UACxD;AAAA,QACF;AAEA,cAAM,SAAS,GAAG,aAAa,IAAI,OAAO;AAC1C,uBAAe,KAAK,iBAAiB,MAAM,EAAE,MAAM,UAAU,MAAM,IAAI,QAAQ,UAAU,oBAAoB,eAAe,WAAW,WAAW,CAAC;AAMnJ,YAAI,uBAA4C;AAChD,YAAI,gBAAgB;AAClB,gBAAM,uBAAuB,MAAM;AACjC,2BAAe,KAAK,iBAAiB,YAAY,EAAE,IAAI,OAAO,CAAC;AAC/D,sCAA0B;AAC1B,sCAA0B;AAC1B,mCAAuB;AACvB,mCAAuB;AACvB,oBAAQ,mBAAmB;AAAA,cACzB,UAAU,EAAE,QAAQ,SAAS,WAAW,kBAAkB;AAAA,cAC1D,mBAAmB;AAAA,YACrB,CAAC,CAAC;AAAA,UACJ;AACA,yBAAe,iBAAiB,SAAS,oBAAoB;AAC7D,iCAAuB,MAAM;AAC3B,2BAAe,oBAAoB,SAAS,oBAAoB;AAAA,UAClE;AAAA,QACF;AAEA,uBAAe,KAAK,2BAA2B,SAAS,GAAG,CAAC,iBAAkC;AAC5F,oCAA0B;AAC1B,iCAAuB;AACvB,iCAAuB;AAEvB,cAAI,aAAa,WAAW,SAAS;AACnC,kBAAM,kBAAkB,mBAAmB;AAAA,cACzC,UAAU;AAAA,cACV,mBAAmB;AAAA,YACrB,CAAC;AAED,gBAAI,aAAa,GAAG;AAClB,wBAAU,EAAE,MAAM,QAAQ,aAAa,WAAW,QAAW,EAAE,SAAS,gBAAgB,QAAQ,CAAC;AAAA,YACnG;AACA,gBAAI,gBAAgB,GAAG;AACrB,qBAAO,MAAM;AAAA,gBACX,KAAK;AAAA,gBACL,QAAQ;AAAA,kBACN,EAAE,KAAK,QAAQ,OAAO,cAAc;AAAA,kBACpC,EAAE,KAAK,WAAW,OAAO,gBAAgB,QAAQ;AAAA,gBACnD;AAAA,cACF,CAAC;AAAA,YACH;AACA,oBAAQ,eAAe;AACvB;AAAA,UACF;AAEA,cAAI,aAAa,WAAW,WAAW;AACrC,oBAAQ,mBAAmB;AAAA,cACzB,UAAU;AAAA,cACV,mBAAmB;AAAA,YACrB,CAAC,CAAC;AACF;AAAA,UACF;AAEA,gBAAM,SAAS,aAAa,UAAU,OAAO,aAAa,WAAW,WACjE,aAAa,SACb,CAAC;AAQL,kBAAQ;AAAA,YACN,QAAQ;AAAA,YACR,SAAS,OAAO,aAAa,YAAY,YAAY,aAAa,QAAQ,KAAK,EAAE,SAAS,IACtF,aAAa,UACb,QAAQ,aAAa;AAAA,YACzB;AAAA,UACF,CAAkB;AAAA,QACpB,CAAC;AAAA,MACH;AAEA,iBAAW,MAAM;AAAA,IACnB,GAAG;AAAA,EACL,CAAC;AACH;AAEO,SAAS,YACd,QACyD;AACzD,SAAO,oBAAoB,MAAM;AACnC;AA0BO,IAAM,gBAAgB,MAAM;AACjC,QAAM,mBAAmB,OAAuC,oBAAI,IAAI,CAAC;AACzE,QAAM,yBAAyB,OAA6C,oBAAI,IAAI,CAAC;AAErF,QAAM,0BAA0B,YAAY,CAC1C,WACiB;AAEjB,QAAI,OAAO,OAAO,YAAY,UAAU;AACtC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,6CAA6C;AAAA,MACjE;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,sBAAsB,CAAC;AAAA,MAC7C;AACA,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,OAAO,aAAa,YAAY;AACzC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,8CAA8C;AAAA,MAClE;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,uBAAuB,CAAC;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,OAAO;AACzB,UAAM,cAAc,sBAAsB,SAAS;AACnD,QAAI,YAAY,WAAW,SAAS;AAClC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,4CAA4C,QAAW,EAAE,WAAW,QAAQ,YAAY,OAAO,CAAC;AAAA,MACpH;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,kCAAkC,CAAC;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AACA,UAAM,gBAAgB,YAAY;AAElC,UAAM,eAAe,OAAO;AAC5B,UAAM,WAAW,QAAQ,aAAa,IAAI,YAAY;AACtD,UAAM,WAA8B,CAAC,EAAE,cAAc,aAAa,MAAM;AACtE,aAAO,SAAS;AAAA,QACd;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,YAAY,qBAAqB,QAAQ;AAE/C,UAAM,mBAAmB,iBAAiB,QAAQ,IAAI,QAAQ;AAC9D,QAAI,kBAAkB;AACpB,iBAAW,QAAQ,IAAI,UAAU,OAAO,CAAC,OAAO,OAAO,gBAAgB;AAAA,IACzE;AAEA,UAAM,gBAAgB,qBAAqB,QAAQ;AAGnD,QAAI,cAAc,SAAS,QAAQ,GAAG;AACpC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,KAAK,4DAA4D,EAAE,SAAS,CAAC;AAAA,MAC3F;AAEA,uBAAiB,QAAQ,IAAI,UAAU,QAAQ;AAE/C,aAAO,MAAM;AACX,cAAM,UAAU,qBAAqB,QAAQ;AAC7C,mBAAW,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAE7D,YAAI,iBAAiB,QAAQ,IAAI,QAAQ,MAAM,UAAU;AACvD,2BAAiB,QAAQ,OAAO,QAAQ;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAEA,kBAAc,KAAK,QAAQ;AAC3B,eAAW,QAAQ,IAAI;AACvB,qBAAiB,QAAQ,IAAI,UAAU,QAAQ;AAE/C,WAAO,MAAM;AACX,YAAM,UAAU,qBAAqB,QAAQ;AAC7C,iBAAW,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAE7D,UAAI,iBAAiB,QAAQ,IAAI,QAAQ,MAAM,UAAU;AACvD,yBAAiB,QAAQ,OAAO,QAAQ;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gCAAgC,YAAY,CAChD,WACiB;AAEjB,QAAI,OAAO,OAAO,YAAY,UAAU;AACtC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,mDAAmD;AAAA,MACvE;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,sBAAsB,CAAC;AAAA,MAC7C;AACA,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,OAAO,aAAa,YAAY;AACzC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,oDAAoD;AAAA,MACxE;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,uBAAuB,CAAC;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,OAAO;AACzB,UAAM,cAAc,sBAAsB,SAAS;AACnD,QAAI,YAAY,WAAW,SAAS;AAClC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,MAAM,kDAAkD,QAAW,EAAE,WAAW,QAAQ,YAAY,OAAO,CAAC;AAAA,MAC1H;AACA,UAAI,gBAAgB,GAAG;AACrB,eAAO,MAAM,EAAE,KAAK,kCAAkC,CAAC;AAAA,MACzD;AACA,aAAO;AAAA,IACT;AACA,UAAM,gBAAgB,YAAY;AAElC,UAAM,eAAe,OAAO;AAC5B,UAAM,WAAW,QAAQ,aAAa,IAAI,YAAY;AAItD,UAAM,gBAAgB,OAAO;AAG7B,UAAM,WAAoC,CAAC,EAAE,OAAO,MAAM;AACxD,oBAAc;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,YAAY,2BAA2B,QAAQ;AAErD,UAAM,mBAAmB,uBAAuB,QAAQ,IAAI,QAAQ;AACpE,QAAI,kBAAkB;AACpB,uBAAiB,QAAQ,IAAI,UAAU,OAAO,CAAC,OAAO,OAAO,gBAAgB;AAAA,IAC/E;AAEA,UAAM,gBAAgB,2BAA2B,QAAQ;AACzD,QAAI,cAAc,SAAS,QAAQ,GAAG;AACpC,UAAI,aAAa,GAAG;AAClB,kBAAU,EAAE,KAAK,mEAAmE,EAAE,SAAS,CAAC;AAAA,MAClG;AAEA,6BAAuB,QAAQ,IAAI,UAAU,QAAQ;AAErD,aAAO,MAAM;AACX,cAAM,UAAU,2BAA2B,QAAQ;AACnD,yBAAiB,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAEnE,YAAI,uBAAuB,QAAQ,IAAI,QAAQ,MAAM,UAAU;AAC7D,iCAAuB,QAAQ,OAAO,QAAQ;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,kBAAc,KAAK,QAAQ;AAC3B,qBAAiB,QAAQ,IAAI;AAC7B,2BAAuB,QAAQ,IAAI,UAAU,QAAQ;AAErD,WAAO,MAAM;AACX,YAAM,UAAU,2BAA2B,QAAQ;AACnD,uBAAiB,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAEnE,UAAI,uBAAuB,QAAQ,IAAI,QAAQ,MAAM,UAAU;AAC7D,+BAAuB,QAAQ,OAAO,QAAQ;AAAA,MAChD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,gBAAgB,iBAAiB;AACvC,UAAM,sBAAsB,uBAAuB;AAEnD,WAAO,MAAM;AACX,iBAAW,CAAC,UAAU,QAAQ,KAAK,cAAc,QAAQ,GAAG;AAC1D,cAAM,UAAU,qBAAqB,QAAQ;AAC7C,mBAAW,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAAA,MAC/D;AAEA,iBAAW,CAAC,UAAU,QAAQ,KAAK,oBAAoB,QAAQ,GAAG;AAChE,cAAM,UAAU,2BAA2B,QAAQ;AACnD,yBAAiB,QAAQ,IAAI,QAAQ,OAAO,CAAC,OAAO,OAAO,QAAQ;AAAA,MACrE;AAEA,oBAAc,MAAM;AACpB,0BAAoB,MAAM;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,yBAAyB,8BAA8B;AAClE;AAEO,IAAM,sBAAsB,MAAM;AACvC,QAAM,mBAAmB,YAAY,CAAC,MAAc,eAAwB,CAAC,GAAG,eAAwB,CAAC,MAAM;AAC7G,yBAAqB,MAAM,cAAc,YAAY;AAAA,EACvD,GAAG,CAAC,CAAC;AAEL,QAAM,yBAAyB,YAAY,CAAC,MAAc,WAAiC;AACzF,+BAA2B,MAAM,MAAM;AAAA,EACzC,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,kBAAkB,uBAAuB;AACpD;AASA,IAAM,sBAAsB,CAAC,EAAE,gBAAgB,MAA+C,MAAM;AAClG,MAAI,sBAAsB,EAAG,WAAU,EAAE,KAAK,qBAAqB;AACnE,kBAAgB,WAAS,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,QAAQ,YAAY,EAAE,EAAE;AACpF;AAEA,IAAM,yBAAyB,CAAC,EAAE,gBAAgB,MAA+C,MAAM;AACrG,kBAAgB,WAAS,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,QAAQ,eAAe,EAAE,EAAE;AACrF,MAAI,sBAAsB,EAAG,WAAU,EAAE,KAAK,sCAAsC;AACtF;AAEA,IAAM,+BAA+B,CAAC,EAAE,gBAAgB,MAA+C,CAAC,YAAoB;AAC1H,kBAAgB,WAAS,EAAE,GAAG,MAAM,MAAM,EAAE,GAAG,KAAK,MAAM,QAAQ,gBAAgB,kBAAkB,QAAQ,EAAE,EAAE;AAChH,MAAI,sBAAsB,EAAG,WAAU,EAAE,KAAK,wBAAwB,OAAO,OAAO,CAAC,KAAK;AAC5F;AAEA,IAAM,sBAAsB,CAAC;AAAA,EAC3B;AAAA,EAAiB;AACnB,MAGM,CAAC,EAAE,QAAQ,QAAQ,MAA4C;AACnE,MAAI,WAAW,YAAY,QAAQ,WAAW,WAAW,QAAQ,IAAI;AACnE,oBAAgB,WAAS;AAAA,MACvB,GAAG;AAAA,MACH,MAAM,EAAE,QAAQ,gBAAgB,kBAAkB,QAAW,QAAQ;AAAA,IACvE,EAAE;AAAA,EACJ,OAAO;AACL,oBAAgB,WAAS;AAAA,MACvB,GAAG;AAAA,MACH,CAAC,MAAM,GAAG,EAAE,QAAQ,gBAAgB,QAAQ;AAAA,IAC9C,EAAE;AAAA,EACJ;AACF;AAEA,IAAM,uBAAuB,CAAC,EAAE,gBAAgB,MAA+C,CAAC,EAAE,OAAO,MAA0B;AACjI,MAAI,sBAAsB,EAAG,WAAU,EAAE,MAAM,YAAY,EAAE,OAAO,CAAC;AACrE,kBAAgB,WAAS;AAAA,IACvB,GAAG;AAAA,IACH,CAAC,MAAM,GAAG,EAAE,QAAQ,aAAa,SAAS,OAAU;AAAA,EACtD,EAAE;AACJ;AAEA,IAAM,2BAA2B,CAAC,EAAE,gBAAgB,MAA+C,CAAC,QAA6B;AAC/H,MAAI,sBAAsB,EAAG,WAAU,EAAE,MAAM,iBAAiB,EAAE,IAAI,CAAC;AACvE,kBAAgB,WAAS;AAAA,IACvB,GAAG;AAAA,IACH,MAAM,EAAE,GAAG,KAAK,MAAM,QAAQ,gBAAgB,kBAAkB,OAAU;AAAA,EAC5E,EAAE;AACF,MAAI,aAAa,EAAG,WAAU,EAAE,MAAM,oBAAoB,GAAG;AAC7D,MAAI,gBAAgB,EAAG,QAAO,MAAM,EAAE,KAAK,yBAAyB,CAAC;AACvE;AAEO,IAAM,kBAAkB,OAAO;AAAA,EACpC;AAAA,EACA;AACF,MAGM;AAEJ,MAAI,CAAC,MAAM,cAAc,GAAG;AAAE;AAAA,EAAQ;AACtC,MAAI,CAAC,QAAQ;AAAE;AAAA,EAAQ;AACvB,MAAI,CAAC,YAAY;AAAE;AAAA,EAAQ;AAE3B,MAAI,yBAAyB;AAC3B,WAAO,IAAI,iBAAiB,SAAS,wBAAwB,OAAO;AACpE,WAAO,IAAI,iBAAiB,YAAY,wBAAwB,UAAU;AAC1E,WAAO,IAAI,iBAAiB,kBAAkB,wBAAwB,gBAAgB;AACtF,WAAO,IAAI,iBAAiB,SAAS,wBAAwB,OAAO;AACpE,WAAO,IAAI,iBAAiB,UAAU,wBAAwB,QAAQ;AACtE,WAAO,IAAI,iBAAiB,cAAc,wBAAwB,YAAY;AAAA,EAChF;AAEA,QAAM,UAAU,oBAAoB,EAAE,gBAAgB,CAAC;AACvD,QAAM,aAAa,uBAAuB,EAAE,gBAAgB,CAAC;AAC7D,QAAM,mBAAmB,6BAA6B,EAAE,gBAAgB,CAAC;AACzE,QAAM,UAAU,oBAAoB,EAAE,iBAAiB,WAAW,CAAC;AACnE,QAAM,WAAW,qBAAqB,EAAE,gBAAgB,CAAC;AACzD,QAAM,eAAe,yBAAyB,EAAE,gBAAgB,CAAC;AAEjE,4BAA0B;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,GAAG,iBAAiB,SAAS,OAAO;AAC3C,SAAO,GAAG,iBAAiB,YAAY,UAAU;AACjD,SAAO,GAAG,iBAAiB,kBAAkB,gBAAgB;AAC7D,SAAO,GAAG,iBAAiB,SAAS,OAAO;AAC3C,SAAO,GAAG,iBAAiB,UAAU,QAAQ;AAC7C,SAAO,GAAG,iBAAiB,cAAc,YAAY;AAEvD;","names":[]}
|