@emeryld/rrroutes-client 2.0.1 → 2.0.3
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/index.cjs +65 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +65 -26
- package/dist/index.mjs.map +1 -1
- package/dist/sockets/socket.client.context.d.ts +1 -3
- package/dist/sockets/socket.client.index.d.ts +29 -30
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -256,6 +256,7 @@ function createRouteClient(opts) {
|
|
|
256
256
|
try {
|
|
257
257
|
const out = await fetcher({ url, method });
|
|
258
258
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
259
|
+
rqOpts?.onReceive?.(parsed);
|
|
259
260
|
emit(
|
|
260
261
|
decorateDebugEvent(
|
|
261
262
|
{
|
|
@@ -321,6 +322,7 @@ function createRouteClient(opts) {
|
|
|
321
322
|
try {
|
|
322
323
|
const out = await fetcher({ url, method });
|
|
323
324
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
325
|
+
rqOpts?.onReceive?.(parsed);
|
|
324
326
|
emit(
|
|
325
327
|
decorateDebugEvent(
|
|
326
328
|
{
|
|
@@ -394,6 +396,7 @@ function createRouteClient(opts) {
|
|
|
394
396
|
try {
|
|
395
397
|
const out = await fetcher({ url, method, body: payload });
|
|
396
398
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
399
|
+
rqOpts?.onReceive?.(parsed);
|
|
397
400
|
emit(
|
|
398
401
|
decorateDebugEvent(
|
|
399
402
|
{
|
|
@@ -468,20 +471,16 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
468
471
|
var SocketCtx = React.createContext(null);
|
|
469
472
|
function buildSocketProvider({
|
|
470
473
|
events,
|
|
471
|
-
ping,
|
|
472
|
-
pong,
|
|
473
474
|
options
|
|
474
475
|
}) {
|
|
475
476
|
return {
|
|
476
|
-
SocketProvider: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SocketProvider, { events,
|
|
477
|
+
SocketProvider: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SocketProvider, { events, options, children }),
|
|
477
478
|
useSocketClient: () => useSocketClient(),
|
|
478
479
|
useSocketConnection: (args) => useSocketConnection(args)
|
|
479
480
|
};
|
|
480
481
|
}
|
|
481
482
|
function SocketProvider({
|
|
482
483
|
events,
|
|
483
|
-
ping,
|
|
484
|
-
pong,
|
|
485
484
|
options,
|
|
486
485
|
children
|
|
487
486
|
}) {
|
|
@@ -542,18 +541,37 @@ var SocketClient = class {
|
|
|
542
541
|
onPong: hb.onPong
|
|
543
542
|
};
|
|
544
543
|
this.socket.on("connect", () => {
|
|
545
|
-
this.dbg({
|
|
544
|
+
this.dbg({
|
|
545
|
+
type: "connection",
|
|
546
|
+
phase: "connect",
|
|
547
|
+
id: this.socket.id ?? ""
|
|
548
|
+
});
|
|
546
549
|
this.startHeartbeat();
|
|
547
550
|
});
|
|
548
|
-
this.socket.on("reconnect", (attempt) =>
|
|
551
|
+
this.socket.on("reconnect", (attempt) => {
|
|
552
|
+
this.dbg({
|
|
553
|
+
type: "connection",
|
|
554
|
+
phase: "reconnect",
|
|
555
|
+
attempt
|
|
556
|
+
});
|
|
557
|
+
});
|
|
549
558
|
this.socket.on("disconnect", (reason) => {
|
|
550
|
-
this.dbg({
|
|
559
|
+
this.dbg({
|
|
560
|
+
type: "connection",
|
|
561
|
+
phase: "disconnect",
|
|
562
|
+
reason: String(reason)
|
|
563
|
+
});
|
|
551
564
|
this.stopHeartbeat();
|
|
552
565
|
});
|
|
553
|
-
this.socket.on("connect_error", (err) =>
|
|
566
|
+
this.socket.on("connect_error", (err) => {
|
|
567
|
+
this.dbg({
|
|
568
|
+
type: "connection",
|
|
569
|
+
phase: "connect_error",
|
|
570
|
+
err: String(err)
|
|
571
|
+
});
|
|
572
|
+
});
|
|
554
573
|
this.socket.on(this.hb.pongEvent, (raw) => {
|
|
555
574
|
const receivedAt = Date.now();
|
|
556
|
-
const serverNowIso = raw?.serverNow;
|
|
557
575
|
const clientSentIso = raw?.clientEcho?.__clientSentAt;
|
|
558
576
|
let latencyMs;
|
|
559
577
|
if (clientSentIso) {
|
|
@@ -564,17 +582,21 @@ var SocketClient = class {
|
|
|
564
582
|
const ok = this.hb.pongSchema.safeParse(raw);
|
|
565
583
|
if (!ok.success) return;
|
|
566
584
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
585
|
+
const latency = latencyMs ?? raw?.sinceMs ?? 0;
|
|
586
|
+
this.dbg({
|
|
587
|
+
type: "heartbeat",
|
|
588
|
+
action: "pong_recv",
|
|
589
|
+
latencyMs: latency,
|
|
590
|
+
payload: raw
|
|
591
|
+
});
|
|
592
|
+
this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });
|
|
571
593
|
});
|
|
572
594
|
}
|
|
573
595
|
dbg(e) {
|
|
574
596
|
const d = this.debug;
|
|
575
597
|
if (!d.logger) return;
|
|
576
598
|
if (!d[e.type]) return;
|
|
577
|
-
if (d.only && !d.only.includes(e.event)) return;
|
|
599
|
+
if (d.only && "event" in e && !d.only.includes(e.event)) return;
|
|
578
600
|
d.logger(e);
|
|
579
601
|
}
|
|
580
602
|
/** internal stats snapshot */
|
|
@@ -601,7 +623,8 @@ var SocketClient = class {
|
|
|
601
623
|
const candidate = { ...basePayload, __clientSentAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
602
624
|
const check = this.hb.pingSchema.safeParse(candidate);
|
|
603
625
|
if (!check.success) {
|
|
604
|
-
if (this.environment === "development")
|
|
626
|
+
if (this.environment === "development")
|
|
627
|
+
console.warn("[socket] ping schema validation failed", check.error.issues);
|
|
605
628
|
return;
|
|
606
629
|
}
|
|
607
630
|
const timer = setTimeout(() => {
|
|
@@ -609,7 +632,11 @@ var SocketClient = class {
|
|
|
609
632
|
this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {
|
|
610
633
|
clearTimeout(timer);
|
|
611
634
|
});
|
|
612
|
-
this.dbg({
|
|
635
|
+
this.dbg({
|
|
636
|
+
type: "heartbeat",
|
|
637
|
+
action: "ping_emit",
|
|
638
|
+
payload: check.data
|
|
639
|
+
});
|
|
613
640
|
};
|
|
614
641
|
this.hbTimer = setInterval(tick, this.hb.intervalMs);
|
|
615
642
|
tick();
|
|
@@ -634,7 +661,11 @@ var SocketClient = class {
|
|
|
634
661
|
} else {
|
|
635
662
|
this.socket.emit(String(event), parsed.data);
|
|
636
663
|
}
|
|
637
|
-
this.dbg({
|
|
664
|
+
this.dbg({
|
|
665
|
+
type: "emit",
|
|
666
|
+
event,
|
|
667
|
+
metadata: this.debug.verbose ? metadata : void 0
|
|
668
|
+
});
|
|
638
669
|
}
|
|
639
670
|
joinRooms(rooms) {
|
|
640
671
|
const list = this.toArray(rooms);
|
|
@@ -646,7 +677,7 @@ var SocketClient = class {
|
|
|
646
677
|
}
|
|
647
678
|
if (toJoin.length > 0) {
|
|
648
679
|
this.socket.emit(this.roomJoinEvent, { rooms: toJoin });
|
|
649
|
-
this.dbg({ type: "join", rooms: toJoin });
|
|
680
|
+
this.dbg({ type: "room", action: "join", rooms: toJoin });
|
|
650
681
|
}
|
|
651
682
|
}
|
|
652
683
|
leaveRooms(rooms) {
|
|
@@ -661,12 +692,12 @@ var SocketClient = class {
|
|
|
661
692
|
}
|
|
662
693
|
if (toLeave.length > 0) {
|
|
663
694
|
this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });
|
|
664
|
-
this.dbg({ type: "leave", rooms: toLeave });
|
|
695
|
+
this.dbg({ type: "room", action: "leave", rooms: toLeave });
|
|
665
696
|
}
|
|
666
697
|
}
|
|
667
698
|
on(event, handler) {
|
|
668
699
|
const schema = this.events[event].message;
|
|
669
|
-
this.dbg({ type: "register", event });
|
|
700
|
+
this.dbg({ type: "register", action: "register", event });
|
|
670
701
|
const wrapped = (envelopeOrRaw, maybeAck) => {
|
|
671
702
|
const maybeEnvelope = envelopeOrRaw;
|
|
672
703
|
const rawData = maybeEnvelope?.data ?? maybeEnvelope;
|
|
@@ -694,10 +725,18 @@ var SocketClient = class {
|
|
|
694
725
|
} catch {
|
|
695
726
|
}
|
|
696
727
|
} : void 0
|
|
697
|
-
// NEW
|
|
698
728
|
}
|
|
699
729
|
};
|
|
700
|
-
this.dbg({
|
|
730
|
+
this.dbg({
|
|
731
|
+
type: "receive",
|
|
732
|
+
event,
|
|
733
|
+
envelope: this.debug.verbose ? {
|
|
734
|
+
eventName: meta.envelope.eventName,
|
|
735
|
+
sentAt: meta.envelope.sentAt,
|
|
736
|
+
sentTo: meta.envelope.sentTo,
|
|
737
|
+
metadata: meta.envelope.metadata
|
|
738
|
+
} : void 0
|
|
739
|
+
});
|
|
701
740
|
handler(parsed.data, meta);
|
|
702
741
|
};
|
|
703
742
|
const errorWrapped = (e) => {
|
|
@@ -722,17 +761,17 @@ var SocketClient = class {
|
|
|
722
761
|
s.delete(entry);
|
|
723
762
|
if (s.size === 0) this.handlerMap.delete(String(event));
|
|
724
763
|
}
|
|
725
|
-
this.dbg({ type: "unregister", event });
|
|
764
|
+
this.dbg({ type: "register", action: "unregister", event });
|
|
726
765
|
};
|
|
727
766
|
}
|
|
728
767
|
disconnect() {
|
|
729
768
|
this.stopHeartbeat();
|
|
730
769
|
this.socket.disconnect();
|
|
731
|
-
this.dbg({ type: "disconnect", reason: "client_disconnect" });
|
|
770
|
+
this.dbg({ type: "connection", phase: "disconnect", reason: "client_disconnect" });
|
|
732
771
|
}
|
|
733
772
|
connect() {
|
|
734
773
|
this.socket.connect();
|
|
735
|
-
this.dbg({ type: "connect", id: this.socket.id ?? "" });
|
|
774
|
+
this.dbg({ type: "connection", phase: "connect", id: this.socket.id ?? "" });
|
|
736
775
|
}
|
|
737
776
|
};
|
|
738
777
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts","../src/sockets/socket.client.index.ts","../src/sockets/socket.client.context.tsx"],"sourcesContent":["/**\n * This package exports React Query hooks, so we mark it as client-only to keep Next.js happy.\n */\n'use client';\n\nexport * from './routesV3.client.types';\nexport * from './routesV3.client.fetch';\nexport * from './routesV3.client.index';\nexport * from './sockets/socket.client.index';","// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuildMeta,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n RouteClientDebugEvent,\n RouteClientDebugLogger,\n RouteClientDebugMode,\n RouteClientDebugOptions,\n RouteClientDebugToggleOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Debug logging --------------------------------------------------------------\nconst noopDebugLogger: RouteClientDebugLogger = () => {};\n\nconst defaultDebugLogger: RouteClientDebugLogger = (event: RouteClientDebugEvent) => {\n if (typeof console === 'undefined') return;\n const fn = console.debug ?? console.log;\n fn?.call(console, '[rrroutes-client]', event);\n};\n\nconst debugEventTypes: RouteClientDebugEvent['type'][] = [\n 'fetch',\n 'invalidate',\n 'setData',\n 'build',\n 'useEndpoint',\n];\n\ntype DebugEmitter<Names extends string> = {\n emit: (event: RouteClientDebugEvent, name?: Names) => void;\n mode: RouteClientDebugMode;\n};\n\nconst noopEmit = () => {};\n\nfunction createDebugEmitter<Names extends string>(\n option?: RouteClientDebugOptions<Names>,\n environment?: string,\n): DebugEmitter<Names> {\n const disabled: DebugEmitter<Names> = { emit: noopEmit, mode: 'minimal' };\n\n if (environment && environment.toLowerCase() === 'production') {\n return disabled;\n }\n\n if (!option) {\n return disabled;\n }\n if (option === true || option === 'minimal') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (option === 'complete') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'complete',\n };\n }\n if (typeof option === 'function') {\n return {\n emit: (event, name) => option(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (typeof option === 'object') {\n const toggles = option as RouteClientDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = debugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteClientDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger ?? defaultDebugLogger;\n const emit: DebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type)) return;\n if (onlySet) {\n if (!name || !onlySet.has(name)) return;\n }\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL plus normalized params/query payloads.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery, normalizedParams };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient<Names extends string = string>(\n opts: RouteClientOptions<Names>,\n): RouteClient<Names> {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n const environment =\n opts.environment ?? undefined;\n const { emit: emitDebug, mode: debugMode } = createDebugEmitter<Names>(opts.debug, environment);\n const isVerboseDebug = debugMode === 'complete';\n const decorateDebugEvent = <T extends RouteClientDebugEvent>(\n event: T,\n details?: Partial<RouteClientDebugEvent>,\n ): RouteClientDebugEvent => {\n if (!isVerboseDebug || !details) return event;\n return { ...event, ...details } as RouteClientDebugEvent;\n };\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n const queryKey = prefix as unknown as QueryKey;\n await queryClient.invalidateQueries({ queryKey, exact });\n emitDebug({ type: 'invalidate', key: queryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n meta?: BuildMeta<Names>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n const method = toUpper(leaf.method);\n const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;\n const debugName = meta?.name;\n const emit = (event: RouteClientDebugEvent) => emitDebug(event, debugName);\n emit({ type: 'build', leaf: leafLabel });\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = async (...tuple: ArgsTuple<L>) => {\n const queryKey = key(...tuple);\n await queryClient.invalidateQueries({ queryKey, exact: true });\n emit({ type: 'invalidate', key: queryKey, exact: true });\n };\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n emit({ type: 'setData', key: k });\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'infiniteGet' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const startedAt = Date.now();\n const detail = isVerboseDebug ? { params: normalizedParams, query: pageQuery } : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug ? { params: normalizedParams, query: pageQuery, output: parsed } : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'get' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const fetchEndpoint: BuiltMutation<L>['fetch'] = async (\n ...tupleWithBody: [...ArgsTuple<L>, InferBody<L>]\n ) => {\n if (tupleWithBody.length === 0) {\n throw new Error('Body is required when invoking a mutation fetch.');\n }\n const bodyIndex = tupleWithBody.length - 1;\n const tuple = tupleWithBody.slice(0, bodyIndex) as ArgsTuple<L>;\n const body = tupleWithBody[bodyIndex] as InferBody<L>;\n const args = extractArgs<L>(tuple);\n const params = (args as any)?.params as InferParams<L> | undefined;\n const query = (args as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'start',\n method,\n url,\n leaf: leafLabel,\n body: payload,\n },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method, body: payload });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n body: payload,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n };\n\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'mutation' });\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: (body: InferBody<L>) =>\n fetchEndpoint(...([...tuple, body] as [...ArgsTuple<L>, InferBody<L>])),\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n fetch: fetchEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient<Names>['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n","// socket.client.index.ts\n\nimport { io, Socket } from 'socket.io-client';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\nexport type EventMap = Record<string, SocketEvent>;\nexport type Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type ServerEnvelope<T extends EventMap, K extends keyof T & string> = {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n data?: Payload<T, K>;\n metadata?: Record<string, unknown>;\n};\n\nexport type ClientCtx = {\n receivedAt: Date;\n latencyMs?: number;\n nsp?: string;\n socketId?: string;\n rooms?: string[];\n socket?: Socket;\n reply?: (data?: unknown) => void;\n};\n\nexport type ClientStatsSnapshot = {\n roomsCount: number;\n totalHandlers: number;\n rooms: { room: string; count: number }[];\n handlers: { event: string; handlers: number }[];\n};\n\nexport type SocketClientDebugEvent<K extends string = string> =\n | { type: 'connect'; id: string }\n | { type: 'reconnect'; attempt: number }\n | { type: 'disconnect'; reason: string }\n | { type: 'connect_error'; err: string }\n | { type: 'register'; event: K }\n | { type: 'unregister'; event: K }\n | { type: 'emit'; event: K; metadata?: Record<string, unknown> }\n | { type: 'receive'; event: K; envelope?: { eventName: K; sentAt: string | Date; sentTo: string[]; metadata?: Record<string, unknown> } }\n | { type: 'join'; rooms: string[]; }\n | { type: 'leave'; rooms: string[]; }\n | { type: 'stats'; value: ClientStatsSnapshot }\n | { type: 'ping_emit'; payload: unknown }\n | { type: 'pong_recv'; latencyMs: number; payload?: unknown };\n\nexport type SocketClientDebugOptions<K extends string = string> = {\n verbose?: boolean;\n only?: K[];\n logger?: (e: SocketClientDebugEvent<K>) => void;\n} & {\n [P in SocketClientDebugEvent['type']]?: boolean;\n};\n\n/** === NEW: Heartbeat config (enforced) === */\nexport type HeartbeatClientOptions<Ping extends ZodType, Pong extends ZodType> = {\n /** Event names. Defaults are 'sys:ping' and 'sys:pong'. */\n pingEvent?: string;\n pongEvent?: string;\n /** Interval between pings. Default 15_000. */\n intervalMs?: number;\n /** Give up waiting for pong after this many ms. Default 7_500. */\n timeoutMs?: number;\n\n /** Schema of the ping payload you will emit. */\n pingSchema: Ping;\n /** Produce the ping payload on each tick. */\n makePingPayload: (ctx: { socket: Socket }) => NoInfer<z.infer<Ping>>;\n\n /** Optional validation of the pong payload you receive. */\n pongSchema?: Pong;\n /** Optional hook called on each pong. */\n onPong?: (args: { latencyMs: number; payload?: NoInfer<z.infer<Pong>>; socket: Socket }) => void;\n};\n\nexport type SocketClientOptions<Ping extends ZodType, Pong extends ZodType,T extends EventMap = EventMap> = {\n url: string;\n ioOptions?: Parameters<typeof io>[1];\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n environment?: 'development' | 'production';\n debug?: SocketClientDebugOptions<keyof T & string>;\n\n /** NEW: required heartbeat config */\n heartbeat: HeartbeatClientOptions<Ping, Pong>;\n};\n\ntype HandlerEntry<T extends EventMap, K extends keyof T & string> = {\n orig: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n wrapped: (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>) => void;\n errorWrapped: (e: unknown) => void;\n};\n\nexport class SocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap> {\n readonly socket: Socket;\n private readonly events: T;\n private readonly roomJoinEvent: string;\n private readonly roomLeaveEvent: string;\n private readonly environment: 'development' | 'production';\n private readonly debug: SocketClientDebugOptions<keyof T & string>;\n\n // NEW\n private readonly hb: Required<Omit<HeartbeatClientOptions<Ping, Pong>, 'pongSchema' | 'onPong'>> & {\n pongSchema?: z.ZodTypeAny;\n onPong?: HeartbeatClientOptions<Ping, Pong>['onPong'];\n };\n private hbTimer: ReturnType<typeof setInterval> | null = null;\n\n // stats\n private readonly roomCounts = new Map<string, number>();\n private readonly handlerMap = new Map<string, Set<HandlerEntry<T, any>>>();\n\n constructor(events: T, opts: SocketClientOptions<Ping, Pong, T>) {\n this.events = events;\n this.socket = io(opts.url, { autoConnect: true, ...opts.ioOptions });\n this.roomJoinEvent = opts.roomJoinEvent ?? 'room:join';\n this.roomLeaveEvent = opts.roomLeaveEvent ?? 'room:leave';\n this.environment = opts.environment ?? 'development';\n this.debug = opts.debug ?? {};\n // heartbeat\n const hb = opts.heartbeat;\n this.hb = {\n pingEvent: hb.pingEvent ?? 'sys:ping',\n pongEvent: hb.pongEvent ?? 'sys:pong',\n intervalMs: hb.intervalMs ?? 15_000,\n timeoutMs: hb.timeoutMs ?? 7_500,\n pingSchema: hb.pingSchema,\n makePingPayload: hb.makePingPayload,\n pongSchema: hb.pongSchema,\n onPong: hb.onPong,\n };\n\n // socket lifecycle\n this.socket.on('connect', () => {\n this.dbg( { type: 'connect', id: this.socket.id ?? '' });\n this.startHeartbeat();\n });\n this.socket.on('reconnect', (attempt) => this.dbg( { type: 'reconnect', attempt }));\n this.socket.on('disconnect', (reason) => {\n this.dbg( { type: 'disconnect', reason: String(reason) });\n this.stopHeartbeat();\n });\n this.socket.on('connect_error', (err) =>this.dbg( { type: 'connect_error', err: String(err) }));\n\n // wire pong listener\n this.socket.on(this.hb.pongEvent, (raw: any) => {\n const receivedAt = Date.now();\n const serverNowIso: string | undefined = raw?.serverNow;\n const clientSentIso: string | undefined = raw?.clientEcho?.__clientSentAt;\n let latencyMs: number | undefined;\n\n if (clientSentIso) {\n const sent = Date.parse(clientSentIso);\n if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);\n }\n\n if (this.hb.pongSchema) {\n const ok = this.hb.pongSchema.safeParse(raw);\n if (!ok.success) return; // drop invalid\n }\n\n if (this.debug.logger && this.debug.pong_recv) {\n this.debug.logger({ type: 'pong_recv', latencyMs: latencyMs ?? raw?.sinceMs ?? 0, payload: raw });\n }\n this.hb.onPong?.({ latencyMs: latencyMs ?? raw?.sinceMs ?? 0, payload: raw, socket: this.socket });\n });\n }\n\n dbg(\n e: SocketClientDebugEvent<any>\n ){\n const d = this.debug;\n if (!d.logger) return;\n if (!d[e.type]) return;\n if (d.only && !d.only.includes((e as any).event)) return;\n d.logger(e);\n };\n\n /** internal stats snapshot */\n stats(): ClientStatsSnapshot {\n const rooms = Array.from(this.roomCounts.entries()).map(([room, count]) => ({ room, count }));\n const handlers = Array.from(this.handlerMap.entries()).map(([event, set]) => ({\n event,\n handlers: set.size,\n }));\n return {\n roomsCount: rooms.length,\n totalHandlers: handlers.reduce((a, b) => a + b.handlers, 0),\n rooms,\n handlers,\n };\n }\n\n private toArray(rooms?: string[] | string): string[] {\n return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n }\n\n private startHeartbeat() {\n this.stopHeartbeat();\n const tick = () => {\n const basePayload = this.hb.makePingPayload({ socket: this.socket }) ?? {};\n const candidate = { ...basePayload, __clientSentAt: new Date().toISOString() };\n\n const check = this.hb.pingSchema.safeParse(candidate);\n if (!check.success) {\n if (this.environment === 'development') console.warn('[socket] ping schema validation failed', check.error.issues);\n return;\n }\n\n // emit with ack timeout support\n const timer = setTimeout(() => {\n /* timeout, no-op here. consumer can observe missing pong if desired */\n }, this.hb.timeoutMs);\n\n this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {\n clearTimeout(timer);\n });\n\n this.dbg({ type: 'ping_emit', payload: check.data });\n\n };\n\n this.hbTimer = setInterval(tick, this.hb.intervalMs);\n // fire first ping immediately\n tick();\n }\n\n private stopHeartbeat() {\n if (this.hbTimer) {\n clearInterval(this.hbTimer);\n this.hbTimer = null;\n }\n }\n\nemit<K extends keyof T & string>(\n event: K,\n payload: Payload<T, K>,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void, \n timeoutMs?: number \n): void {\n const schema = this.events[event].message;\n const parsed = schema.safeParse(payload);\n if (!parsed.success) throw new Error(`Invalid payload for \"${event}\": ${parsed.error.message}`);\n\n if (onAck) {\n this.socket.timeout(timeoutMs ?? this.hb.timeoutMs).emit(String(event), parsed.data, (ack: unknown) => {\n try { onAck(ack); } catch { /* noop */ }\n });\n } else {\n this.socket.emit(String(event), parsed.data);\n }\n\n this.dbg( { type: 'emit', event, metadata: this.debug.verbose ? metadata : undefined });\n}\n\n\n joinRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toJoin: string[] = [];\n for (const r of list) {\n const next = (this.roomCounts.get(r) ?? 0) + 1;\n this.roomCounts.set(r, next);\n if (next === 1) toJoin.push(r);\n }\n if (toJoin.length > 0) {\n this.socket.emit(this.roomJoinEvent, { rooms: toJoin });\n this.dbg({ type: 'join', rooms: toJoin });\n }\n }\n\n leaveRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toLeave: string[] = [];\n for (const r of list) {\n const curr = this.roomCounts.get(r) ?? 0;\n const next = Math.max(0, curr - 1);\n if (next === 0 && curr > 0) toLeave.push(r);\n if (next === 0) this.roomCounts.delete(r);\n else this.roomCounts.set(r, next);\n }\n if (toLeave.length > 0) {\n this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });\n this.dbg({ type: 'leave', rooms: toLeave });\n }\n }\n\n on<K extends keyof T & string>(\n event: K,\n handler: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void,\n ): () => void {\n const schema = this.events[event].message;\n\n this.dbg( { type: 'register', event });\n\nconst wrapped = (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>, maybeAck?: (data?: unknown) => void) => {\n const maybeEnvelope = envelopeOrRaw as any;\n const rawData = maybeEnvelope?.data ?? maybeEnvelope;\n\n const parsed = schema.safeParse(rawData);\n if (!parsed.success) return;\n\n const receivedAt = new Date();\n const sentAt = maybeEnvelope?.sentAt ? new Date(maybeEnvelope.sentAt) : undefined;\n\n const meta = {\n envelope: {\n eventName: (maybeEnvelope?.eventName ?? event) as K,\n sentAt: maybeEnvelope?.sentAt ?? receivedAt.toISOString(),\n sentTo: maybeEnvelope?.sentTo ?? [],\n data: undefined,\n metadata: maybeEnvelope?.metadata,\n },\n ctx: {\n receivedAt,\n latencyMs: sentAt ? Math.max(0, receivedAt.getTime() - sentAt.getTime()) : undefined,\n nsp: (this.socket as any).nsp,\n socketId: this.socket.id,\n socket: this.socket,\n reply: typeof maybeAck === 'function' ? (d?: unknown) => { try { maybeAck(d); } catch { /* noop */ } } : undefined, // NEW\n },\n } as const;\n this.dbg( { type: 'receive', event, envelope: this.debug.verbose ? { eventName: meta.envelope.eventName, sentAt: meta.envelope.sentAt, sentTo: meta.envelope.sentTo, metadata: meta.envelope.metadata } : undefined });\n\n handler(parsed.data as any, meta as any);\n};\n\n\n const errorWrapped = (e: unknown) => {\n if (this.environment === 'development') {\n // eslint-disable-next-line no-console\n console.warn(`[socket] ${String(event)}:error`, e);\n }\n };\n\n this.socket.on(String(event), wrapped);\n this.socket.on(`${String(event)}:error`, errorWrapped);\n\n let set = this.handlerMap.get(String(event));\n if (!set) {\n set = new Set();\n this.handlerMap.set(String(event), set);\n }\n const entry: HandlerEntry<T, K> = { orig: handler, wrapped, errorWrapped };\n set.add(entry);\n\n return () => {\n this.socket.off(String(event), wrapped);\n this.socket.off(`${String(event)}:error`, errorWrapped);\n const s = this.handlerMap.get(String(event));\n if (s) {\n s.delete(entry);\n if (s.size === 0) this.handlerMap.delete(String(event));\n }\n this.dbg( { type: 'unregister', event });\n };\n }\n\n disconnect(): void {\n this.stopHeartbeat();\n this.socket.disconnect();\n this.dbg( { type: 'disconnect', reason: 'client_disconnect' });\n }\n\n connect(): void {\n this.socket.connect();\n this.dbg( { type: 'connect', id: this.socket.id ?? '' });\n }\n}\n\nexport * from './socket.client.context';","// socket.client.context.tsx\nimport * as React from 'react';\nimport { SocketClient, SocketClientOptions, EventMap, ClientCtx, Payload, ServerEnvelope } from './socket.client.index';\nimport { ZodType } from 'zod';\n\ntype SocketProviderProps<Ping extends ZodType, Pong extends ZodType, T extends EventMap> = React.PropsWithChildren<{\n events: T;\n ping: Ping;\n pong: Pong;\n options: SocketClientOptions<Ping, Pong, T>;\n}>;\n\nconst SocketCtx = React.createContext<SocketClient<ZodType, ZodType, any> | null>(null);\n\nexport function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n ping,\n pong,\n options,\n}: Omit<SocketProviderProps<Ping, Pong, T>, 'children'>) {\n return {\n SocketProvider: ({ children }: React.PropsWithChildren<{}>) => (\n <SocketProvider<Ping, Pong, T> events={events} ping={ping} pong={pong} options={options}>\n {children}\n </SocketProvider>\n ),\n useSocketClient: () => useSocketClient<Ping, Pong, T>(),\n useSocketConnection: <K extends keyof T & string>(\n args: Parameters<typeof useSocketConnection<T, K>>[0]\n ) => useSocketConnection<T, K>(args),\n };\n}\n\nfunction SocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n ping,\n pong,\n options,\n children,\n}: SocketProviderProps<Ping, Pong, T>) {\n const client = React.useMemo(() => new SocketClient(events, options), [events, options.url]);\n\n React.useEffect(() => {\n client.connect();\n return () => {\n client.disconnect();\n };\n }, []);\n\n return <SocketCtx.Provider value={client}>{children}</SocketCtx.Provider>;\n}\n\nfunction useSocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(): SocketClient<Ping, Pong, T> {\n const ctx = React.useContext(SocketCtx);\n if (!ctx) throw new Error('SocketClient not found. Wrap with <SocketProvider>.');\n return ctx as SocketClient<Ping, Pong, T>;\n}\n\ntype Rooms = string[] | string | undefined;\n\nexport type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string> = {\n event: K;\n rooms?: Rooms;\n onMessage: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n onCleanup?: () => void;\n autoJoin?: boolean;\n autoLeave?: boolean;\n deps?: React.DependencyList;\n};\n\nfunction useSocketConnection<T extends EventMap, K extends keyof T & string>(\n args: UseSocketConnectionArgs<T, K>,\n) {\n const { event, rooms, onMessage, onCleanup, autoJoin = true, autoLeave = true } = args;\n const client = useSocketClient<ZodType, ZodType, T>();\n\n const normalizedRooms = React.useMemo(\n () => (rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms]),\n [rooms],\n );\n\n React.useEffect(() => {\n if (autoJoin && normalizedRooms.length > 0) client.joinRooms(normalizedRooms);\n const unsubscribe = client.on(event, onMessage);\n\n return () => {\n unsubscribe();\n if (autoLeave && normalizedRooms.length > 0) client.leaveRooms(normalizedRooms);\n if (onCleanup) onCleanup();\n };\n }, args.deps ?? [client, event, onMessage, autoJoin, autoLeave, ...normalizedRooms]);\n}\n"],"mappings":"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","names":["useEndpoint"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts","../src/sockets/socket.client.index.ts","../src/sockets/socket.client.context.tsx"],"sourcesContent":["/**\n * This package exports React Query hooks, so we mark it as client-only to keep Next.js happy.\n */\n'use client';\n\nexport * from './routesV3.client.types';\nexport * from './routesV3.client.fetch';\nexport * from './routesV3.client.index';\nexport * from './sockets/socket.client.index';","// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuildMeta,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n RouteClientDebugEvent,\n RouteClientDebugLogger,\n RouteClientDebugMode,\n RouteClientDebugOptions,\n RouteClientDebugToggleOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Debug logging --------------------------------------------------------------\nconst noopDebugLogger: RouteClientDebugLogger = () => {};\n\nconst defaultDebugLogger: RouteClientDebugLogger = (event: RouteClientDebugEvent) => {\n if (typeof console === 'undefined') return;\n const fn = console.debug ?? console.log;\n fn?.call(console, '[rrroutes-client]', event);\n};\n\nconst debugEventTypes: RouteClientDebugEvent['type'][] = [\n 'fetch',\n 'invalidate',\n 'setData',\n 'build',\n 'useEndpoint',\n];\n\ntype DebugEmitter<Names extends string> = {\n emit: (event: RouteClientDebugEvent, name?: Names) => void;\n mode: RouteClientDebugMode;\n};\n\nconst noopEmit = () => {};\n\nfunction createDebugEmitter<Names extends string>(\n option?: RouteClientDebugOptions<Names>,\n environment?: string,\n): DebugEmitter<Names> {\n const disabled: DebugEmitter<Names> = { emit: noopEmit, mode: 'minimal' };\n\n if (environment && environment.toLowerCase() === 'production') {\n return disabled;\n }\n\n if (!option) {\n return disabled;\n }\n if (option === true || option === 'minimal') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (option === 'complete') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'complete',\n };\n }\n if (typeof option === 'function') {\n return {\n emit: (event, name) => option(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (typeof option === 'object') {\n const toggles = option as RouteClientDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = debugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteClientDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger ?? defaultDebugLogger;\n const emit: DebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type)) return;\n if (onlySet) {\n if (!name || !onlySet.has(name)) return;\n }\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL plus normalized params/query payloads.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery, normalizedParams };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient<Names extends string = string>(\n opts: RouteClientOptions<Names>,\n): RouteClient<Names> {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n const environment =\n opts.environment ?? undefined;\n const { emit: emitDebug, mode: debugMode } = createDebugEmitter<Names>(opts.debug, environment);\n const isVerboseDebug = debugMode === 'complete';\n const decorateDebugEvent = <T extends RouteClientDebugEvent>(\n event: T,\n details?: Partial<RouteClientDebugEvent>,\n ): RouteClientDebugEvent => {\n if (!isVerboseDebug || !details) return event;\n return { ...event, ...details } as RouteClientDebugEvent;\n };\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n const queryKey = prefix as unknown as QueryKey;\n await queryClient.invalidateQueries({ queryKey, exact });\n emitDebug({ type: 'invalidate', key: queryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n meta?: BuildMeta<Names>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n const method = toUpper(leaf.method);\n const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;\n const debugName = meta?.name;\n const emit = (event: RouteClientDebugEvent) => emitDebug(event, debugName);\n emit({ type: 'build', leaf: leafLabel });\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = async (...tuple: ArgsTuple<L>) => {\n const queryKey = key(...tuple);\n await queryClient.invalidateQueries({ queryKey, exact: true });\n emit({ type: 'invalidate', key: queryKey, exact: true });\n };\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n emit({ type: 'setData', key: k });\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'infiniteGet' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>,\n unknown,\n InfiniteData<InferOutput<L>>,\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const startedAt = Date.now();\n const detail = isVerboseDebug ? { params: normalizedParams, query: pageQuery } : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as InfiniteBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug ? { params: normalizedParams, query: pageQuery, output: parsed } : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'get' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as QueryBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const fetchEndpoint: BuiltMutation<L>['fetch'] = async (\n ...tupleWithBody: [...ArgsTuple<L>, InferBody<L>]\n ) => {\n if (tupleWithBody.length === 0) {\n throw new Error('Body is required when invoking a mutation fetch.');\n }\n const bodyIndex = tupleWithBody.length - 1;\n const tuple = tupleWithBody.slice(0, bodyIndex) as ArgsTuple<L>;\n const body = tupleWithBody[bodyIndex] as InferBody<L>;\n const args = extractArgs<L>(tuple);\n const params = (args as any)?.params as InferParams<L> | undefined;\n const query = (args as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'start',\n method,\n url,\n leaf: leafLabel,\n body: payload,\n },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method, body: payload });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as MutationBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n body: payload,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n };\n\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'mutation' });\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: (body: InferBody<L>) =>\n fetchEndpoint(...([...tuple, body] as [...ArgsTuple<L>, InferBody<L>])),\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n fetch: fetchEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient<Names>['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n","// socket.client.index.ts\n\nimport { io, Socket } from 'socket.io-client';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\nexport type EventMap = Record<string, SocketEvent>;\nexport type Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type ServerEnvelope<T extends EventMap, K extends keyof T & string> = {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n data?: Payload<T, K>;\n metadata?: Record<string, unknown>;\n};\n\nexport type ClientCtx = {\n receivedAt: Date;\n latencyMs?: number;\n nsp?: string;\n socketId?: string;\n rooms?: string[];\n socket?: Socket;\n reply?: (data?: unknown) => void;\n};\n\nexport type ClientStatsSnapshot = {\n roomsCount: number;\n totalHandlers: number;\n rooms: { room: string; count: number }[];\n handlers: { event: string; handlers: number }[];\n};\n\n// helper, since original code used NoInfer\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\n/**\n * Merged/logically grouped debug events.\n *\n * Buckets:\n * - connection: connect, reconnect, disconnect, connect_error\n * - register: register, unregister\n * - pingpong: ping_emit, pong_recv\n * - room: join, leave\n * - emit\n * - receive\n * - stats\n */\nexport type SocketClientDebugEvent<K extends string = string> =\n | {\n type: 'connection';\n phase: 'connect' | 'reconnect' | 'disconnect' | 'connect_error';\n id?: string;\n attempt?: number;\n reason?: string;\n err?: string;\n }\n | {\n type: 'register';\n action: 'register' | 'unregister';\n event: K;\n }\n | {\n type: 'heartbeat';\n action: 'ping_emit' | 'pong_recv';\n latencyMs?: number;\n payload?: unknown;\n }\n | {\n type: 'room';\n action: 'join' | 'leave';\n rooms: string[];\n }\n | {\n type: 'emit';\n event: K;\n metadata?: Record<string, unknown>;\n }\n | {\n type: 'receive';\n event: K;\n envelope?: {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n metadata?: Record<string, unknown>;\n };\n };\n\nexport type SocketClientDebugOptions<K extends string = string> = {\n verbose?: boolean;\n only?: K[];\n logger?: (e: SocketClientDebugEvent<K>) => void;\n} & {\n [P in SocketClientDebugEvent['type']]?: boolean;\n};\n\n/** === NEW: Heartbeat config (enforced) === */\nexport type HeartbeatClientOptions<Ping extends ZodType, Pong extends ZodType> = {\n /** Event names. Defaults are 'sys:ping' and 'sys:pong'. */\n pingEvent?: string;\n pongEvent?: string;\n /** Interval between pings. Default 15_000. */\n intervalMs?: number;\n /** Give up waiting for pong after this many ms. Default 7_500. */\n timeoutMs?: number;\n\n /** Schema of the ping payload you will emit. */\n pingSchema: Ping;\n /** Produce the ping payload on each tick. */\n makePingPayload: (ctx: { socket: Socket }) => NoInfer<z.infer<Ping>>;\n\n /** Optional validation of the pong payload you receive. */\n pongSchema?: Pong;\n /** Optional hook called on each pong. */\n onPong?: (args: { latencyMs: number; payload?: NoInfer<z.infer<Pong>>; socket: Socket }) => void;\n};\n\nexport type SocketClientOptions<\n Ping extends ZodType,\n Pong extends ZodType,\n T extends EventMap = EventMap\n> = {\n url: string;\n ioOptions?: Parameters<typeof io>[1];\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n environment?: 'development' | 'production';\n debug?: SocketClientDebugOptions<keyof T & string>;\n /** NEW: required heartbeat config */\n heartbeat: HeartbeatClientOptions<Ping, Pong>;\n};\n\ntype HandlerEntry<T extends EventMap, K extends keyof T & string> = {\n orig: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n wrapped: (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>) => void;\n errorWrapped: (e: unknown) => void;\n};\n\nexport class SocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap> {\n readonly socket: Socket;\n private readonly events: T;\n private readonly roomJoinEvent: string;\n private readonly roomLeaveEvent: string;\n private readonly environment: 'development' | 'production';\n private readonly debug: SocketClientDebugOptions<keyof T & string>;\n\n // heartbeat\n private readonly hb: Required<Omit<HeartbeatClientOptions<Ping, Pong>, 'pongSchema' | 'onPong'>> & {\n pongSchema?: z.ZodTypeAny;\n onPong?: HeartbeatClientOptions<Ping, Pong>['onPong'];\n };\n private hbTimer: ReturnType<typeof setInterval> | null = null;\n\n // stats\n private readonly roomCounts = new Map<string, number>();\n private readonly handlerMap = new Map<string, Set<HandlerEntry<T, any>>>();\n\n constructor(events: T, opts: SocketClientOptions<Ping, Pong, T>) {\n this.events = events;\n this.socket = io(opts.url, { autoConnect: true, ...opts.ioOptions });\n this.roomJoinEvent = opts.roomJoinEvent ?? 'room:join';\n this.roomLeaveEvent = opts.roomLeaveEvent ?? 'room:leave';\n this.environment = opts.environment ?? 'development';\n this.debug = opts.debug ?? {};\n // heartbeat fixed config\n const hb = opts.heartbeat;\n this.hb = {\n pingEvent: hb.pingEvent ?? 'sys:ping',\n pongEvent: hb.pongEvent ?? 'sys:pong',\n intervalMs: hb.intervalMs ?? 15_000,\n timeoutMs: hb.timeoutMs ?? 7_500,\n pingSchema: hb.pingSchema,\n makePingPayload: hb.makePingPayload,\n pongSchema: hb.pongSchema,\n onPong: hb.onPong,\n };\n\n /* socket lifecycle → connection bucket */\n this.socket.on('connect', () => {\n this.dbg({\n type: 'connection',\n phase: 'connect',\n id: this.socket.id ?? '',\n });\n this.startHeartbeat();\n });\n\n this.socket.on('reconnect', (attempt) => {\n this.dbg({\n type: 'connection',\n phase: 'reconnect',\n attempt,\n });\n });\n\n this.socket.on('disconnect', (reason) => {\n this.dbg({\n type: 'connection',\n phase: 'disconnect',\n reason: String(reason),\n });\n this.stopHeartbeat();\n });\n\n this.socket.on('connect_error', (err) => {\n this.dbg({\n type: 'connection',\n phase: 'connect_error',\n err: String(err),\n });\n });\n\n // wire pong listener → pingpong bucket\n this.socket.on(this.hb.pongEvent, (raw: any) => {\n const receivedAt = Date.now();\n const clientSentIso: string | undefined = raw?.clientEcho?.__clientSentAt;\n let latencyMs: number | undefined;\n\n if (clientSentIso) {\n const sent = Date.parse(clientSentIso);\n if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);\n }\n\n if (this.hb.pongSchema) {\n const ok = this.hb.pongSchema.safeParse(raw);\n if (!ok.success) return; // drop invalid\n }\n\n const latency = latencyMs ?? raw?.sinceMs ?? 0;\n\n this.dbg({\n type: 'heartbeat',\n action: 'pong_recv',\n latencyMs: latency,\n payload: raw,\n });\n\n this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });\n });\n }\n\n private dbg(e: SocketClientDebugEvent<any>) {\n const d = this.debug;\n if (!d.logger) return;\n if (!d[e.type]) return;\n if (d.only && 'event' in e && !d.only.includes(e.event as any)) return;\n d.logger(e);\n }\n\n /** internal stats snapshot */\n stats(): ClientStatsSnapshot {\n const rooms = Array.from(this.roomCounts.entries()).map(([room, count]) => ({ room, count }));\n const handlers = Array.from(this.handlerMap.entries()).map(([event, set]) => ({\n event,\n handlers: set.size,\n }));\n return {\n roomsCount: rooms.length,\n totalHandlers: handlers.reduce((a, b) => a + b.handlers, 0),\n rooms,\n handlers,\n };\n }\n\n private toArray(rooms?: string[] | string): string[] {\n return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n }\n\n private startHeartbeat() {\n this.stopHeartbeat();\n\n const tick = () => {\n const basePayload = this.hb.makePingPayload({ socket: this.socket }) ?? {};\n const candidate = { ...basePayload, __clientSentAt: new Date().toISOString() };\n\n const check = this.hb.pingSchema.safeParse(candidate);\n if (!check.success) {\n if (this.environment === 'development')\n console.warn('[socket] ping schema validation failed', check.error.issues);\n return;\n }\n\n const timer = setTimeout(() => {\n /* timeout, no-op here */\n }, this.hb.timeoutMs);\n\n this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {\n clearTimeout(timer);\n });\n\n this.dbg({\n type: 'heartbeat',\n action: 'ping_emit',\n payload: check.data,\n });\n };\n\n this.hbTimer = setInterval(tick, this.hb.intervalMs);\n tick();\n }\n\n private stopHeartbeat() {\n if (this.hbTimer) {\n clearInterval(this.hbTimer);\n this.hbTimer = null;\n }\n }\n\n emit<K extends keyof T & string>(\n event: K,\n payload: Payload<T, K>,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void,\n timeoutMs?: number,\n ): void {\n const schema = this.events[event].message;\n const parsed = schema.safeParse(payload);\n if (!parsed.success) throw new Error(`Invalid payload for \"${event}\": ${parsed.error.message}`);\n\n if (onAck) {\n this.socket.timeout(timeoutMs ?? this.hb.timeoutMs).emit(String(event), parsed.data, (ack: unknown) => {\n try {\n onAck(ack);\n } catch {\n /* noop */\n }\n });\n } else {\n this.socket.emit(String(event), parsed.data);\n }\n\n this.dbg({\n type: 'emit',\n event,\n metadata: this.debug.verbose ? metadata : undefined,\n });\n }\n\n joinRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toJoin: string[] = [];\n for (const r of list) {\n const next = (this.roomCounts.get(r) ?? 0) + 1;\n this.roomCounts.set(r, next);\n if (next === 1) toJoin.push(r);\n }\n if (toJoin.length > 0) {\n this.socket.emit(this.roomJoinEvent, { rooms: toJoin });\n this.dbg({ type: 'room', action: 'join', rooms: toJoin });\n }\n }\n\n leaveRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toLeave: string[] = [];\n for (const r of list) {\n const curr = this.roomCounts.get(r) ?? 0;\n const next = Math.max(0, curr - 1);\n if (next === 0 && curr > 0) toLeave.push(r);\n if (next === 0) this.roomCounts.delete(r);\n else this.roomCounts.set(r, next);\n }\n if (toLeave.length > 0) {\n this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });\n this.dbg({ type: 'room', action: 'leave', rooms: toLeave });\n }\n }\n\n on<K extends keyof T & string>(\n event: K,\n handler: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void,\n ): () => void {\n const schema = this.events[event].message;\n\n this.dbg({ type: 'register', action: 'register', event });\n\n const wrapped = (\n envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>,\n maybeAck?: (data?: unknown) => void,\n ) => {\n const maybeEnvelope = envelopeOrRaw as any;\n const rawData = maybeEnvelope?.data ?? maybeEnvelope;\n\n const parsed = schema.safeParse(rawData);\n if (!parsed.success) return;\n\n const receivedAt = new Date();\n const sentAt = maybeEnvelope?.sentAt ? new Date(maybeEnvelope.sentAt) : undefined;\n\n const meta = {\n envelope: {\n eventName: (maybeEnvelope?.eventName ?? event) as K,\n sentAt: maybeEnvelope?.sentAt ?? receivedAt.toISOString(),\n sentTo: maybeEnvelope?.sentTo ?? [],\n data: undefined,\n metadata: maybeEnvelope?.metadata,\n },\n ctx: {\n receivedAt,\n latencyMs: sentAt ? Math.max(0, receivedAt.getTime() - sentAt.getTime()) : undefined,\n nsp: (this.socket as any).nsp,\n socketId: this.socket.id,\n socket: this.socket,\n reply:\n typeof maybeAck === 'function'\n ? (d?: unknown) => {\n try {\n maybeAck(d);\n } catch {\n /* noop */\n }\n }\n : undefined,\n },\n } as const;\n\n this.dbg({\n type: 'receive',\n event,\n envelope: this.debug.verbose\n ? {\n eventName: meta.envelope.eventName,\n sentAt: meta.envelope.sentAt,\n sentTo: meta.envelope.sentTo,\n metadata: meta.envelope.metadata,\n }\n : undefined,\n });\n\n handler(parsed.data as any, meta as any);\n };\n\n const errorWrapped = (e: unknown) => {\n if (this.environment === 'development') {\n // eslint-disable-next-line no-console\n console.warn(`[socket] ${String(event)}:error`, e);\n }\n };\n\n this.socket.on(String(event), wrapped);\n this.socket.on(`${String(event)}:error`, errorWrapped);\n\n let set = this.handlerMap.get(String(event));\n if (!set) {\n set = new Set();\n this.handlerMap.set(String(event), set);\n }\n const entry: HandlerEntry<T, K> = { orig: handler, wrapped, errorWrapped };\n set.add(entry);\n\n return () => {\n this.socket.off(String(event), wrapped);\n this.socket.off(`${String(event)}:error`, errorWrapped);\n const s = this.handlerMap.get(String(event));\n if (s) {\n s.delete(entry);\n if (s.size === 0) this.handlerMap.delete(String(event));\n }\n this.dbg({ type: 'register', action: 'unregister', event });\n };\n }\n\n disconnect(): void {\n this.stopHeartbeat();\n this.socket.disconnect();\n this.dbg({ type: 'connection', phase: 'disconnect', reason: 'client_disconnect' });\n }\n\n connect(): void {\n this.socket.connect();\n this.dbg({ type: 'connection', phase: 'connect', id: this.socket.id ?? '' });\n }\n}\n\nexport * from './socket.client.context';\n","// socket.client.context.tsx\nimport * as React from 'react';\nimport { SocketClient, SocketClientOptions, EventMap, ClientCtx, Payload, ServerEnvelope } from './socket.client.index';\nimport { ZodType } from 'zod';\n\ntype SocketProviderProps<Ping extends ZodType, Pong extends ZodType, T extends EventMap> = React.PropsWithChildren<{\n events: T;\n options: SocketClientOptions<Ping, Pong, T>;\n}>;\n\nconst SocketCtx = React.createContext<SocketClient<ZodType, ZodType, any> | null>(null);\n\nexport function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n options,\n}: Omit<SocketProviderProps<Ping, Pong, T>, 'children'>) {\n return {\n SocketProvider: ({ children }: React.PropsWithChildren<{}>) => (\n <SocketProvider<Ping, Pong, T> events={events} options={options}>\n {children}\n </SocketProvider>\n ),\n useSocketClient: () => useSocketClient<Ping, Pong, T>(),\n useSocketConnection: <K extends keyof T & string>(\n args: Parameters<typeof useSocketConnection<T, K>>[0]\n ) => useSocketConnection<T, K>(args),\n };\n}\n\nfunction SocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n options,\n children,\n}: SocketProviderProps<Ping, Pong, T>) {\n const client = React.useMemo(() => new SocketClient(events, options), [events, options.url]);\n\n React.useEffect(() => {\n client.connect();\n return () => {\n client.disconnect();\n };\n }, []);\n\n return <SocketCtx.Provider value={client}>{children}</SocketCtx.Provider>;\n}\n\nfunction useSocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(): SocketClient<Ping, Pong, T> {\n const ctx = React.useContext(SocketCtx);\n if (!ctx) throw new Error('SocketClient not found. Wrap with <SocketProvider>.');\n return ctx as SocketClient<Ping, Pong, T>;\n}\n\ntype Rooms = string[] | string | undefined;\n\nexport type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string> = {\n event: K;\n rooms?: Rooms;\n onMessage: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n onCleanup?: () => void;\n autoJoin?: boolean;\n autoLeave?: boolean;\n deps?: React.DependencyList;\n};\n\nfunction useSocketConnection<T extends EventMap, K extends keyof T & string>(\n args: UseSocketConnectionArgs<T, K>,\n) {\n const { event, rooms, onMessage, onCleanup, autoJoin = true, autoLeave = true } = args;\n const client = useSocketClient<ZodType, ZodType, T>();\n\n const normalizedRooms = React.useMemo(\n () => (rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms]),\n [rooms],\n );\n\n React.useEffect(() => {\n if (autoJoin && normalizedRooms.length > 0) client.joinRooms(normalizedRooms);\n const unsubscribe = client.on(event, onMessage);\n\n return () => {\n unsubscribe();\n if (autoLeave && normalizedRooms.length > 0) client.leaveRooms(normalizedRooms);\n if (onCleanup) onCleanup();\n };\n }, args.deps ?? [client, event, onMessage, autoJoin, autoLeave, ...normalizedRooms]);\n}\n"],"mappings":"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","names":["useEndpoint"]}
|
package/dist/index.mjs
CHANGED
|
@@ -226,6 +226,7 @@ function createRouteClient(opts) {
|
|
|
226
226
|
try {
|
|
227
227
|
const out = await fetcher({ url, method });
|
|
228
228
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
229
|
+
rqOpts?.onReceive?.(parsed);
|
|
229
230
|
emit(
|
|
230
231
|
decorateDebugEvent(
|
|
231
232
|
{
|
|
@@ -291,6 +292,7 @@ function createRouteClient(opts) {
|
|
|
291
292
|
try {
|
|
292
293
|
const out = await fetcher({ url, method });
|
|
293
294
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
295
|
+
rqOpts?.onReceive?.(parsed);
|
|
294
296
|
emit(
|
|
295
297
|
decorateDebugEvent(
|
|
296
298
|
{
|
|
@@ -364,6 +366,7 @@ function createRouteClient(opts) {
|
|
|
364
366
|
try {
|
|
365
367
|
const out = await fetcher({ url, method, body: payload });
|
|
366
368
|
const parsed = zParse(out, leaf.cfg.outputSchema);
|
|
369
|
+
rqOpts?.onReceive?.(parsed);
|
|
367
370
|
emit(
|
|
368
371
|
decorateDebugEvent(
|
|
369
372
|
{
|
|
@@ -438,20 +441,16 @@ import { jsx } from "react/jsx-runtime";
|
|
|
438
441
|
var SocketCtx = React.createContext(null);
|
|
439
442
|
function buildSocketProvider({
|
|
440
443
|
events,
|
|
441
|
-
ping,
|
|
442
|
-
pong,
|
|
443
444
|
options
|
|
444
445
|
}) {
|
|
445
446
|
return {
|
|
446
|
-
SocketProvider: ({ children }) => /* @__PURE__ */ jsx(SocketProvider, { events,
|
|
447
|
+
SocketProvider: ({ children }) => /* @__PURE__ */ jsx(SocketProvider, { events, options, children }),
|
|
447
448
|
useSocketClient: () => useSocketClient(),
|
|
448
449
|
useSocketConnection: (args) => useSocketConnection(args)
|
|
449
450
|
};
|
|
450
451
|
}
|
|
451
452
|
function SocketProvider({
|
|
452
453
|
events,
|
|
453
|
-
ping,
|
|
454
|
-
pong,
|
|
455
454
|
options,
|
|
456
455
|
children
|
|
457
456
|
}) {
|
|
@@ -512,18 +511,37 @@ var SocketClient = class {
|
|
|
512
511
|
onPong: hb.onPong
|
|
513
512
|
};
|
|
514
513
|
this.socket.on("connect", () => {
|
|
515
|
-
this.dbg({
|
|
514
|
+
this.dbg({
|
|
515
|
+
type: "connection",
|
|
516
|
+
phase: "connect",
|
|
517
|
+
id: this.socket.id ?? ""
|
|
518
|
+
});
|
|
516
519
|
this.startHeartbeat();
|
|
517
520
|
});
|
|
518
|
-
this.socket.on("reconnect", (attempt) =>
|
|
521
|
+
this.socket.on("reconnect", (attempt) => {
|
|
522
|
+
this.dbg({
|
|
523
|
+
type: "connection",
|
|
524
|
+
phase: "reconnect",
|
|
525
|
+
attempt
|
|
526
|
+
});
|
|
527
|
+
});
|
|
519
528
|
this.socket.on("disconnect", (reason) => {
|
|
520
|
-
this.dbg({
|
|
529
|
+
this.dbg({
|
|
530
|
+
type: "connection",
|
|
531
|
+
phase: "disconnect",
|
|
532
|
+
reason: String(reason)
|
|
533
|
+
});
|
|
521
534
|
this.stopHeartbeat();
|
|
522
535
|
});
|
|
523
|
-
this.socket.on("connect_error", (err) =>
|
|
536
|
+
this.socket.on("connect_error", (err) => {
|
|
537
|
+
this.dbg({
|
|
538
|
+
type: "connection",
|
|
539
|
+
phase: "connect_error",
|
|
540
|
+
err: String(err)
|
|
541
|
+
});
|
|
542
|
+
});
|
|
524
543
|
this.socket.on(this.hb.pongEvent, (raw) => {
|
|
525
544
|
const receivedAt = Date.now();
|
|
526
|
-
const serverNowIso = raw?.serverNow;
|
|
527
545
|
const clientSentIso = raw?.clientEcho?.__clientSentAt;
|
|
528
546
|
let latencyMs;
|
|
529
547
|
if (clientSentIso) {
|
|
@@ -534,17 +552,21 @@ var SocketClient = class {
|
|
|
534
552
|
const ok = this.hb.pongSchema.safeParse(raw);
|
|
535
553
|
if (!ok.success) return;
|
|
536
554
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
555
|
+
const latency = latencyMs ?? raw?.sinceMs ?? 0;
|
|
556
|
+
this.dbg({
|
|
557
|
+
type: "heartbeat",
|
|
558
|
+
action: "pong_recv",
|
|
559
|
+
latencyMs: latency,
|
|
560
|
+
payload: raw
|
|
561
|
+
});
|
|
562
|
+
this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });
|
|
541
563
|
});
|
|
542
564
|
}
|
|
543
565
|
dbg(e) {
|
|
544
566
|
const d = this.debug;
|
|
545
567
|
if (!d.logger) return;
|
|
546
568
|
if (!d[e.type]) return;
|
|
547
|
-
if (d.only && !d.only.includes(e.event)) return;
|
|
569
|
+
if (d.only && "event" in e && !d.only.includes(e.event)) return;
|
|
548
570
|
d.logger(e);
|
|
549
571
|
}
|
|
550
572
|
/** internal stats snapshot */
|
|
@@ -571,7 +593,8 @@ var SocketClient = class {
|
|
|
571
593
|
const candidate = { ...basePayload, __clientSentAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
572
594
|
const check = this.hb.pingSchema.safeParse(candidate);
|
|
573
595
|
if (!check.success) {
|
|
574
|
-
if (this.environment === "development")
|
|
596
|
+
if (this.environment === "development")
|
|
597
|
+
console.warn("[socket] ping schema validation failed", check.error.issues);
|
|
575
598
|
return;
|
|
576
599
|
}
|
|
577
600
|
const timer = setTimeout(() => {
|
|
@@ -579,7 +602,11 @@ var SocketClient = class {
|
|
|
579
602
|
this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {
|
|
580
603
|
clearTimeout(timer);
|
|
581
604
|
});
|
|
582
|
-
this.dbg({
|
|
605
|
+
this.dbg({
|
|
606
|
+
type: "heartbeat",
|
|
607
|
+
action: "ping_emit",
|
|
608
|
+
payload: check.data
|
|
609
|
+
});
|
|
583
610
|
};
|
|
584
611
|
this.hbTimer = setInterval(tick, this.hb.intervalMs);
|
|
585
612
|
tick();
|
|
@@ -604,7 +631,11 @@ var SocketClient = class {
|
|
|
604
631
|
} else {
|
|
605
632
|
this.socket.emit(String(event), parsed.data);
|
|
606
633
|
}
|
|
607
|
-
this.dbg({
|
|
634
|
+
this.dbg({
|
|
635
|
+
type: "emit",
|
|
636
|
+
event,
|
|
637
|
+
metadata: this.debug.verbose ? metadata : void 0
|
|
638
|
+
});
|
|
608
639
|
}
|
|
609
640
|
joinRooms(rooms) {
|
|
610
641
|
const list = this.toArray(rooms);
|
|
@@ -616,7 +647,7 @@ var SocketClient = class {
|
|
|
616
647
|
}
|
|
617
648
|
if (toJoin.length > 0) {
|
|
618
649
|
this.socket.emit(this.roomJoinEvent, { rooms: toJoin });
|
|
619
|
-
this.dbg({ type: "join", rooms: toJoin });
|
|
650
|
+
this.dbg({ type: "room", action: "join", rooms: toJoin });
|
|
620
651
|
}
|
|
621
652
|
}
|
|
622
653
|
leaveRooms(rooms) {
|
|
@@ -631,12 +662,12 @@ var SocketClient = class {
|
|
|
631
662
|
}
|
|
632
663
|
if (toLeave.length > 0) {
|
|
633
664
|
this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });
|
|
634
|
-
this.dbg({ type: "leave", rooms: toLeave });
|
|
665
|
+
this.dbg({ type: "room", action: "leave", rooms: toLeave });
|
|
635
666
|
}
|
|
636
667
|
}
|
|
637
668
|
on(event, handler) {
|
|
638
669
|
const schema = this.events[event].message;
|
|
639
|
-
this.dbg({ type: "register", event });
|
|
670
|
+
this.dbg({ type: "register", action: "register", event });
|
|
640
671
|
const wrapped = (envelopeOrRaw, maybeAck) => {
|
|
641
672
|
const maybeEnvelope = envelopeOrRaw;
|
|
642
673
|
const rawData = maybeEnvelope?.data ?? maybeEnvelope;
|
|
@@ -664,10 +695,18 @@ var SocketClient = class {
|
|
|
664
695
|
} catch {
|
|
665
696
|
}
|
|
666
697
|
} : void 0
|
|
667
|
-
// NEW
|
|
668
698
|
}
|
|
669
699
|
};
|
|
670
|
-
this.dbg({
|
|
700
|
+
this.dbg({
|
|
701
|
+
type: "receive",
|
|
702
|
+
event,
|
|
703
|
+
envelope: this.debug.verbose ? {
|
|
704
|
+
eventName: meta.envelope.eventName,
|
|
705
|
+
sentAt: meta.envelope.sentAt,
|
|
706
|
+
sentTo: meta.envelope.sentTo,
|
|
707
|
+
metadata: meta.envelope.metadata
|
|
708
|
+
} : void 0
|
|
709
|
+
});
|
|
671
710
|
handler(parsed.data, meta);
|
|
672
711
|
};
|
|
673
712
|
const errorWrapped = (e) => {
|
|
@@ -692,17 +731,17 @@ var SocketClient = class {
|
|
|
692
731
|
s.delete(entry);
|
|
693
732
|
if (s.size === 0) this.handlerMap.delete(String(event));
|
|
694
733
|
}
|
|
695
|
-
this.dbg({ type: "unregister", event });
|
|
734
|
+
this.dbg({ type: "register", action: "unregister", event });
|
|
696
735
|
};
|
|
697
736
|
}
|
|
698
737
|
disconnect() {
|
|
699
738
|
this.stopHeartbeat();
|
|
700
739
|
this.socket.disconnect();
|
|
701
|
-
this.dbg({ type: "disconnect", reason: "client_disconnect" });
|
|
740
|
+
this.dbg({ type: "connection", phase: "disconnect", reason: "client_disconnect" });
|
|
702
741
|
}
|
|
703
742
|
connect() {
|
|
704
743
|
this.socket.connect();
|
|
705
|
-
this.dbg({ type: "connect", id: this.socket.id ?? "" });
|
|
744
|
+
this.dbg({ type: "connection", phase: "connect", id: this.socket.id ?? "" });
|
|
706
745
|
}
|
|
707
746
|
};
|
|
708
747
|
export {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts","../src/sockets/socket.client.index.ts","../src/sockets/socket.client.context.tsx"],"sourcesContent":["// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuildMeta,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n RouteClientDebugEvent,\n RouteClientDebugLogger,\n RouteClientDebugMode,\n RouteClientDebugOptions,\n RouteClientDebugToggleOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Debug logging --------------------------------------------------------------\nconst noopDebugLogger: RouteClientDebugLogger = () => {};\n\nconst defaultDebugLogger: RouteClientDebugLogger = (event: RouteClientDebugEvent) => {\n if (typeof console === 'undefined') return;\n const fn = console.debug ?? console.log;\n fn?.call(console, '[rrroutes-client]', event);\n};\n\nconst debugEventTypes: RouteClientDebugEvent['type'][] = [\n 'fetch',\n 'invalidate',\n 'setData',\n 'build',\n 'useEndpoint',\n];\n\ntype DebugEmitter<Names extends string> = {\n emit: (event: RouteClientDebugEvent, name?: Names) => void;\n mode: RouteClientDebugMode;\n};\n\nconst noopEmit = () => {};\n\nfunction createDebugEmitter<Names extends string>(\n option?: RouteClientDebugOptions<Names>,\n environment?: string,\n): DebugEmitter<Names> {\n const disabled: DebugEmitter<Names> = { emit: noopEmit, mode: 'minimal' };\n\n if (environment && environment.toLowerCase() === 'production') {\n return disabled;\n }\n\n if (!option) {\n return disabled;\n }\n if (option === true || option === 'minimal') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (option === 'complete') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'complete',\n };\n }\n if (typeof option === 'function') {\n return {\n emit: (event, name) => option(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (typeof option === 'object') {\n const toggles = option as RouteClientDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = debugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteClientDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger ?? defaultDebugLogger;\n const emit: DebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type)) return;\n if (onlySet) {\n if (!name || !onlySet.has(name)) return;\n }\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL plus normalized params/query payloads.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery, normalizedParams };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient<Names extends string = string>(\n opts: RouteClientOptions<Names>,\n): RouteClient<Names> {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n const environment =\n opts.environment ?? undefined;\n const { emit: emitDebug, mode: debugMode } = createDebugEmitter<Names>(opts.debug, environment);\n const isVerboseDebug = debugMode === 'complete';\n const decorateDebugEvent = <T extends RouteClientDebugEvent>(\n event: T,\n details?: Partial<RouteClientDebugEvent>,\n ): RouteClientDebugEvent => {\n if (!isVerboseDebug || !details) return event;\n return { ...event, ...details } as RouteClientDebugEvent;\n };\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n const queryKey = prefix as unknown as QueryKey;\n await queryClient.invalidateQueries({ queryKey, exact });\n emitDebug({ type: 'invalidate', key: queryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n meta?: BuildMeta<Names>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n const method = toUpper(leaf.method);\n const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;\n const debugName = meta?.name;\n const emit = (event: RouteClientDebugEvent) => emitDebug(event, debugName);\n emit({ type: 'build', leaf: leafLabel });\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = async (...tuple: ArgsTuple<L>) => {\n const queryKey = key(...tuple);\n await queryClient.invalidateQueries({ queryKey, exact: true });\n emit({ type: 'invalidate', key: queryKey, exact: true });\n };\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n emit({ type: 'setData', key: k });\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'infiniteGet' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const startedAt = Date.now();\n const detail = isVerboseDebug ? { params: normalizedParams, query: pageQuery } : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug ? { params: normalizedParams, query: pageQuery, output: parsed } : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'get' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const fetchEndpoint: BuiltMutation<L>['fetch'] = async (\n ...tupleWithBody: [...ArgsTuple<L>, InferBody<L>]\n ) => {\n if (tupleWithBody.length === 0) {\n throw new Error('Body is required when invoking a mutation fetch.');\n }\n const bodyIndex = tupleWithBody.length - 1;\n const tuple = tupleWithBody.slice(0, bodyIndex) as ArgsTuple<L>;\n const body = tupleWithBody[bodyIndex] as InferBody<L>;\n const args = extractArgs<L>(tuple);\n const params = (args as any)?.params as InferParams<L> | undefined;\n const query = (args as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'start',\n method,\n url,\n leaf: leafLabel,\n body: payload,\n },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method, body: payload });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n body: payload,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n };\n\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'mutation' });\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: (body: InferBody<L>) =>\n fetchEndpoint(...([...tuple, body] as [...ArgsTuple<L>, InferBody<L>])),\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n fetch: fetchEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient<Names>['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n","// socket.client.index.ts\n\nimport { io, Socket } from 'socket.io-client';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\nexport type EventMap = Record<string, SocketEvent>;\nexport type Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type ServerEnvelope<T extends EventMap, K extends keyof T & string> = {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n data?: Payload<T, K>;\n metadata?: Record<string, unknown>;\n};\n\nexport type ClientCtx = {\n receivedAt: Date;\n latencyMs?: number;\n nsp?: string;\n socketId?: string;\n rooms?: string[];\n socket?: Socket;\n reply?: (data?: unknown) => void;\n};\n\nexport type ClientStatsSnapshot = {\n roomsCount: number;\n totalHandlers: number;\n rooms: { room: string; count: number }[];\n handlers: { event: string; handlers: number }[];\n};\n\nexport type SocketClientDebugEvent<K extends string = string> =\n | { type: 'connect'; id: string }\n | { type: 'reconnect'; attempt: number }\n | { type: 'disconnect'; reason: string }\n | { type: 'connect_error'; err: string }\n | { type: 'register'; event: K }\n | { type: 'unregister'; event: K }\n | { type: 'emit'; event: K; metadata?: Record<string, unknown> }\n | { type: 'receive'; event: K; envelope?: { eventName: K; sentAt: string | Date; sentTo: string[]; metadata?: Record<string, unknown> } }\n | { type: 'join'; rooms: string[]; }\n | { type: 'leave'; rooms: string[]; }\n | { type: 'stats'; value: ClientStatsSnapshot }\n | { type: 'ping_emit'; payload: unknown }\n | { type: 'pong_recv'; latencyMs: number; payload?: unknown };\n\nexport type SocketClientDebugOptions<K extends string = string> = {\n verbose?: boolean;\n only?: K[];\n logger?: (e: SocketClientDebugEvent<K>) => void;\n} & {\n [P in SocketClientDebugEvent['type']]?: boolean;\n};\n\n/** === NEW: Heartbeat config (enforced) === */\nexport type HeartbeatClientOptions<Ping extends ZodType, Pong extends ZodType> = {\n /** Event names. Defaults are 'sys:ping' and 'sys:pong'. */\n pingEvent?: string;\n pongEvent?: string;\n /** Interval between pings. Default 15_000. */\n intervalMs?: number;\n /** Give up waiting for pong after this many ms. Default 7_500. */\n timeoutMs?: number;\n\n /** Schema of the ping payload you will emit. */\n pingSchema: Ping;\n /** Produce the ping payload on each tick. */\n makePingPayload: (ctx: { socket: Socket }) => NoInfer<z.infer<Ping>>;\n\n /** Optional validation of the pong payload you receive. */\n pongSchema?: Pong;\n /** Optional hook called on each pong. */\n onPong?: (args: { latencyMs: number; payload?: NoInfer<z.infer<Pong>>; socket: Socket }) => void;\n};\n\nexport type SocketClientOptions<Ping extends ZodType, Pong extends ZodType,T extends EventMap = EventMap> = {\n url: string;\n ioOptions?: Parameters<typeof io>[1];\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n environment?: 'development' | 'production';\n debug?: SocketClientDebugOptions<keyof T & string>;\n\n /** NEW: required heartbeat config */\n heartbeat: HeartbeatClientOptions<Ping, Pong>;\n};\n\ntype HandlerEntry<T extends EventMap, K extends keyof T & string> = {\n orig: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n wrapped: (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>) => void;\n errorWrapped: (e: unknown) => void;\n};\n\nexport class SocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap> {\n readonly socket: Socket;\n private readonly events: T;\n private readonly roomJoinEvent: string;\n private readonly roomLeaveEvent: string;\n private readonly environment: 'development' | 'production';\n private readonly debug: SocketClientDebugOptions<keyof T & string>;\n\n // NEW\n private readonly hb: Required<Omit<HeartbeatClientOptions<Ping, Pong>, 'pongSchema' | 'onPong'>> & {\n pongSchema?: z.ZodTypeAny;\n onPong?: HeartbeatClientOptions<Ping, Pong>['onPong'];\n };\n private hbTimer: ReturnType<typeof setInterval> | null = null;\n\n // stats\n private readonly roomCounts = new Map<string, number>();\n private readonly handlerMap = new Map<string, Set<HandlerEntry<T, any>>>();\n\n constructor(events: T, opts: SocketClientOptions<Ping, Pong, T>) {\n this.events = events;\n this.socket = io(opts.url, { autoConnect: true, ...opts.ioOptions });\n this.roomJoinEvent = opts.roomJoinEvent ?? 'room:join';\n this.roomLeaveEvent = opts.roomLeaveEvent ?? 'room:leave';\n this.environment = opts.environment ?? 'development';\n this.debug = opts.debug ?? {};\n // heartbeat\n const hb = opts.heartbeat;\n this.hb = {\n pingEvent: hb.pingEvent ?? 'sys:ping',\n pongEvent: hb.pongEvent ?? 'sys:pong',\n intervalMs: hb.intervalMs ?? 15_000,\n timeoutMs: hb.timeoutMs ?? 7_500,\n pingSchema: hb.pingSchema,\n makePingPayload: hb.makePingPayload,\n pongSchema: hb.pongSchema,\n onPong: hb.onPong,\n };\n\n // socket lifecycle\n this.socket.on('connect', () => {\n this.dbg( { type: 'connect', id: this.socket.id ?? '' });\n this.startHeartbeat();\n });\n this.socket.on('reconnect', (attempt) => this.dbg( { type: 'reconnect', attempt }));\n this.socket.on('disconnect', (reason) => {\n this.dbg( { type: 'disconnect', reason: String(reason) });\n this.stopHeartbeat();\n });\n this.socket.on('connect_error', (err) =>this.dbg( { type: 'connect_error', err: String(err) }));\n\n // wire pong listener\n this.socket.on(this.hb.pongEvent, (raw: any) => {\n const receivedAt = Date.now();\n const serverNowIso: string | undefined = raw?.serverNow;\n const clientSentIso: string | undefined = raw?.clientEcho?.__clientSentAt;\n let latencyMs: number | undefined;\n\n if (clientSentIso) {\n const sent = Date.parse(clientSentIso);\n if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);\n }\n\n if (this.hb.pongSchema) {\n const ok = this.hb.pongSchema.safeParse(raw);\n if (!ok.success) return; // drop invalid\n }\n\n if (this.debug.logger && this.debug.pong_recv) {\n this.debug.logger({ type: 'pong_recv', latencyMs: latencyMs ?? raw?.sinceMs ?? 0, payload: raw });\n }\n this.hb.onPong?.({ latencyMs: latencyMs ?? raw?.sinceMs ?? 0, payload: raw, socket: this.socket });\n });\n }\n\n dbg(\n e: SocketClientDebugEvent<any>\n ){\n const d = this.debug;\n if (!d.logger) return;\n if (!d[e.type]) return;\n if (d.only && !d.only.includes((e as any).event)) return;\n d.logger(e);\n };\n\n /** internal stats snapshot */\n stats(): ClientStatsSnapshot {\n const rooms = Array.from(this.roomCounts.entries()).map(([room, count]) => ({ room, count }));\n const handlers = Array.from(this.handlerMap.entries()).map(([event, set]) => ({\n event,\n handlers: set.size,\n }));\n return {\n roomsCount: rooms.length,\n totalHandlers: handlers.reduce((a, b) => a + b.handlers, 0),\n rooms,\n handlers,\n };\n }\n\n private toArray(rooms?: string[] | string): string[] {\n return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n }\n\n private startHeartbeat() {\n this.stopHeartbeat();\n const tick = () => {\n const basePayload = this.hb.makePingPayload({ socket: this.socket }) ?? {};\n const candidate = { ...basePayload, __clientSentAt: new Date().toISOString() };\n\n const check = this.hb.pingSchema.safeParse(candidate);\n if (!check.success) {\n if (this.environment === 'development') console.warn('[socket] ping schema validation failed', check.error.issues);\n return;\n }\n\n // emit with ack timeout support\n const timer = setTimeout(() => {\n /* timeout, no-op here. consumer can observe missing pong if desired */\n }, this.hb.timeoutMs);\n\n this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {\n clearTimeout(timer);\n });\n\n this.dbg({ type: 'ping_emit', payload: check.data });\n\n };\n\n this.hbTimer = setInterval(tick, this.hb.intervalMs);\n // fire first ping immediately\n tick();\n }\n\n private stopHeartbeat() {\n if (this.hbTimer) {\n clearInterval(this.hbTimer);\n this.hbTimer = null;\n }\n }\n\nemit<K extends keyof T & string>(\n event: K,\n payload: Payload<T, K>,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void, \n timeoutMs?: number \n): void {\n const schema = this.events[event].message;\n const parsed = schema.safeParse(payload);\n if (!parsed.success) throw new Error(`Invalid payload for \"${event}\": ${parsed.error.message}`);\n\n if (onAck) {\n this.socket.timeout(timeoutMs ?? this.hb.timeoutMs).emit(String(event), parsed.data, (ack: unknown) => {\n try { onAck(ack); } catch { /* noop */ }\n });\n } else {\n this.socket.emit(String(event), parsed.data);\n }\n\n this.dbg( { type: 'emit', event, metadata: this.debug.verbose ? metadata : undefined });\n}\n\n\n joinRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toJoin: string[] = [];\n for (const r of list) {\n const next = (this.roomCounts.get(r) ?? 0) + 1;\n this.roomCounts.set(r, next);\n if (next === 1) toJoin.push(r);\n }\n if (toJoin.length > 0) {\n this.socket.emit(this.roomJoinEvent, { rooms: toJoin });\n this.dbg({ type: 'join', rooms: toJoin });\n }\n }\n\n leaveRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toLeave: string[] = [];\n for (const r of list) {\n const curr = this.roomCounts.get(r) ?? 0;\n const next = Math.max(0, curr - 1);\n if (next === 0 && curr > 0) toLeave.push(r);\n if (next === 0) this.roomCounts.delete(r);\n else this.roomCounts.set(r, next);\n }\n if (toLeave.length > 0) {\n this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });\n this.dbg({ type: 'leave', rooms: toLeave });\n }\n }\n\n on<K extends keyof T & string>(\n event: K,\n handler: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void,\n ): () => void {\n const schema = this.events[event].message;\n\n this.dbg( { type: 'register', event });\n\nconst wrapped = (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>, maybeAck?: (data?: unknown) => void) => {\n const maybeEnvelope = envelopeOrRaw as any;\n const rawData = maybeEnvelope?.data ?? maybeEnvelope;\n\n const parsed = schema.safeParse(rawData);\n if (!parsed.success) return;\n\n const receivedAt = new Date();\n const sentAt = maybeEnvelope?.sentAt ? new Date(maybeEnvelope.sentAt) : undefined;\n\n const meta = {\n envelope: {\n eventName: (maybeEnvelope?.eventName ?? event) as K,\n sentAt: maybeEnvelope?.sentAt ?? receivedAt.toISOString(),\n sentTo: maybeEnvelope?.sentTo ?? [],\n data: undefined,\n metadata: maybeEnvelope?.metadata,\n },\n ctx: {\n receivedAt,\n latencyMs: sentAt ? Math.max(0, receivedAt.getTime() - sentAt.getTime()) : undefined,\n nsp: (this.socket as any).nsp,\n socketId: this.socket.id,\n socket: this.socket,\n reply: typeof maybeAck === 'function' ? (d?: unknown) => { try { maybeAck(d); } catch { /* noop */ } } : undefined, // NEW\n },\n } as const;\n this.dbg( { type: 'receive', event, envelope: this.debug.verbose ? { eventName: meta.envelope.eventName, sentAt: meta.envelope.sentAt, sentTo: meta.envelope.sentTo, metadata: meta.envelope.metadata } : undefined });\n\n handler(parsed.data as any, meta as any);\n};\n\n\n const errorWrapped = (e: unknown) => {\n if (this.environment === 'development') {\n // eslint-disable-next-line no-console\n console.warn(`[socket] ${String(event)}:error`, e);\n }\n };\n\n this.socket.on(String(event), wrapped);\n this.socket.on(`${String(event)}:error`, errorWrapped);\n\n let set = this.handlerMap.get(String(event));\n if (!set) {\n set = new Set();\n this.handlerMap.set(String(event), set);\n }\n const entry: HandlerEntry<T, K> = { orig: handler, wrapped, errorWrapped };\n set.add(entry);\n\n return () => {\n this.socket.off(String(event), wrapped);\n this.socket.off(`${String(event)}:error`, errorWrapped);\n const s = this.handlerMap.get(String(event));\n if (s) {\n s.delete(entry);\n if (s.size === 0) this.handlerMap.delete(String(event));\n }\n this.dbg( { type: 'unregister', event });\n };\n }\n\n disconnect(): void {\n this.stopHeartbeat();\n this.socket.disconnect();\n this.dbg( { type: 'disconnect', reason: 'client_disconnect' });\n }\n\n connect(): void {\n this.socket.connect();\n this.dbg( { type: 'connect', id: this.socket.id ?? '' });\n }\n}\n\nexport * from './socket.client.context';","// socket.client.context.tsx\nimport * as React from 'react';\nimport { SocketClient, SocketClientOptions, EventMap, ClientCtx, Payload, ServerEnvelope } from './socket.client.index';\nimport { ZodType } from 'zod';\n\ntype SocketProviderProps<Ping extends ZodType, Pong extends ZodType, T extends EventMap> = React.PropsWithChildren<{\n events: T;\n ping: Ping;\n pong: Pong;\n options: SocketClientOptions<Ping, Pong, T>;\n}>;\n\nconst SocketCtx = React.createContext<SocketClient<ZodType, ZodType, any> | null>(null);\n\nexport function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n ping,\n pong,\n options,\n}: Omit<SocketProviderProps<Ping, Pong, T>, 'children'>) {\n return {\n SocketProvider: ({ children }: React.PropsWithChildren<{}>) => (\n <SocketProvider<Ping, Pong, T> events={events} ping={ping} pong={pong} options={options}>\n {children}\n </SocketProvider>\n ),\n useSocketClient: () => useSocketClient<Ping, Pong, T>(),\n useSocketConnection: <K extends keyof T & string>(\n args: Parameters<typeof useSocketConnection<T, K>>[0]\n ) => useSocketConnection<T, K>(args),\n };\n}\n\nfunction SocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n ping,\n pong,\n options,\n children,\n}: SocketProviderProps<Ping, Pong, T>) {\n const client = React.useMemo(() => new SocketClient(events, options), [events, options.url]);\n\n React.useEffect(() => {\n client.connect();\n return () => {\n client.disconnect();\n };\n }, []);\n\n return <SocketCtx.Provider value={client}>{children}</SocketCtx.Provider>;\n}\n\nfunction useSocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(): SocketClient<Ping, Pong, T> {\n const ctx = React.useContext(SocketCtx);\n if (!ctx) throw new Error('SocketClient not found. Wrap with <SocketProvider>.');\n return ctx as SocketClient<Ping, Pong, T>;\n}\n\ntype Rooms = string[] | string | undefined;\n\nexport type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string> = {\n event: K;\n rooms?: Rooms;\n onMessage: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n onCleanup?: () => void;\n autoJoin?: boolean;\n autoLeave?: boolean;\n deps?: React.DependencyList;\n};\n\nfunction useSocketConnection<T extends EventMap, K extends keyof T & string>(\n args: UseSocketConnectionArgs<T, K>,\n) {\n const { event, rooms, onMessage, onCleanup, autoJoin = true, autoLeave = true } = args;\n const client = useSocketClient<ZodType, ZodType, T>();\n\n const normalizedRooms = React.useMemo(\n () => (rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms]),\n [rooms],\n );\n\n React.useEffect(() => {\n if (autoJoin && normalizedRooms.length > 0) client.joinRooms(normalizedRooms);\n const unsubscribe = client.on(event, onMessage);\n\n return () => {\n unsubscribe();\n if (autoLeave && normalizedRooms.length > 0) client.leaveRooms(normalizedRooms);\n if (onCleanup) onCleanup();\n };\n }, args.deps ?? [client, event, onMessage, autoJoin, autoLeave, ...normalizedRooms]);\n}\n"],"mappings":"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","names":["useEndpoint"]}
|
|
1
|
+
{"version":3,"sources":["../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts","../src/sockets/socket.client.index.ts","../src/sockets/socket.client.context.tsx"],"sourcesContent":["// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuildMeta,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n RouteClientDebugEvent,\n RouteClientDebugLogger,\n RouteClientDebugMode,\n RouteClientDebugOptions,\n RouteClientDebugToggleOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Debug logging --------------------------------------------------------------\nconst noopDebugLogger: RouteClientDebugLogger = () => {};\n\nconst defaultDebugLogger: RouteClientDebugLogger = (event: RouteClientDebugEvent) => {\n if (typeof console === 'undefined') return;\n const fn = console.debug ?? console.log;\n fn?.call(console, '[rrroutes-client]', event);\n};\n\nconst debugEventTypes: RouteClientDebugEvent['type'][] = [\n 'fetch',\n 'invalidate',\n 'setData',\n 'build',\n 'useEndpoint',\n];\n\ntype DebugEmitter<Names extends string> = {\n emit: (event: RouteClientDebugEvent, name?: Names) => void;\n mode: RouteClientDebugMode;\n};\n\nconst noopEmit = () => {};\n\nfunction createDebugEmitter<Names extends string>(\n option?: RouteClientDebugOptions<Names>,\n environment?: string,\n): DebugEmitter<Names> {\n const disabled: DebugEmitter<Names> = { emit: noopEmit, mode: 'minimal' };\n\n if (environment && environment.toLowerCase() === 'production') {\n return disabled;\n }\n\n if (!option) {\n return disabled;\n }\n if (option === true || option === 'minimal') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (option === 'complete') {\n return {\n emit: (event, name) => defaultDebugLogger(name ? { ...event, name } : event),\n mode: 'complete',\n };\n }\n if (typeof option === 'function') {\n return {\n emit: (event, name) => option(name ? { ...event, name } : event),\n mode: 'minimal',\n };\n }\n if (typeof option === 'object') {\n const toggles = option as RouteClientDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = debugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteClientDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger ?? defaultDebugLogger;\n const emit: DebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type)) return;\n if (onlySet) {\n if (!name || !onlySet.has(name)) return;\n }\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL plus normalized params/query payloads.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery, normalizedParams };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient<Names extends string = string>(\n opts: RouteClientOptions<Names>,\n): RouteClient<Names> {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n const environment =\n opts.environment ?? undefined;\n const { emit: emitDebug, mode: debugMode } = createDebugEmitter<Names>(opts.debug, environment);\n const isVerboseDebug = debugMode === 'complete';\n const decorateDebugEvent = <T extends RouteClientDebugEvent>(\n event: T,\n details?: Partial<RouteClientDebugEvent>,\n ): RouteClientDebugEvent => {\n if (!isVerboseDebug || !details) return event;\n return { ...event, ...details } as RouteClientDebugEvent;\n };\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n const queryKey = prefix as unknown as QueryKey;\n await queryClient.invalidateQueries({ queryKey, exact });\n emitDebug({ type: 'invalidate', key: queryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n meta?: BuildMeta<Names>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n const method = toUpper(leaf.method);\n const leafLabel = `${leaf.method.toUpperCase()} ${String(leaf.path)}`;\n const debugName = meta?.name;\n const emit = (event: RouteClientDebugEvent) => emitDebug(event, debugName);\n emit({ type: 'build', leaf: leafLabel });\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = async (...tuple: ArgsTuple<L>) => {\n const queryKey = key(...tuple);\n await queryClient.invalidateQueries({ queryKey, exact: true });\n emit({ type: 'invalidate', key: queryKey, exact: true });\n };\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n emit({ type: 'setData', key: k });\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'infiniteGet' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>,\n unknown,\n InfiniteData<InferOutput<L>>,\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const startedAt = Date.now();\n const detail = isVerboseDebug ? { params: normalizedParams, query: pageQuery } : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as InfiniteBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug ? { params: normalizedParams, query: pageQuery, output: parsed } : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'get' });\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n { type: 'fetch', stage: 'start', method, url, leaf: leafLabel },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as QueryBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n },\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const fetchEndpoint: BuiltMutation<L>['fetch'] = async (\n ...tupleWithBody: [...ArgsTuple<L>, InferBody<L>]\n ) => {\n if (tupleWithBody.length === 0) {\n throw new Error('Body is required when invoking a mutation fetch.');\n }\n const bodyIndex = tupleWithBody.length - 1;\n const tuple = tupleWithBody.slice(0, bodyIndex) as ArgsTuple<L>;\n const body = tupleWithBody[bodyIndex] as InferBody<L>;\n const args = extractArgs<L>(tuple);\n const params = (args as any)?.params as InferParams<L> | undefined;\n const query = (args as any)?.query as InferQuery<L> | undefined;\n\n const { url, normalizedQuery, normalizedParams } = buildUrl(leaf, baseUrl, params, query);\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const startedAt = Date.now();\n const detail = isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery }\n : undefined;\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'start',\n method,\n url,\n leaf: leafLabel,\n body: payload,\n },\n detail,\n ),\n );\n try {\n const out = await fetcher<unknown>({ url, method, body: payload });\n const parsed = zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n\n // call onReceive after fetch and validation\n (rqOpts as MutationBuildOptionsFor<L> & { onReceive?: (data: InferOutput<L>) => void })\n ?.onReceive?.(parsed);\n\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'success',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? { params: normalizedParams, query: normalizedQuery, output: parsed }\n : undefined,\n ),\n );\n return parsed;\n } catch (error) {\n emit(\n decorateDebugEvent(\n {\n type: 'fetch',\n stage: 'error',\n method,\n url,\n leaf: leafLabel,\n durationMs: Date.now() - startedAt,\n body: payload,\n error,\n },\n detail,\n ),\n );\n throw error;\n }\n };\n\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n emit({ type: 'useEndpoint', leaf: leafLabel, variant: 'mutation' });\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: (body: InferBody<L>) =>\n fetchEndpoint(...([...tuple, body] as [...ArgsTuple<L>, InferBody<L>])),\n }, queryClient);\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n fetch: fetchEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient<Names>['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n","// socket.client.index.ts\n\nimport { io, Socket } from 'socket.io-client';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\nexport type EventMap = Record<string, SocketEvent>;\nexport type Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type ServerEnvelope<T extends EventMap, K extends keyof T & string> = {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n data?: Payload<T, K>;\n metadata?: Record<string, unknown>;\n};\n\nexport type ClientCtx = {\n receivedAt: Date;\n latencyMs?: number;\n nsp?: string;\n socketId?: string;\n rooms?: string[];\n socket?: Socket;\n reply?: (data?: unknown) => void;\n};\n\nexport type ClientStatsSnapshot = {\n roomsCount: number;\n totalHandlers: number;\n rooms: { room: string; count: number }[];\n handlers: { event: string; handlers: number }[];\n};\n\n// helper, since original code used NoInfer\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\n/**\n * Merged/logically grouped debug events.\n *\n * Buckets:\n * - connection: connect, reconnect, disconnect, connect_error\n * - register: register, unregister\n * - pingpong: ping_emit, pong_recv\n * - room: join, leave\n * - emit\n * - receive\n * - stats\n */\nexport type SocketClientDebugEvent<K extends string = string> =\n | {\n type: 'connection';\n phase: 'connect' | 'reconnect' | 'disconnect' | 'connect_error';\n id?: string;\n attempt?: number;\n reason?: string;\n err?: string;\n }\n | {\n type: 'register';\n action: 'register' | 'unregister';\n event: K;\n }\n | {\n type: 'heartbeat';\n action: 'ping_emit' | 'pong_recv';\n latencyMs?: number;\n payload?: unknown;\n }\n | {\n type: 'room';\n action: 'join' | 'leave';\n rooms: string[];\n }\n | {\n type: 'emit';\n event: K;\n metadata?: Record<string, unknown>;\n }\n | {\n type: 'receive';\n event: K;\n envelope?: {\n eventName: K;\n sentAt: string | Date;\n sentTo: string[];\n metadata?: Record<string, unknown>;\n };\n };\n\nexport type SocketClientDebugOptions<K extends string = string> = {\n verbose?: boolean;\n only?: K[];\n logger?: (e: SocketClientDebugEvent<K>) => void;\n} & {\n [P in SocketClientDebugEvent['type']]?: boolean;\n};\n\n/** === NEW: Heartbeat config (enforced) === */\nexport type HeartbeatClientOptions<Ping extends ZodType, Pong extends ZodType> = {\n /** Event names. Defaults are 'sys:ping' and 'sys:pong'. */\n pingEvent?: string;\n pongEvent?: string;\n /** Interval between pings. Default 15_000. */\n intervalMs?: number;\n /** Give up waiting for pong after this many ms. Default 7_500. */\n timeoutMs?: number;\n\n /** Schema of the ping payload you will emit. */\n pingSchema: Ping;\n /** Produce the ping payload on each tick. */\n makePingPayload: (ctx: { socket: Socket }) => NoInfer<z.infer<Ping>>;\n\n /** Optional validation of the pong payload you receive. */\n pongSchema?: Pong;\n /** Optional hook called on each pong. */\n onPong?: (args: { latencyMs: number; payload?: NoInfer<z.infer<Pong>>; socket: Socket }) => void;\n};\n\nexport type SocketClientOptions<\n Ping extends ZodType,\n Pong extends ZodType,\n T extends EventMap = EventMap\n> = {\n url: string;\n ioOptions?: Parameters<typeof io>[1];\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n environment?: 'development' | 'production';\n debug?: SocketClientDebugOptions<keyof T & string>;\n /** NEW: required heartbeat config */\n heartbeat: HeartbeatClientOptions<Ping, Pong>;\n};\n\ntype HandlerEntry<T extends EventMap, K extends keyof T & string> = {\n orig: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n wrapped: (envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>) => void;\n errorWrapped: (e: unknown) => void;\n};\n\nexport class SocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap> {\n readonly socket: Socket;\n private readonly events: T;\n private readonly roomJoinEvent: string;\n private readonly roomLeaveEvent: string;\n private readonly environment: 'development' | 'production';\n private readonly debug: SocketClientDebugOptions<keyof T & string>;\n\n // heartbeat\n private readonly hb: Required<Omit<HeartbeatClientOptions<Ping, Pong>, 'pongSchema' | 'onPong'>> & {\n pongSchema?: z.ZodTypeAny;\n onPong?: HeartbeatClientOptions<Ping, Pong>['onPong'];\n };\n private hbTimer: ReturnType<typeof setInterval> | null = null;\n\n // stats\n private readonly roomCounts = new Map<string, number>();\n private readonly handlerMap = new Map<string, Set<HandlerEntry<T, any>>>();\n\n constructor(events: T, opts: SocketClientOptions<Ping, Pong, T>) {\n this.events = events;\n this.socket = io(opts.url, { autoConnect: true, ...opts.ioOptions });\n this.roomJoinEvent = opts.roomJoinEvent ?? 'room:join';\n this.roomLeaveEvent = opts.roomLeaveEvent ?? 'room:leave';\n this.environment = opts.environment ?? 'development';\n this.debug = opts.debug ?? {};\n // heartbeat fixed config\n const hb = opts.heartbeat;\n this.hb = {\n pingEvent: hb.pingEvent ?? 'sys:ping',\n pongEvent: hb.pongEvent ?? 'sys:pong',\n intervalMs: hb.intervalMs ?? 15_000,\n timeoutMs: hb.timeoutMs ?? 7_500,\n pingSchema: hb.pingSchema,\n makePingPayload: hb.makePingPayload,\n pongSchema: hb.pongSchema,\n onPong: hb.onPong,\n };\n\n /* socket lifecycle → connection bucket */\n this.socket.on('connect', () => {\n this.dbg({\n type: 'connection',\n phase: 'connect',\n id: this.socket.id ?? '',\n });\n this.startHeartbeat();\n });\n\n this.socket.on('reconnect', (attempt) => {\n this.dbg({\n type: 'connection',\n phase: 'reconnect',\n attempt,\n });\n });\n\n this.socket.on('disconnect', (reason) => {\n this.dbg({\n type: 'connection',\n phase: 'disconnect',\n reason: String(reason),\n });\n this.stopHeartbeat();\n });\n\n this.socket.on('connect_error', (err) => {\n this.dbg({\n type: 'connection',\n phase: 'connect_error',\n err: String(err),\n });\n });\n\n // wire pong listener → pingpong bucket\n this.socket.on(this.hb.pongEvent, (raw: any) => {\n const receivedAt = Date.now();\n const clientSentIso: string | undefined = raw?.clientEcho?.__clientSentAt;\n let latencyMs: number | undefined;\n\n if (clientSentIso) {\n const sent = Date.parse(clientSentIso);\n if (!Number.isNaN(sent)) latencyMs = Math.max(0, receivedAt - sent);\n }\n\n if (this.hb.pongSchema) {\n const ok = this.hb.pongSchema.safeParse(raw);\n if (!ok.success) return; // drop invalid\n }\n\n const latency = latencyMs ?? raw?.sinceMs ?? 0;\n\n this.dbg({\n type: 'heartbeat',\n action: 'pong_recv',\n latencyMs: latency,\n payload: raw,\n });\n\n this.hb.onPong?.({ latencyMs: latency, payload: raw, socket: this.socket });\n });\n }\n\n private dbg(e: SocketClientDebugEvent<any>) {\n const d = this.debug;\n if (!d.logger) return;\n if (!d[e.type]) return;\n if (d.only && 'event' in e && !d.only.includes(e.event as any)) return;\n d.logger(e);\n }\n\n /** internal stats snapshot */\n stats(): ClientStatsSnapshot {\n const rooms = Array.from(this.roomCounts.entries()).map(([room, count]) => ({ room, count }));\n const handlers = Array.from(this.handlerMap.entries()).map(([event, set]) => ({\n event,\n handlers: set.size,\n }));\n return {\n roomsCount: rooms.length,\n totalHandlers: handlers.reduce((a, b) => a + b.handlers, 0),\n rooms,\n handlers,\n };\n }\n\n private toArray(rooms?: string[] | string): string[] {\n return rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n }\n\n private startHeartbeat() {\n this.stopHeartbeat();\n\n const tick = () => {\n const basePayload = this.hb.makePingPayload({ socket: this.socket }) ?? {};\n const candidate = { ...basePayload, __clientSentAt: new Date().toISOString() };\n\n const check = this.hb.pingSchema.safeParse(candidate);\n if (!check.success) {\n if (this.environment === 'development')\n console.warn('[socket] ping schema validation failed', check.error.issues);\n return;\n }\n\n const timer = setTimeout(() => {\n /* timeout, no-op here */\n }, this.hb.timeoutMs);\n\n this.socket.timeout(this.hb.timeoutMs).emit(this.hb.pingEvent, { payload: check.data }, () => {\n clearTimeout(timer);\n });\n\n this.dbg({\n type: 'heartbeat',\n action: 'ping_emit',\n payload: check.data,\n });\n };\n\n this.hbTimer = setInterval(tick, this.hb.intervalMs);\n tick();\n }\n\n private stopHeartbeat() {\n if (this.hbTimer) {\n clearInterval(this.hbTimer);\n this.hbTimer = null;\n }\n }\n\n emit<K extends keyof T & string>(\n event: K,\n payload: Payload<T, K>,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void,\n timeoutMs?: number,\n ): void {\n const schema = this.events[event].message;\n const parsed = schema.safeParse(payload);\n if (!parsed.success) throw new Error(`Invalid payload for \"${event}\": ${parsed.error.message}`);\n\n if (onAck) {\n this.socket.timeout(timeoutMs ?? this.hb.timeoutMs).emit(String(event), parsed.data, (ack: unknown) => {\n try {\n onAck(ack);\n } catch {\n /* noop */\n }\n });\n } else {\n this.socket.emit(String(event), parsed.data);\n }\n\n this.dbg({\n type: 'emit',\n event,\n metadata: this.debug.verbose ? metadata : undefined,\n });\n }\n\n joinRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toJoin: string[] = [];\n for (const r of list) {\n const next = (this.roomCounts.get(r) ?? 0) + 1;\n this.roomCounts.set(r, next);\n if (next === 1) toJoin.push(r);\n }\n if (toJoin.length > 0) {\n this.socket.emit(this.roomJoinEvent, { rooms: toJoin });\n this.dbg({ type: 'room', action: 'join', rooms: toJoin });\n }\n }\n\n leaveRooms(rooms?: string[] | string): void {\n const list = this.toArray(rooms);\n const toLeave: string[] = [];\n for (const r of list) {\n const curr = this.roomCounts.get(r) ?? 0;\n const next = Math.max(0, curr - 1);\n if (next === 0 && curr > 0) toLeave.push(r);\n if (next === 0) this.roomCounts.delete(r);\n else this.roomCounts.set(r, next);\n }\n if (toLeave.length > 0) {\n this.socket.emit(this.roomLeaveEvent, { rooms: toLeave });\n this.dbg({ type: 'room', action: 'leave', rooms: toLeave });\n }\n }\n\n on<K extends keyof T & string>(\n event: K,\n handler: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void,\n ): () => void {\n const schema = this.events[event].message;\n\n this.dbg({ type: 'register', action: 'register', event });\n\n const wrapped = (\n envelopeOrRaw: ServerEnvelope<T, K> | Payload<T, K>,\n maybeAck?: (data?: unknown) => void,\n ) => {\n const maybeEnvelope = envelopeOrRaw as any;\n const rawData = maybeEnvelope?.data ?? maybeEnvelope;\n\n const parsed = schema.safeParse(rawData);\n if (!parsed.success) return;\n\n const receivedAt = new Date();\n const sentAt = maybeEnvelope?.sentAt ? new Date(maybeEnvelope.sentAt) : undefined;\n\n const meta = {\n envelope: {\n eventName: (maybeEnvelope?.eventName ?? event) as K,\n sentAt: maybeEnvelope?.sentAt ?? receivedAt.toISOString(),\n sentTo: maybeEnvelope?.sentTo ?? [],\n data: undefined,\n metadata: maybeEnvelope?.metadata,\n },\n ctx: {\n receivedAt,\n latencyMs: sentAt ? Math.max(0, receivedAt.getTime() - sentAt.getTime()) : undefined,\n nsp: (this.socket as any).nsp,\n socketId: this.socket.id,\n socket: this.socket,\n reply:\n typeof maybeAck === 'function'\n ? (d?: unknown) => {\n try {\n maybeAck(d);\n } catch {\n /* noop */\n }\n }\n : undefined,\n },\n } as const;\n\n this.dbg({\n type: 'receive',\n event,\n envelope: this.debug.verbose\n ? {\n eventName: meta.envelope.eventName,\n sentAt: meta.envelope.sentAt,\n sentTo: meta.envelope.sentTo,\n metadata: meta.envelope.metadata,\n }\n : undefined,\n });\n\n handler(parsed.data as any, meta as any);\n };\n\n const errorWrapped = (e: unknown) => {\n if (this.environment === 'development') {\n // eslint-disable-next-line no-console\n console.warn(`[socket] ${String(event)}:error`, e);\n }\n };\n\n this.socket.on(String(event), wrapped);\n this.socket.on(`${String(event)}:error`, errorWrapped);\n\n let set = this.handlerMap.get(String(event));\n if (!set) {\n set = new Set();\n this.handlerMap.set(String(event), set);\n }\n const entry: HandlerEntry<T, K> = { orig: handler, wrapped, errorWrapped };\n set.add(entry);\n\n return () => {\n this.socket.off(String(event), wrapped);\n this.socket.off(`${String(event)}:error`, errorWrapped);\n const s = this.handlerMap.get(String(event));\n if (s) {\n s.delete(entry);\n if (s.size === 0) this.handlerMap.delete(String(event));\n }\n this.dbg({ type: 'register', action: 'unregister', event });\n };\n }\n\n disconnect(): void {\n this.stopHeartbeat();\n this.socket.disconnect();\n this.dbg({ type: 'connection', phase: 'disconnect', reason: 'client_disconnect' });\n }\n\n connect(): void {\n this.socket.connect();\n this.dbg({ type: 'connection', phase: 'connect', id: this.socket.id ?? '' });\n }\n}\n\nexport * from './socket.client.context';\n","// socket.client.context.tsx\nimport * as React from 'react';\nimport { SocketClient, SocketClientOptions, EventMap, ClientCtx, Payload, ServerEnvelope } from './socket.client.index';\nimport { ZodType } from 'zod';\n\ntype SocketProviderProps<Ping extends ZodType, Pong extends ZodType, T extends EventMap> = React.PropsWithChildren<{\n events: T;\n options: SocketClientOptions<Ping, Pong, T>;\n}>;\n\nconst SocketCtx = React.createContext<SocketClient<ZodType, ZodType, any> | null>(null);\n\nexport function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n options,\n}: Omit<SocketProviderProps<Ping, Pong, T>, 'children'>) {\n return {\n SocketProvider: ({ children }: React.PropsWithChildren<{}>) => (\n <SocketProvider<Ping, Pong, T> events={events} options={options}>\n {children}\n </SocketProvider>\n ),\n useSocketClient: () => useSocketClient<Ping, Pong, T>(),\n useSocketConnection: <K extends keyof T & string>(\n args: Parameters<typeof useSocketConnection<T, K>>[0]\n ) => useSocketConnection<T, K>(args),\n };\n}\n\nfunction SocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({\n events,\n options,\n children,\n}: SocketProviderProps<Ping, Pong, T>) {\n const client = React.useMemo(() => new SocketClient(events, options), [events, options.url]);\n\n React.useEffect(() => {\n client.connect();\n return () => {\n client.disconnect();\n };\n }, []);\n\n return <SocketCtx.Provider value={client}>{children}</SocketCtx.Provider>;\n}\n\nfunction useSocketClient<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(): SocketClient<Ping, Pong, T> {\n const ctx = React.useContext(SocketCtx);\n if (!ctx) throw new Error('SocketClient not found. Wrap with <SocketProvider>.');\n return ctx as SocketClient<Ping, Pong, T>;\n}\n\ntype Rooms = string[] | string | undefined;\n\nexport type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string> = {\n event: K;\n rooms?: Rooms;\n onMessage: (payload: Payload<T, K>, meta: { envelope: ServerEnvelope<T, K>; ctx: ClientCtx }) => void;\n onCleanup?: () => void;\n autoJoin?: boolean;\n autoLeave?: boolean;\n deps?: React.DependencyList;\n};\n\nfunction useSocketConnection<T extends EventMap, K extends keyof T & string>(\n args: UseSocketConnectionArgs<T, K>,\n) {\n const { event, rooms, onMessage, onCleanup, autoJoin = true, autoLeave = true } = args;\n const client = useSocketClient<ZodType, ZodType, T>();\n\n const normalizedRooms = React.useMemo(\n () => (rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms]),\n [rooms],\n );\n\n React.useEffect(() => {\n if (autoJoin && normalizedRooms.length > 0) client.joinRooms(normalizedRooms);\n const unsubscribe = client.on(event, onMessage);\n\n return () => {\n unsubscribe();\n if (autoLeave && normalizedRooms.length > 0) client.leaveRooms(normalizedRooms);\n if (onCleanup) onCleanup();\n };\n }, args.deps ?? [client, event, onMessage, autoJoin, autoLeave, ...normalizedRooms]);\n}\n"],"mappings":";;;AASO,IAAM,iBAA0B,OAAU,QAAgC;AAC/E,QAAM,UAAkC,EAAE,GAAI,IAAI,WAAW,CAAC,EAAG;AACjE,QAAM,aAAa,OAAO,aAAa,eAAe,IAAI,gBAAgB;AAC1E,MAAI,CAAC,YAAY;AACf,0DAA4B;AAC5B,8CAAsB;AAAA,EACxB;AAEA,QAAM,MAAM,MAAM,MAAM,IAAI,KAAK;AAAA,IAC/B,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA,MAAM,aAAc,IAAI,OAAe,IAAI,QAAQ,OAAO,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EAC/F,CAAC;AAED,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,UAAU,KAAK,MAAM,GAAG,GAAG;AACjC,UAAM,IAAI,MAAM,IAAI,IAAI,MAAM,KAAK,IAAI,UAAU,WAAM,OAAO,EAAE;AAAA,EAClE;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACjCA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AAwCP,IAAM,UAAU,CAAC,MAAyC,EAAE,YAAY;AAQxE,SAAS,OAAU,OAAgB,QAAqB;AACtD,SAAO,SAAU,OAAO,MAAM,KAAK,IAAW;AAChD;AAOA,SAAS,eAAe,OAA4C;AAClE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,IAAI,gBAAgB;AACnC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,KAAK,KAAM;AACf,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,QAAE,QAAQ,CAAC,MAAM;AACf,YAAI,KAAK,KAAM;AACf,YAAI,OAAO,MAAM,UAAU;AACzB,iBAAO,OAAO,GAAG,KAAK,UAAU,CAAC,CAAC;AAAA,QACpC,OAAO;AACL,iBAAO,OAAO,GAAG,OAAO,CAAC,CAAC;AAAA,QAC5B;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,MAAM,UAAU;AACzB,aAAO,IAAI,GAAG,KAAK,UAAU,CAAC,CAAC;AAC/B;AAAA,IACF;AACA,WAAO,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,EACzB;AACA,QAAM,IAAI,OAAO,SAAS;AAC1B,SAAO,IAAI,IAAI,CAAC,KAAK;AACvB;AAQA,SAAS,SAAwD,KAAQ,KAAgB;AACvF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,EAAE,CAAC,GAAG,GAAG,OAAO,GAAG,KAAK,IAAI;AAClC,SAAO;AACT;AAOA,IAAM,uBAAuB,CAAC,MAC5B,KAAK,OAAO,MAAM,YAAY,gBAAgB,IAAK,EAAU,aAAa;AAK5E,IAAM,qBAA6C,CAAC,UAAiC;AACnF,MAAI,OAAO,YAAY,YAAa;AACpC,QAAM,KAAK,QAAQ,SAAS,QAAQ;AACpC,MAAI,KAAK,SAAS,qBAAqB,KAAK;AAC9C;AAEA,IAAM,kBAAmD;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOA,IAAM,WAAW,MAAM;AAAC;AAExB,SAAS,mBACP,QACA,aACqB;AACrB,QAAM,WAAgC,EAAE,MAAM,UAAU,MAAM,UAAU;AAExE,MAAI,eAAe,YAAY,YAAY,MAAM,cAAc;AAC7D,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,MAAI,WAAW,QAAQ,WAAW,WAAW;AAC3C,WAAO;AAAA,MACL,MAAM,CAAC,OAAO,SAAS,mBAAmB,OAAO,EAAE,GAAG,OAAO,KAAK,IAAI,KAAK;AAAA,MAC3E,MAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,WAAW,YAAY;AACzB,WAAO;AAAA,MACL,MAAM,CAAC,OAAO,SAAS,mBAAmB,OAAO,EAAE,GAAG,OAAO,KAAK,IAAI,KAAK;AAAA,MAC3E,MAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,OAAO,WAAW,YAAY;AAChC,WAAO;AAAA,MACL,MAAM,CAAC,OAAO,SAAS,OAAO,OAAO,EAAE,GAAG,OAAO,KAAK,IAAI,KAAK;AAAA,MAC/D,MAAM;AAAA,IACR;AAAA,EACF;AACA,MAAI,OAAO,WAAW,UAAU;AAC9B,UAAM,UAAU;AAChB,UAAM,UAAU,QAAQ,QAAQ,OAAO;AACvC,UAAM,eAAe,gBAAgB,OAAO,CAAC,SAAS,QAAQ,IAAI,CAAC;AACnE,QAAI,aAAa,WAAW,GAAG;AAC7B,aAAO,EAAE,MAAM,UAAU,MAAM,UAAU,aAAa,UAAU;AAAA,IAClE;AACA,UAAM,YAAY,IAAI,IAAmC,YAAY;AACrE,UAAM,UACJ,QAAQ,QAAQ,QAAQ,KAAK,SAAS,IAAI,IAAI,IAAW,QAAQ,IAAI,IAAI;AAC3E,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,OAAoC,CAAC,OAAO,SAAS;AACzD,UAAI,CAAC,UAAU,IAAI,MAAM,IAAI,EAAG;AAChC,UAAI,SAAS;AACX,YAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI,EAAG;AAAA,MACnC;AACA,aAAO,OAAO,EAAE,GAAG,OAAO,KAAK,IAAI,KAAK;AAAA,IAC1C;AACA,WAAO,EAAE,MAAM,MAAM,UAAU,aAAa,UAAU;AAAA,EACxD;AAEA,SAAO;AACT;AAQA,SAAS,YAA+B,MAA4C;AAClF,SAAQ,KAA0B,CAAC;AACrC;AAUA,SAAS,SACP,MACA,SACA,QACA,OACA;AACA,QAAM,mBAAmB,OAAuB,QAAQ,KAAK,IAAI,YAAY;AAC7E,QAAM,kBAAkB,OAAsB,OAAO,KAAK,IAAI,WAAW;AACzE,QAAM,OAAO,YAAuB,KAAK,MAAO,oBAAoB,CAAC,CAAS;AAC9E,QAAM,MAAM,GAAG,WAAW,EAAE,GAAG,IAAI,GAAG,eAAe,eAAsB,CAAC;AAC5E,SAAO,EAAE,KAAK,iBAAiB,iBAAiB;AAClD;AAUO,SAAS,kBACd,MACoB;AACpB,QAAM,cAAc,KAAK;AACzB,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,UAAU,KAAK;AACrB,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,gBAAgB,KAAK,iBAAiB;AAC5C,QAAM,cACJ,KAAK,eAAe;AACtB,QAAM,EAAE,MAAM,WAAW,MAAM,UAAU,IAAI,mBAA0B,KAAK,OAAO,WAAW;AAC9F,QAAM,iBAAiB,cAAc;AACrC,QAAM,qBAAqB,CACzB,OACA,YAC0B;AAC1B,QAAI,CAAC,kBAAkB,CAAC,QAAS,QAAO;AACxC,WAAO,EAAE,GAAG,OAAO,GAAG,QAAQ;AAAA,EAChC;AAOA,iBAAe,WAAW,QAAkB,QAAQ,OAAO;AACzD,UAAM,WAAW;AACjB,UAAM,YAAY,kBAAkB,EAAE,UAAU,MAAM,CAAC;AACvD,cAAU,EAAE,MAAM,cAAc,KAAK,UAAU,MAAM,CAAC;AAAA,EACxD;AAQA,WAAS,cACP,MACA,QACA,MACiB;AACjB,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,SAAS,CAAC,CAAC,KAAK,IAAI;AAC1B,UAAM,SAAS,QAAQ,KAAK,MAAM;AAClC,UAAM,YAAY,GAAG,KAAK,OAAO,YAAY,CAAC,IAAI,OAAO,KAAK,IAAI,CAAC;AACnE,UAAM,YAAY,MAAM;AACxB,UAAM,OAAO,CAAC,UAAiC,UAAU,OAAO,SAAS;AACzE,SAAK,EAAE,MAAM,SAAS,MAAM,UAAU,CAAC;AAGvC,UAAM,MAAM,IAAI,UAAkC;AAChD,YAAM,IAAI,YAAe,KAAK;AAC9B,YAAM,SAAU,GAAW;AAC3B,YAAM,QAAS,GAAW;AAC1B,YAAM,UAAU,SAAS,SAAS,SAAS,OAAc,WAAW,IAAK;AACzE,aAAO,cAAc,EAAE,MAAM,QAAuB,OAAO,QAAQ,CAAC;AAAA,IACtE;AAMA,UAAM,kBAAkB,UAAU,UAAwB;AACxD,YAAM,WAAW,IAAI,GAAG,KAAK;AAC7B,YAAM,YAAY,kBAAkB,EAAE,UAAU,OAAO,KAAK,CAAC;AAC7D,WAAK,EAAE,MAAM,cAAc,KAAK,UAAU,OAAO,KAAK,CAAC;AAAA,IACzD;AAMA,UAAM,UAAU,IAAI,SAAyD;AAC3E,YAAM,CAAC,SAAS,GAAG,IAAI,IAAI;AAC3B,YAAM,IAAI,IAAI,GAAI,IAAqB;AACvC,UAAI,SAAS,QAAQ;AACnB,oBAAY;AAAA,UAAuD;AAAA,UAAG,CAAC,SACrE,OAAO,YAAY,aAAc,QAAgB,IAAI,IAAK;AAAA,QAC5D;AAAA,MACF,OAAO;AACL,oBAAY;AAAA,UAAyC;AAAA,UAAG,CAAC,SACvD,OAAO,YAAY,aAAc,QAAgB,IAAI,IAAK;AAAA,QAC5D;AAAA,MACF;AACA,WAAK,EAAE,MAAM,WAAW,KAAK,EAAE,CAAC;AAAA,IAClC;AAGA,QAAI,SAAS,QAAQ;AACnB,YAAMA,eAA+C,IAAI,UAAU;AACjE,aAAK,EAAE,MAAM,eAAe,MAAM,WAAW,SAAS,cAAc,CAAC;AACrE,cAAM,IAAI,YAAe,KAAK;AAC9B,cAAM,SAAU,GAAW;AAC3B,cAAM,QAAS,GAAW;AAG1B,cAAM,EAAE,iBAAiB,iBAAiB,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACnF,eAAO,iBAML;AAAA,UACA,GAAI;AAAA,UACJ,UAAU,IAAI,GAAG,KAAK;AAAA,UACtB,kBAAkB;AAAA,UAClB,kBAAkB,CAAC,aAAa,cAAc,QAAQ;AAAA,UACtD,iBAAiB;AAAA,UACjB,SAAS,OAAO,EAAE,UAAU,MAAM;AAChC,kBAAM,YAAY;AAAA,cAChB,GAAI;AAAA,cACJ,GAAI,YAAY,EAAE,CAAC,WAAW,GAAG,UAAU,IAAI,CAAC;AAAA,YAClD;AACA,kBAAM,EAAE,IAAI,IAAI,SAAS,MAAM,SAAS,QAAQ,SAAS;AACzD,kBAAM,YAAY,KAAK,IAAI;AAC3B,kBAAM,SAAS,iBAAiB,EAAE,QAAQ,kBAAkB,OAAO,UAAU,IAAI;AACjF;AAAA,cACE;AAAA,gBACE,EAAE,MAAM,SAAS,OAAO,SAAS,QAAQ,KAAK,MAAM,UAAU;AAAA,gBAC9D;AAAA,cACF;AAAA,YACF;AACA,gBAAI;AACF,oBAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,OAAO,CAAC;AAClD,oBAAM,SAAS,OAAuB,KAAK,KAAK,IAAI,YAAY;AAGhE,cAAC,QACG,YAAY,MAAM;AAEtB;AAAA,gBACE;AAAA,kBACE;AAAA,oBACE,MAAM;AAAA,oBACN,OAAO;AAAA,oBACP;AAAA,oBACA;AAAA,oBACA,MAAM;AAAA,oBACN,YAAY,KAAK,IAAI,IAAI;AAAA,kBAC3B;AAAA,kBACA,iBAAiB,EAAE,QAAQ,kBAAkB,OAAO,WAAW,QAAQ,OAAO,IAAI;AAAA,gBACpF;AAAA,cACF;AACA,qBAAO;AAAA,YACT,SAAS,OAAO;AACd;AAAA,gBACE;AAAA,kBACE;AAAA,oBACE,MAAM;AAAA,oBACN,OAAO;AAAA,oBACP;AAAA,oBACA;AAAA,oBACA,MAAM;AAAA,oBACN,YAAY,KAAK,IAAI,IAAI;AAAA,oBACzB;AAAA,kBACF;AAAA,kBACA;AAAA,gBACF;AAAA,cACF;AACA,oBAAM;AAAA,YACR;AAAA,UACF;AAAA;AAAA,QAEF,GAAG,WAAW;AAAA,MAChB;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA,aAAAA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO;AACT,YAAMA,eAA4C,IAAI,UAAU;AAC9D,aAAK,EAAE,MAAM,eAAe,MAAM,WAAW,SAAS,MAAM,CAAC;AAC7D,cAAM,IAAI,YAAe,KAAK;AAC9B,cAAM,SAAU,GAAW;AAC3B,cAAM,QAAS,GAAW;AAE1B,cAAM,EAAE,KAAK,iBAAiB,iBAAiB,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACxF,eAAO,SAA4D;AAAA,UACjE,GAAI;AAAA,UACJ,UAAU,IAAI,GAAG,KAAK;AAAA,UACtB,iBAAiB;AAAA,UACjB,SAAS,YAAY;AACnB,kBAAM,YAAY,KAAK,IAAI;AAC3B,kBAAM,SAAS,iBACX,EAAE,QAAQ,kBAAkB,OAAO,gBAAgB,IACnD;AACJ;AAAA,cACE;AAAA,gBACE,EAAE,MAAM,SAAS,OAAO,SAAS,QAAQ,KAAK,MAAM,UAAU;AAAA,gBAC9D;AAAA,cACF;AAAA,YACF;AACA,gBAAI;AACF,oBAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,OAAO,CAAC;AAClD,oBAAM,SAAS,OAAuB,KAAK,KAAK,IAAI,YAAY;AAGhE,cAAC,QACG,YAAY,MAAM;AAEtB;AAAA,gBACE;AAAA,kBACE;AAAA,oBACE,MAAM;AAAA,oBACN,OAAO;AAAA,oBACP;AAAA,oBACA;AAAA,oBACA,MAAM;AAAA,oBACN,YAAY,KAAK,IAAI,IAAI;AAAA,kBAC3B;AAAA,kBACA,iBACI,EAAE,QAAQ,kBAAkB,OAAO,iBAAiB,QAAQ,OAAO,IACnE;AAAA,gBACN;AAAA,cACF;AACA,qBAAO;AAAA,YACT,SAAS,OAAO;AACd;AAAA,gBACE;AAAA,kBACE;AAAA,oBACE,MAAM;AAAA,oBACN,OAAO;AAAA,oBACP;AAAA,oBACA;AAAA,oBACA,MAAM;AAAA,oBACN,YAAY,KAAK,IAAI,IAAI;AAAA,oBACzB;AAAA,kBACF;AAAA,kBACA;AAAA,gBACF;AAAA,cACF;AACA,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF,GAAG,WAAW;AAAA,MAChB;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA,aAAAA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,gBAA2C,UAC5C,kBACA;AACH,UAAI,cAAc,WAAW,GAAG;AAC9B,cAAM,IAAI,MAAM,kDAAkD;AAAA,MACpE;AACA,YAAM,YAAY,cAAc,SAAS;AACzC,YAAM,QAAQ,cAAc,MAAM,GAAG,SAAS;AAC9C,YAAM,OAAO,cAAc,SAAS;AACpC,YAAM,OAAO,YAAe,KAAK;AACjC,YAAM,SAAU,MAAc;AAC9B,YAAM,QAAS,MAAc;AAE7B,YAAM,EAAE,KAAK,iBAAiB,iBAAiB,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACxF,YAAM,iBAAiB,OAAqB,MAAM,KAAK,IAAI,UAAU;AAErE,YAAM,cAAc,MAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,KAAK,IAAI,UAAU,SAAS;AACrF,YAAM,UAAU,cAAc,WAAW,cAAqB,IAAI;AAElE,YAAM,YAAY,KAAK,IAAI;AAC3B,YAAM,SAAS,iBACX,EAAE,QAAQ,kBAAkB,OAAO,gBAAgB,IACnD;AACJ;AAAA,QACE;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,OAAO;AAAA,YACP;AAAA,YACA;AAAA,YACA,MAAM;AAAA,YACN,MAAM;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI;AACF,cAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACjE,cAAM,SAAS,OAAuB,KAAK,KAAK,IAAI,YAAY;AAGhE,QAAC,QACG,YAAY,MAAM;AAEtB;AAAA,UACE;AAAA,YACE;AAAA,cACE,MAAM;AAAA,cACN,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,MAAM;AAAA,cACN,YAAY,KAAK,IAAI,IAAI;AAAA,YAC3B;AAAA,YACA,iBACI,EAAE,QAAQ,kBAAkB,OAAO,iBAAiB,QAAQ,OAAO,IACnE;AAAA,UACN;AAAA,QACF;AACA,eAAO;AAAA,MACT,SAAS,OAAO;AACd;AAAA,UACE;AAAA,YACE;AAAA,cACE,MAAM;AAAA,cACN,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,MAAM;AAAA,cACN,YAAY,KAAK,IAAI,IAAI;AAAA,cACzB,MAAM;AAAA,cACN;AAAA,YACF;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,cAA+C,IAAI,UAAU;AACjE,WAAK,EAAE,MAAM,eAAe,MAAM,WAAW,SAAS,WAAW,CAAC;AAClE,aAAO,YAA4D;AAAA,QACjE,GAAI;AAAA,QACJ,aAAa,IAAI,GAAG,KAAK;AAAA,QACzB,YAAY,CAAC,SACX,cAAc,GAAI,CAAC,GAAG,OAAO,IAAI,CAAqC;AAAA,MAC1E,GAAG,WAAW;AAAA,IAChB;AAEA,WAAO;AAAA,MACL;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACT;AACF;AAKA,SAAS,WAAW,MAAqC;AACvD,QAAM,KAAK,IAAI,SAAS;AACxB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,CAAC,CAAC,GAAG;AAC/C,QAAI,KAAK,KAAM;AACf,QAAI,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ,CAAC,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,IAAW,CAAC;AAAA,QAC5E,IAAG,OAAO,GAAG,CAAQ;AAAA,EAC5B;AACA,SAAO;AACT;;;AC3lBA,SAAS,UAAkB;;;ACD3B,YAAY,WAAW;AAiBjB;AARN,IAAM,YAAkB,oBAA0D,IAAI;AAE/E,SAAS,oBAAoF;AAAA,EAClG;AAAA,EACA;AACF,GAAyD;AACvD,SAAO;AAAA,IACL,gBAAgB,CAAC,EAAE,SAAS,MAC1B,oBAAC,kBAA8B,QAAgB,SAC5C,UACH;AAAA,IAEF,iBAAiB,MAAM,gBAA+B;AAAA,IACtD,qBAAqB,CACnB,SACG,oBAA0B,IAAI;AAAA,EACrC;AACF;AAEA,SAAS,eAA+E;AAAA,EACtF;AAAA,EACA;AAAA,EACA;AACF,GAAuC;AACrC,QAAM,SAAe,cAAQ,MAAM,IAAI,aAAa,QAAQ,OAAO,GAAG,CAAC,QAAQ,QAAQ,GAAG,CAAC;AAE3F,EAAM,gBAAU,MAAM;AACpB,WAAO,QAAQ;AACf,WAAO,MAAM;AACX,aAAO,WAAW;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,oBAAC,UAAU,UAAV,EAAmB,OAAO,QAAS,UAAS;AACtD;AAEA,SAAS,kBAA+G;AACtH,QAAM,MAAY,iBAAW,SAAS;AACtC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,qDAAqD;AAC/E,SAAO;AACT;AAcA,SAAS,oBACP,MACA;AACA,QAAM,EAAE,OAAO,OAAO,WAAW,WAAW,WAAW,MAAM,YAAY,KAAK,IAAI;AAClF,QAAM,SAAS,gBAAqC;AAEpD,QAAM,kBAAwB;AAAA,IAC5B,MAAO,SAAS,OAAO,CAAC,IAAI,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAAA,IACjE,CAAC,KAAK;AAAA,EACR;AAEA,EAAM,gBAAU,MAAM;AACpB,QAAI,YAAY,gBAAgB,SAAS,EAAG,QAAO,UAAU,eAAe;AAC5E,UAAM,cAAc,OAAO,GAAG,OAAO,SAAS;AAE9C,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,aAAa,gBAAgB,SAAS,EAAG,QAAO,WAAW,eAAe;AAC9E,UAAI,UAAW,WAAU;AAAA,IAC3B;AAAA,EACF,GAAG,KAAK,QAAQ,CAAC,QAAQ,OAAO,WAAW,UAAU,WAAW,GAAG,eAAe,CAAC;AACrF;;;ADuDO,IAAM,eAAN,MAAmF;AAAA,EAmBxF,YAAY,QAAW,MAA0C;AANjE,SAAQ,UAAiD;AAGzD;AAAA,SAAiB,aAAa,oBAAI,IAAoB;AACtD,SAAiB,aAAa,oBAAI,IAAuC;AAGvE,SAAK,SAAS;AACd,SAAK,SAAS,GAAG,KAAK,KAAK,EAAE,aAAa,MAAM,GAAG,KAAK,UAAU,CAAC;AACnE,SAAK,gBAAgB,KAAK,iBAAiB;AAC3C,SAAK,iBAAiB,KAAK,kBAAkB;AAC7C,SAAK,cAAc,KAAK,eAAe;AACvC,SAAK,QAAQ,KAAK,SAAS,CAAC;AAE5B,UAAM,KAAK,KAAK;AAChB,SAAK,KAAK;AAAA,MACR,WAAW,GAAG,aAAa;AAAA,MAC3B,WAAW,GAAG,aAAa;AAAA,MAC3B,YAAY,GAAG,cAAc;AAAA,MAC7B,WAAW,GAAG,aAAa;AAAA,MAC3B,YAAY,GAAG;AAAA,MACf,iBAAiB,GAAG;AAAA,MACpB,YAAY,GAAG;AAAA,MACf,QAAQ,GAAG;AAAA,IACb;AAGA,SAAK,OAAO,GAAG,WAAW,MAAM;AAC9B,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,QACP,IAAI,KAAK,OAAO,MAAM;AAAA,MACxB,CAAC;AACD,WAAK,eAAe;AAAA,IACtB,CAAC;AAED,SAAK,OAAO,GAAG,aAAa,CAAC,YAAY;AACvC,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,SAAK,OAAO,GAAG,cAAc,CAAC,WAAW;AACvC,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ,OAAO,MAAM;AAAA,MACvB,CAAC;AACD,WAAK,cAAc;AAAA,IACrB,CAAC;AAED,SAAK,OAAO,GAAG,iBAAiB,CAAC,QAAQ;AACvC,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,QACP,KAAK,OAAO,GAAG;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AAGD,SAAK,OAAO,GAAG,KAAK,GAAG,WAAW,CAAC,QAAa;AAC9C,YAAM,aAAa,KAAK,IAAI;AAC5B,YAAM,gBAAoC,KAAK,YAAY;AAC3D,UAAI;AAEJ,UAAI,eAAe;AACjB,cAAM,OAAO,KAAK,MAAM,aAAa;AACrC,YAAI,CAAC,OAAO,MAAM,IAAI,EAAG,aAAY,KAAK,IAAI,GAAG,aAAa,IAAI;AAAA,MACpE;AAEA,UAAI,KAAK,GAAG,YAAY;AACtB,cAAM,KAAK,KAAK,GAAG,WAAW,UAAU,GAAG;AAC3C,YAAI,CAAC,GAAG,QAAS;AAAA,MACnB;AAEA,YAAM,UAAU,aAAa,KAAK,WAAW;AAE7C,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAED,WAAK,GAAG,SAAS,EAAE,WAAW,SAAS,SAAS,KAAK,QAAQ,KAAK,OAAO,CAAC;AAAA,IAC5E,CAAC;AAAA,EACH;AAAA,EAEQ,IAAI,GAAgC;AAC1C,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAE,OAAQ;AACf,QAAI,CAAC,EAAE,EAAE,IAAI,EAAG;AAChB,QAAI,EAAE,QAAQ,WAAW,KAAK,CAAC,EAAE,KAAK,SAAS,EAAE,KAAY,EAAG;AAChE,MAAE,OAAO,CAAC;AAAA,EACZ;AAAA;AAAA,EAGA,QAA6B;AAC3B,UAAM,QAAQ,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,MAAM,MAAM,EAAE;AAC5F,UAAM,WAAW,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,GAAG,OAAO;AAAA,MAC5E;AAAA,MACA,UAAU,IAAI;AAAA,IAChB,EAAE;AACF,WAAO;AAAA,MACL,YAAY,MAAM;AAAA,MAClB,eAAe,SAAS,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,UAAU,CAAC;AAAA,MAC1D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAqC;AACnD,WAAO,SAAS,OAAO,CAAC,IAAI,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAAA,EACnE;AAAA,EAEQ,iBAAiB;AACvB,SAAK,cAAc;AAEnB,UAAM,OAAO,MAAM;AACjB,YAAM,cAAc,KAAK,GAAG,gBAAgB,EAAE,QAAQ,KAAK,OAAO,CAAC,KAAK,CAAC;AACzE,YAAM,YAAY,EAAE,GAAG,aAAa,iBAAgB,oBAAI,KAAK,GAAE,YAAY,EAAE;AAE7E,YAAM,QAAQ,KAAK,GAAG,WAAW,UAAU,SAAS;AACpD,UAAI,CAAC,MAAM,SAAS;AAClB,YAAI,KAAK,gBAAgB;AACvB,kBAAQ,KAAK,0CAA0C,MAAM,MAAM,MAAM;AAC3E;AAAA,MACF;AAEA,YAAM,QAAQ,WAAW,MAAM;AAAA,MAE/B,GAAG,KAAK,GAAG,SAAS;AAEpB,WAAK,OAAO,QAAQ,KAAK,GAAG,SAAS,EAAE,KAAK,KAAK,GAAG,WAAW,EAAE,SAAS,MAAM,KAAK,GAAG,MAAM;AAC5F,qBAAa,KAAK;AAAA,MACpB,CAAC;AAED,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,SAAK,UAAU,YAAY,MAAM,KAAK,GAAG,UAAU;AACnD,SAAK;AAAA,EACP;AAAA,EAEQ,gBAAgB;AACtB,QAAI,KAAK,SAAS;AAChB,oBAAc,KAAK,OAAO;AAC1B,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,KACE,OACA,SACA,UACA,OACA,WACM;AACN,UAAM,SAAS,KAAK,OAAO,KAAK,EAAE;AAClC,UAAM,SAAS,OAAO,UAAU,OAAO;AACvC,QAAI,CAAC,OAAO,QAAS,OAAM,IAAI,MAAM,wBAAwB,KAAK,MAAM,OAAO,MAAM,OAAO,EAAE;AAE9F,QAAI,OAAO;AACT,WAAK,OAAO,QAAQ,aAAa,KAAK,GAAG,SAAS,EAAE,KAAK,OAAO,KAAK,GAAG,OAAO,MAAM,CAAC,QAAiB;AACrG,YAAI;AACF,gBAAM,GAAG;AAAA,QACX,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,WAAK,OAAO,KAAK,OAAO,KAAK,GAAG,OAAO,IAAI;AAAA,IAC7C;AAEA,SAAK,IAAI;AAAA,MACP,MAAM;AAAA,MACN;AAAA,MACA,UAAU,KAAK,MAAM,UAAU,WAAW;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,OAAiC;AACzC,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,UAAM,SAAmB,CAAC;AAC1B,eAAW,KAAK,MAAM;AACpB,YAAM,QAAQ,KAAK,WAAW,IAAI,CAAC,KAAK,KAAK;AAC7C,WAAK,WAAW,IAAI,GAAG,IAAI;AAC3B,UAAI,SAAS,EAAG,QAAO,KAAK,CAAC;AAAA,IAC/B;AACA,QAAI,OAAO,SAAS,GAAG;AACrB,WAAK,OAAO,KAAK,KAAK,eAAe,EAAE,OAAO,OAAO,CAAC;AACtD,WAAK,IAAI,EAAE,MAAM,QAAQ,QAAQ,QAAQ,OAAO,OAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,WAAW,OAAiC;AAC1C,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,UAAM,UAAoB,CAAC;AAC3B,eAAW,KAAK,MAAM;AACpB,YAAM,OAAO,KAAK,WAAW,IAAI,CAAC,KAAK;AACvC,YAAM,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC;AACjC,UAAI,SAAS,KAAK,OAAO,EAAG,SAAQ,KAAK,CAAC;AAC1C,UAAI,SAAS,EAAG,MAAK,WAAW,OAAO,CAAC;AAAA,UACnC,MAAK,WAAW,IAAI,GAAG,IAAI;AAAA,IAClC;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,OAAO,KAAK,KAAK,gBAAgB,EAAE,OAAO,QAAQ,CAAC;AACxD,WAAK,IAAI,EAAE,MAAM,QAAQ,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,GACE,OACA,SACY;AACZ,UAAM,SAAS,KAAK,OAAO,KAAK,EAAE;AAElC,SAAK,IAAI,EAAE,MAAM,YAAY,QAAQ,YAAY,MAAM,CAAC;AAExD,UAAM,UAAU,CACd,eACA,aACG;AACH,YAAM,gBAAgB;AACtB,YAAM,UAAU,eAAe,QAAQ;AAEvC,YAAM,SAAS,OAAO,UAAU,OAAO;AACvC,UAAI,CAAC,OAAO,QAAS;AAErB,YAAM,aAAa,oBAAI,KAAK;AAC5B,YAAM,SAAS,eAAe,SAAS,IAAI,KAAK,cAAc,MAAM,IAAI;AAExE,YAAM,OAAO;AAAA,QACX,UAAU;AAAA,UACR,WAAY,eAAe,aAAa;AAAA,UACxC,QAAQ,eAAe,UAAU,WAAW,YAAY;AAAA,UACxD,QAAQ,eAAe,UAAU,CAAC;AAAA,UAClC,MAAM;AAAA,UACN,UAAU,eAAe;AAAA,QAC3B;AAAA,QACA,KAAK;AAAA,UACH;AAAA,UACA,WAAW,SAAS,KAAK,IAAI,GAAG,WAAW,QAAQ,IAAI,OAAO,QAAQ,CAAC,IAAI;AAAA,UAC3E,KAAM,KAAK,OAAe;AAAA,UAC1B,UAAU,KAAK,OAAO;AAAA,UACtB,QAAQ,KAAK;AAAA,UACb,OACE,OAAO,aAAa,aAChB,CAAC,MAAgB;AACf,gBAAI;AACF,uBAAS,CAAC;AAAA,YACZ,QAAQ;AAAA,YAER;AAAA,UACF,IACA;AAAA,QACR;AAAA,MACF;AAEA,WAAK,IAAI;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA,UAAU,KAAK,MAAM,UACjB;AAAA,UACE,WAAW,KAAK,SAAS;AAAA,UACzB,QAAQ,KAAK,SAAS;AAAA,UACtB,QAAQ,KAAK,SAAS;AAAA,UACtB,UAAU,KAAK,SAAS;AAAA,QAC1B,IACA;AAAA,MACN,CAAC;AAED,cAAQ,OAAO,MAAa,IAAW;AAAA,IACzC;AAEA,UAAM,eAAe,CAAC,MAAe;AACnC,UAAI,KAAK,gBAAgB,eAAe;AAEtC,gBAAQ,KAAK,YAAY,OAAO,KAAK,CAAC,UAAU,CAAC;AAAA,MACnD;AAAA,IACF;AAEA,SAAK,OAAO,GAAG,OAAO,KAAK,GAAG,OAAO;AACrC,SAAK,OAAO,GAAG,GAAG,OAAO,KAAK,CAAC,UAAU,YAAY;AAErD,QAAI,MAAM,KAAK,WAAW,IAAI,OAAO,KAAK,CAAC;AAC3C,QAAI,CAAC,KAAK;AACR,YAAM,oBAAI,IAAI;AACd,WAAK,WAAW,IAAI,OAAO,KAAK,GAAG,GAAG;AAAA,IACxC;AACA,UAAM,QAA4B,EAAE,MAAM,SAAS,SAAS,aAAa;AACzE,QAAI,IAAI,KAAK;AAEb,WAAO,MAAM;AACX,WAAK,OAAO,IAAI,OAAO,KAAK,GAAG,OAAO;AACtC,WAAK,OAAO,IAAI,GAAG,OAAO,KAAK,CAAC,UAAU,YAAY;AACtD,YAAM,IAAI,KAAK,WAAW,IAAI,OAAO,KAAK,CAAC;AAC3C,UAAI,GAAG;AACL,UAAE,OAAO,KAAK;AACd,YAAI,EAAE,SAAS,EAAG,MAAK,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,MACxD;AACA,WAAK,IAAI,EAAE,MAAM,YAAY,QAAQ,cAAc,MAAM,CAAC;AAAA,IAC5D;AAAA,EACF;AAAA,EAEA,aAAmB;AACjB,SAAK,cAAc;AACnB,SAAK,OAAO,WAAW;AACvB,SAAK,IAAI,EAAE,MAAM,cAAc,OAAO,cAAc,QAAQ,oBAAoB,CAAC;AAAA,EACnF;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAQ;AACpB,SAAK,IAAI,EAAE,MAAM,cAAc,OAAO,WAAW,IAAI,KAAK,OAAO,MAAM,GAAG,CAAC;AAAA,EAC7E;AACF;","names":["useEndpoint"]}
|
|
@@ -3,11 +3,9 @@ import { SocketClient, SocketClientOptions, EventMap, ClientCtx, Payload, Server
|
|
|
3
3
|
import { ZodType } from 'zod';
|
|
4
4
|
type SocketProviderProps<Ping extends ZodType, Pong extends ZodType, T extends EventMap> = React.PropsWithChildren<{
|
|
5
5
|
events: T;
|
|
6
|
-
ping: Ping;
|
|
7
|
-
pong: Pong;
|
|
8
6
|
options: SocketClientOptions<Ping, Pong, T>;
|
|
9
7
|
}>;
|
|
10
|
-
export declare function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({ events,
|
|
8
|
+
export declare function buildSocketProvider<Ping extends ZodType, Pong extends ZodType, T extends EventMap>({ events, options, }: Omit<SocketProviderProps<Ping, Pong, T>, 'children'>): {
|
|
11
9
|
SocketProvider: ({ children }: React.PropsWithChildren<{}>) => import("react/jsx-runtime").JSX.Element;
|
|
12
10
|
useSocketClient: () => SocketClient<Ping, Pong, T>;
|
|
13
11
|
useSocketConnection: <K extends keyof T & string>(args: Parameters<typeof useSocketConnection<T, K>>[0]) => void;
|
|
@@ -31,24 +31,39 @@ export type ClientStatsSnapshot = {
|
|
|
31
31
|
handlers: number;
|
|
32
32
|
}[];
|
|
33
33
|
};
|
|
34
|
+
type NoInfer<T> = [T][T extends any ? 0 : never];
|
|
35
|
+
/**
|
|
36
|
+
* Merged/logically grouped debug events.
|
|
37
|
+
*
|
|
38
|
+
* Buckets:
|
|
39
|
+
* - connection: connect, reconnect, disconnect, connect_error
|
|
40
|
+
* - register: register, unregister
|
|
41
|
+
* - pingpong: ping_emit, pong_recv
|
|
42
|
+
* - room: join, leave
|
|
43
|
+
* - emit
|
|
44
|
+
* - receive
|
|
45
|
+
* - stats
|
|
46
|
+
*/
|
|
34
47
|
export type SocketClientDebugEvent<K extends string = string> = {
|
|
35
|
-
type: '
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
type: 'disconnect';
|
|
42
|
-
reason: string;
|
|
43
|
-
} | {
|
|
44
|
-
type: 'connect_error';
|
|
45
|
-
err: string;
|
|
48
|
+
type: 'connection';
|
|
49
|
+
phase: 'connect' | 'reconnect' | 'disconnect' | 'connect_error';
|
|
50
|
+
id?: string;
|
|
51
|
+
attempt?: number;
|
|
52
|
+
reason?: string;
|
|
53
|
+
err?: string;
|
|
46
54
|
} | {
|
|
47
55
|
type: 'register';
|
|
56
|
+
action: 'register' | 'unregister';
|
|
48
57
|
event: K;
|
|
49
58
|
} | {
|
|
50
|
-
type: '
|
|
51
|
-
|
|
59
|
+
type: 'heartbeat';
|
|
60
|
+
action: 'ping_emit' | 'pong_recv';
|
|
61
|
+
latencyMs?: number;
|
|
62
|
+
payload?: unknown;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'room';
|
|
65
|
+
action: 'join' | 'leave';
|
|
66
|
+
rooms: string[];
|
|
52
67
|
} | {
|
|
53
68
|
type: 'emit';
|
|
54
69
|
event: K;
|
|
@@ -62,22 +77,6 @@ export type SocketClientDebugEvent<K extends string = string> = {
|
|
|
62
77
|
sentTo: string[];
|
|
63
78
|
metadata?: Record<string, unknown>;
|
|
64
79
|
};
|
|
65
|
-
} | {
|
|
66
|
-
type: 'join';
|
|
67
|
-
rooms: string[];
|
|
68
|
-
} | {
|
|
69
|
-
type: 'leave';
|
|
70
|
-
rooms: string[];
|
|
71
|
-
} | {
|
|
72
|
-
type: 'stats';
|
|
73
|
-
value: ClientStatsSnapshot;
|
|
74
|
-
} | {
|
|
75
|
-
type: 'ping_emit';
|
|
76
|
-
payload: unknown;
|
|
77
|
-
} | {
|
|
78
|
-
type: 'pong_recv';
|
|
79
|
-
latencyMs: number;
|
|
80
|
-
payload?: unknown;
|
|
81
80
|
};
|
|
82
81
|
export type SocketClientDebugOptions<K extends string = string> = {
|
|
83
82
|
verbose?: boolean;
|
|
@@ -132,7 +131,7 @@ export declare class SocketClient<Ping extends ZodType, Pong extends ZodType, T
|
|
|
132
131
|
private readonly roomCounts;
|
|
133
132
|
private readonly handlerMap;
|
|
134
133
|
constructor(events: T, opts: SocketClientOptions<Ping, Pong, T>);
|
|
135
|
-
dbg
|
|
134
|
+
private dbg;
|
|
136
135
|
/** internal stats snapshot */
|
|
137
136
|
stats(): ClientStatsSnapshot;
|
|
138
137
|
private toArray;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emeryld/rrroutes-client",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@emeryld/rrroutes-contract": "^2.0.
|
|
22
|
+
"@emeryld/rrroutes-contract": "^2.0.1",
|
|
23
23
|
"zod": "^4.1.12"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|