@emeryld/rrroutes-server 2.0.0 → 2.0.2

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 CHANGED
@@ -151,7 +151,7 @@ function createRRRoute(router, config) {
151
151
  if (!isVerbose || !details) return event;
152
152
  return { ...event, ...details };
153
153
  };
154
- const globalBeforeMws = [...config.global ?? [], ...config.globalMiddleware?.before ?? []].map(
154
+ const globalBeforeMws = [...config.globalMiddleware?.before ?? []].map(
155
155
  (mw) => adaptCtxMw(mw)
156
156
  );
157
157
  const globalAfterMws = mapAfterMiddleware(config.globalMiddleware?.after);
@@ -422,9 +422,12 @@ function warnMissingControllers(router, registry, logger) {
422
422
  // src/sockets/socket.server.index.ts
423
423
  function createSocketConnections(io, events, opts) {
424
424
  const debug = opts?.debug ?? {};
425
- const dbg = (name, e) => {
426
- if (!debug.logger || !debug[e.type]) return;
427
- if (debug.only && !debug.only.includes(name) && e.type === "receive") return;
425
+ const dbg = (maybeEvent, e) => {
426
+ if (!debug.logger) return;
427
+ if (!debug[e.type]) return;
428
+ if (debug.only && maybeEvent && (e.type === "register" || e.type === "handler" || e.type === "emit") && !debug.only.includes(maybeEvent)) {
429
+ return;
430
+ }
428
431
  debug.logger(e);
429
432
  };
430
433
  const getSchema = (k) => events[k].message;
@@ -449,34 +452,39 @@ function createSocketConnections(io, events, opts) {
449
452
  }
450
453
  }
451
454
  registrations.delete(eventName);
452
- dbg(eventName, { type: "unregister", event: eventName });
455
+ dbg(eventName, { type: "register", action: "unregister", event: eventName });
453
456
  };
454
457
  const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? "room:join";
455
458
  const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? "room:leave";
456
459
  const hb = opts?.heartbeat;
457
- if (!hb) {
458
- throw new Error("createSocketConnections: heartbeat config is required");
459
- }
460
+ if (!hb) throw new Error("createSocketConnections: heartbeat config is required");
460
461
  const pingEvent = hb.pingEvent ?? "sys:ping";
461
462
  const pongEvent = hb.pongEvent ?? "sys:pong";
462
463
  io.on("connection", (socket) => {
463
464
  if (socket.data?.user) {
464
- dbg("auth_ok", { type: "auth_ok", socketId: socket.id, sub: socket.data.user?.sub });
465
+ dbg(null, {
466
+ type: "auth",
467
+ phase: "ok",
468
+ socketId: socket.id,
469
+ sub: socket.data.user?.sub
470
+ });
465
471
  }
466
472
  const joinHandler = async ({ rooms }) => {
467
473
  const list = toArray(rooms);
468
474
  for (const r of list) {
469
- if (!opts?.rooms?.onRoomJoin || await opts.rooms.onRoomJoin({ room: r, socket }) == true) {
475
+ const allowed = !opts?.rooms?.onRoomJoin || await opts.rooms.onRoomJoin({ room: r, socket }) === true;
476
+ if (allowed) {
470
477
  await socket.join(r);
471
- dbg("room_join", { type: "room_join", rooms: [r], socketId: socket.id });
478
+ dbg(null, { type: "rooms", action: "join", rooms: [r], socketId: socket.id });
472
479
  }
473
480
  }
474
481
  };
475
482
  const leaveHandler = async ({ rooms }) => {
476
483
  const list = toArray(rooms);
477
484
  for (const r of list) {
478
- if (!opts?.rooms?.onRoomLeave || await opts.rooms.onRoomLeave({ room: r, socket }) == true) {
479
- dbg("room_leave", { type: "room_leave", rooms: [r], socketId: socket.id });
485
+ const allowed = !opts?.rooms?.onRoomLeave || await opts.rooms.onRoomLeave({ room: r, socket }) === true;
486
+ if (allowed) {
487
+ dbg(null, { type: "rooms", action: "leave", rooms: [r], socketId: socket.id });
480
488
  await socket.leave(r);
481
489
  }
482
490
  }
@@ -491,21 +499,48 @@ function createSocketConnections(io, events, opts) {
491
499
  const parsed = hb.pingSchema.safeParse(msg?.payload);
492
500
  if (!parsed.success) {
493
501
  socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });
502
+ dbg(null, {
503
+ type: "heartbeat",
504
+ phase: "ping",
505
+ socketId: socket.id,
506
+ payload: msg?.payload,
507
+ issues: parsed.error.issues,
508
+ error: true
509
+ });
494
510
  return;
495
511
  }
496
- dbg("ping", { type: "ping", socketId: socket.id, payload: parsed.data });
512
+ dbg(null, {
513
+ type: "heartbeat",
514
+ phase: "ping",
515
+ socketId: socket.id,
516
+ payload: parsed.data
517
+ });
497
518
  const pong = hb.makePongPayload({ socket, ping: parsed.data });
498
519
  if (hb.pongSchema) {
499
520
  const check = hb.pongSchema.safeParse(pong);
500
521
  if (!check.success) {
501
522
  socket.emit(`${pongEvent}:error`, { issues: check.error.issues });
502
- dbg("pong", { type: "pong", socketId: socket.id, issues: check.error.issues, error: true });
523
+ dbg(null, {
524
+ type: "heartbeat",
525
+ phase: "pong",
526
+ socketId: socket.id,
527
+ issues: check.error.issues,
528
+ error: true
529
+ });
503
530
  return;
504
531
  }
505
532
  }
506
533
  socket.emit(pongEvent, pong);
507
- dbg("pong", { type: "pong", socketId: socket.id, payload: pong, error: false });
508
- if (typeof ack === "function") ack({ serverNow: (/* @__PURE__ */ new Date()).toISOString() });
534
+ dbg(null, {
535
+ type: "heartbeat",
536
+ phase: "pong",
537
+ socketId: socket.id,
538
+ payload: pong,
539
+ error: false
540
+ });
541
+ if (typeof ack === "function") {
542
+ ack({ serverNow: (/* @__PURE__ */ new Date()).toISOString() });
543
+ }
509
544
  });
510
545
  });
511
546
  const conn = {
@@ -529,10 +564,10 @@ function createSocketConnections(io, events, opts) {
529
564
  } catch {
530
565
  }
531
566
  } : void 0
532
- // NEW
533
567
  };
534
568
  dbg(String(eventName), {
535
- type: "receive",
569
+ type: "handler",
570
+ phase: "receive",
536
571
  event: String(eventName),
537
572
  socketId: ctx.socketId,
538
573
  nsp: ctx.nsp,
@@ -540,15 +575,33 @@ function createSocketConnections(io, events, opts) {
540
575
  raw: debug.verbose ? raw : void 0
541
576
  });
542
577
  if (!parsed.success) {
543
- socket.emit(`${String(eventName)}:error`, { eventName, sentAt: ctx.sentAt, issues: parsed.error.issues });
544
- dbg(String(eventName), { type: "validation_error", event: String(eventName), issues: parsed.error.issues });
578
+ socket.emit(`${String(eventName)}:error`, {
579
+ eventName,
580
+ sentAt: ctx.sentAt,
581
+ issues: parsed.error.issues
582
+ });
583
+ dbg(String(eventName), {
584
+ type: "handler",
585
+ phase: "validation_error",
586
+ event: String(eventName),
587
+ issues: parsed.error.issues
588
+ });
545
589
  return;
546
590
  }
547
591
  try {
548
592
  await handler(parsed.data, ctx);
549
- dbg(String(eventName), { type: "handler_success", event: String(eventName) });
593
+ dbg(String(eventName), {
594
+ type: "handler",
595
+ phase: "handler_success",
596
+ event: String(eventName)
597
+ });
550
598
  } catch (error) {
551
- dbg(String(eventName), { type: "handler_error", event: String(eventName), error });
599
+ dbg(String(eventName), {
600
+ type: "handler",
601
+ phase: "handler_error",
602
+ event: String(eventName),
603
+ error
604
+ });
552
605
  throw error;
553
606
  }
554
607
  };
@@ -561,7 +614,11 @@ function createSocketConnections(io, events, opts) {
561
614
  };
562
615
  io.on("connection", connectionListener);
563
616
  addRegistration(String(eventName), { connectionListener, socketListeners });
564
- dbg(String(eventName), { type: "register", event: String(eventName) });
617
+ dbg(String(eventName), {
618
+ type: "register",
619
+ action: "register",
620
+ event: String(eventName)
621
+ });
565
622
  return () => {
566
623
  const set = registrations.get(String(eventName));
567
624
  if (!set) return;
@@ -577,7 +634,11 @@ function createSocketConnections(io, events, opts) {
577
634
  }
578
635
  }
579
636
  if (set.size === 0) registrations.delete(String(eventName));
580
- dbg(String(eventName), { type: "unregister", event: String(eventName) });
637
+ dbg(String(eventName), {
638
+ type: "register",
639
+ action: "unregister",
640
+ event: String(eventName)
641
+ });
581
642
  };
582
643
  },
583
644
  off(eventName) {
@@ -586,7 +647,8 @@ function createSocketConnections(io, events, opts) {
586
647
  emit(eventName, payload, rooms, metadata, onAck) {
587
648
  const schema = getSchema(eventName);
588
649
  const check = schema.safeParse(payload);
589
- if (!check.success) throw new Error(`Invalid payload for "${String(eventName)}": ${check.error.message}`);
650
+ if (!check.success)
651
+ throw new Error(`Invalid payload for "${String(eventName)}": ${check.error.message}`);
590
652
  const targets = toArray(rooms);
591
653
  const envelope = {
592
654
  eventName,
@@ -599,20 +661,33 @@ function createSocketConnections(io, events, opts) {
599
661
  const sid = targets[0];
600
662
  const sock = io.sockets.sockets.get(sid);
601
663
  if (sock) {
602
- if (onAck) sock.emit(String(eventName), envelope, (ack) => {
603
- try {
604
- onAck(ack);
605
- } catch {
606
- }
664
+ if (onAck) {
665
+ sock.emit(String(eventName), envelope, (ack) => {
666
+ try {
667
+ onAck(ack);
668
+ } catch {
669
+ }
670
+ });
671
+ } else {
672
+ sock.emit(String(eventName), envelope);
673
+ }
674
+ dbg(String(eventName), {
675
+ type: "emit",
676
+ event: String(eventName),
677
+ rooms: targets,
678
+ envelope: debug.verbose ? envelope : void 0
607
679
  });
608
- else sock.emit(String(eventName), envelope);
609
- dbg(String(eventName), { type: "emit", event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : void 0 });
610
680
  return;
611
681
  }
612
682
  }
613
683
  if (targets.length === 0) io.emit(String(eventName), envelope);
614
684
  else io.to(targets).emit(String(eventName), envelope);
615
- dbg(String(eventName), { type: "emit", event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : void 0 });
685
+ dbg(String(eventName), {
686
+ type: "emit",
687
+ event: String(eventName),
688
+ rooms: targets,
689
+ envelope: debug.verbose ? envelope : void 0
690
+ });
616
691
  }
617
692
  };
618
693
  return conn;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/routesV3.server.ts","../src/sockets/socket.server.index.ts"],"sourcesContent":["export * from './routesV3.server';\nexport * from './sockets/socket.server.index';","/**\n * routesV3.server.ts\n * -----------------------------------------------------------------------------\n * Bind an Express router/app to a `finalize(...)` registry of AnyLeafs.\n * - Fully typed handlers (params/query/body/output)\n * - Zod parsing + optional output validation\n * - buildCtx runs as a middleware *first*, before all other middlewares\n * - Global, per-route, and cfg-derived middlewares (auth, uploads)\n * - Helper to warn about unimplemented routes\n * - DX helpers to use `ctx` in any middleware with proper types\n */\n\nimport { AnyLeaf, FileField, HttpMethod, InferBody, InferOutput, InferParams, InferQuery, MethodCfg } from '@emeryld/rrroutes-contract';\nimport type * as express from 'express';\nimport type { RequestHandler, Router } from 'express';\nimport type { ZodType } from 'zod';\n\n\n\n/** Shape expected from optional logger implementations. */\nexport type LoggerLike = {\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n debug?: (...args: any[]) => void;\n verbose?: (...args: any[]) => void;\n system?: (...args: any[]) => void;\n log?: (...args: any[]) => void;\n};\n\ntype RoutesLoggerCarrier = {\n routesLogger?: LoggerLike;\n};\n\ntype CtxWithRoutesLogger<Ctx> = Ctx & RoutesLoggerCarrier;\n\n// Debug logging --------------------------------------------------------------\nexport type RouteServerDebugMode = 'minimal' | 'complete';\n\ntype RouteServerDebugEventBase = {\n /** Optional logical name assigned via `RouteDef.debug?.debugName` (or `RouteDef.debugName`). */\n name?: string;\n};\n\nexport type RouteServerDebugEvent =\n | (RouteServerDebugEventBase & {\n type: 'request';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'register';\n method: Uppercase<HttpMethod>;\n path: string;\n })\n | (RouteServerDebugEventBase & {\n type: 'buildCtx';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'handler';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n });\n\nexport type RouteServerDebugLogger = (event: RouteServerDebugEvent) => void;\n\n/**\n * Configure server-side debug logging.\n * - Use booleans or `'minimal'/'complete'` for quick toggles.\n * - Pass a custom logger function to redirect structured events.\n * - Provide a map to enable specific event types, opt into verbose payload logging, or restrict logs via `only`.\n */\nexport type RouteServerDebugOptions<Names extends string = string> = RouteServerDebugToggleOptions<Names>;\n\n/**\n * Fine-grained toggle map for server debug logging.\n * Enable individual event types, opt into verbose payload logging, override the logger, or restrict to named routes.\n * Use `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`) to set the name that `only` will match against.\n */\nexport type RouteServerDebugToggleOptions<Names extends string = string> = Partial<\n Record<RouteServerDebugEvent['type'], boolean>\n> & {\n verbose?: boolean;\n logger?: RouteServerDebugLogger;\n only?: Names[];\n};\n\n/**\n * Per-route debug overrides. Same toggles as `RouteServerDebugOptions`, but limited to a single route\n * and therefore replaces the `only` filter with a local `debugName`.\n */\nexport type RouteDefDebugOptions<Names extends string = string> = Omit<\n RouteServerDebugToggleOptions<Names>,\n 'only'\n> & {\n debugName?: Names;\n};\n\nconst serverDebugEventTypes: RouteServerDebugEvent['type'][] = [\n 'register',\n 'request',\n 'buildCtx',\n 'handler',\n];\n\ntype ServerDebugEmitter<Names extends string> = {\n emit: (event: RouteServerDebugEvent, name?: Names) => void;\n mode: RouteServerDebugMode;\n};\n\nconst noopServerEmit = () => {};\n\nfunction createServerDebugEmitter<Names extends string>(\n option?: RouteServerDebugOptions<Names>,\n): ServerDebugEmitter<Names> {\n const disabled: ServerDebugEmitter<Names> = { emit: noopServerEmit, mode: 'minimal' };\n if (!option) return disabled;\n\n if (typeof option === 'object') {\n const toggles = option as RouteServerDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = serverDebugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopServerEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteServerDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger;\n const emit: ServerDebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type) || !logger) return;\n if (onlySet && (!name || !onlySet.has(name))) return;\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Keys + leaf helpers (derive keys from byKey to avoid template-literal pitfalls)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Keys like \"GET /v1/foo\" that *actually* exist in the registry */\nexport type KeysOfRegistry<R extends { byKey: Record<string, AnyLeaf> }> = keyof R['byKey'] &\n string;\n\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n/** Given a registry and a key, pick the exact leaf for that method+path */\nexport type LeafFromKey<R extends { all: readonly AnyLeaf[] }, K extends string> = Extract<\n R['all'][number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/** Optional-ify types if your core returns `never` when a schema isn't defined */\ntype Maybe<T> = [T] extends [never] ? undefined : T;\n\n/** Typed params argument exposed to handlers. */\nexport type ArgParams<L extends AnyLeaf> = Maybe<InferParams<L>>;\n/** Typed query argument exposed to handlers. */\nexport type ArgQuery<L extends AnyLeaf> = Maybe<InferQuery<L>>;\n/** Typed body argument exposed to handlers. */\nexport type ArgBody<L extends AnyLeaf> = Maybe<InferBody<L>>;\n\n/**\n * Convenience to compute a `\"METHOD /path\"` key from a leaf.\n * @param leaf Leaf describing the route.\n * @returns Uppercase method + path key.\n */\nexport const keyOf = (leaf: AnyLeaf) => `${leaf.method.toUpperCase()} ${leaf.path}` as const;\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Context typing & DX helpers (so ctx is usable in *any* middleware)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Unique symbol used to stash ctx on res.locals.\n * (Symbols are safer than string keys against collisions.)\n */\nexport const CTX_SYMBOL: unique symbol = Symbol.for('typedLeaves.ctx');\n\nconst AFTER_HANDLER_NEXT_SYMBOL: unique symbol = Symbol.for('typedLeaves.afterHandlerNext');\n\nfunction setAfterHandlerNext(res: express.Response, value: boolean) {\n (res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL] = value;\n}\n\nfunction handlerInvokedNext(res: express.Response): boolean {\n return Boolean((res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL]);\n}\n\n/** Response type that *has* a ctx on locals for DX in middlewares */\nexport type ResponseWithCtx<Ctx> =\n // Replace locals with an intersection that guarantees CTX_SYMBOL exists\n Omit<express.Response, 'locals'> & {\n locals: express.Response['locals'] & { [CTX_SYMBOL]: CtxWithRoutesLogger<Ctx> };\n };\n\n/** A middleware signature that can *use* ctx via `res.locals[CTX_SYMBOL]` */\nexport type CtxRequestHandler<Ctx> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n}) => any;\n\n/**\n * Safely read ctx from any Response.\n * @param res Express response whose locals contain the ctx symbol.\n * @returns Strongly typed context object.\n */\nexport function getCtx<Ctx = unknown>(res: express.Response): CtxWithRoutesLogger<Ctx> {\n return (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n}\n\n/**\n * Wrap a ctx-typed middleware to a plain RequestHandler (for arrays, etc.).\n * @param mw Middleware that expects a typed response with ctx available.\n * @returns Standard Express request handler.\n */\nfunction adaptCtxMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n return (req, res, next) => {\n try {\n const result = mw({ req, res, next, ctx: getCtx<Ctx>(res) });\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).catch((err) => next(err));\n }\n return result as any;\n } catch (err) {\n next(err as any);\n return undefined;\n }\n };\n}\n\nfunction adaptAfterMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n const adapted = adaptCtxMw<Ctx>(mw);\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n if (!handlerInvokedNext(res)) {\n next();\n return;\n }\n adapted(req, res, next);\n };\n}\n\nfunction mapAfterMiddleware<Ctx>(mws?: Array<CtxRequestHandler<Ctx>>): RequestHandler[] {\n return (mws ?? []).map((mw) => adaptAfterMw<Ctx>(mw));\n}\n\nfunction logHandlerDebugWithRoutesLogger(\n logger: LoggerLike | undefined,\n event: RouteServerDebugEvent,\n) {\n if (!logger || event.type !== 'handler') return;\n const payload: [string, RouteServerDebugEvent] = [\n `[rrroutes-server][handler:${event.stage}] ${event.method} ${event.path}`,\n event,\n ];\n if (event.stage === 'error') {\n (\n logger.error ??\n logger.warn ??\n logger.debug ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n return;\n }\n (\n logger.debug ??\n logger.verbose ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Controller types — object form only (simpler, clearer typings)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Typed route handler for a specific leaf */\nexport type Handler<L extends AnyLeaf, Ctx = unknown> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n params: ArgParams<L>;\n query: ArgQuery<L>;\n body: ArgBody<L>;\n}) => Promise<InferOutput<L>> | InferOutput<L>;\n\n/** Route definition for one key */\nexport type RouteDef<L extends AnyLeaf, Ctx = unknown, Names extends string = string> = {\n /** Middlewares before the handler (run after buildCtx/global/derived) */\n use?: Array<CtxRequestHandler<Ctx>>;\n /** Middlewares after the handler *if* it calls next() */\n after?: Array<CtxRequestHandler<Ctx>>;\n /** Your business logic */\n handler: Handler<L, Ctx>;\n /**\n * Optional per-route debug overrides. When provided, these replace the global debug options.\n */\n debug?: RouteDefDebugOptions<Names>;\n /**\n * Optional logical name used for debug filtering. Prefer `debug.debugName`; this field remains for backwards compatibility.\n */\n debugName?: Names;\n};\n\n/** Map of registry keys -> route defs */\nexport type ControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = {\n [P in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, P>, Ctx, Names>;\n};\n\nexport type PartialControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = Partial<ControllerMap<R, Ctx, Names>>;\n\n// ──────────────────────────────────────────────────────────────────────────────\n/** Options + derivation helpers */\n// ──────────────────────────────────────────────────────────────────────────────\n\nexport type RouteServerConfig<Ctx = unknown, Names extends string = string> = {\n /**\n * Build a request-scoped context. We wrap this in a middleware that runs\n * *first* (before global/derived/route middlewares), and stash it on\n * `res.locals[CTX_SYMBOL]` so *all* later middlewares can use it.\n * You can optionally include `routesLogger` to override handler debug logging per request.\n */\n buildCtx: (\n req: express.Request,\n res: express.Response,\n ) => CtxWithRoutesLogger<Ctx> | Promise<CtxWithRoutesLogger<Ctx>>;\n\n /**\n * Global middlewares for every bound route (run *after* buildCtx). Prefer `globalMiddleware.before`.\n */\n global?: Array<CtxRequestHandler<Ctx>>;\n\n /**\n * Grouped global middlewares that run *before* or *after* the handler for every route.\n */\n globalMiddleware?: {\n before?: Array<CtxRequestHandler<Ctx>>;\n after?: Array<CtxRequestHandler<Ctx>>;\n };\n\n /**\n * Derive middleware from MethodCfg.\n * - `upload` runs when cfg.bodyFiles has entries\n */\n fromCfg?: {\n upload?: (files: FileField[] | undefined, leaf: AnyLeaf) => RequestHandler[];\n };\n\n /** Validate handler return values with outputSchema (default: true) */\n validateOutput?: boolean;\n\n /** Custom responder (default: res.json(data)) */\n send?: (res: express.Response, data: unknown) => void;\n\n /** Optional logger hooks */\n logger?: LoggerLike;\n\n /**\n * Optional debug logging for the request lifecycle.\n * Supports booleans/modes/loggers, or a toggle map with per-event enabling, verbose payload logging,\n * and `only` filters tied to `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`).\n */\n debug?: RouteServerDebugOptions<Names>;\n};\n\n/** Default JSON responder (typed to avoid implicit-any diagnostics) */\nconst defaultSend: (res: express.Response, data: unknown) => void = (res, data) => {\n res.json(data as any);\n};\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Core builder\n// ──────────────────────────────────────────────────────────────────────────────\n\nconst REGISTERED_ROUTES_SYMBOL = Symbol.for('routesV3.registeredRoutes');\n\ntype RegisteredRouteStore = Set<string>;\n\n/**\n * Retrieve or initialize the shared store of registered route keys.\n * @param router Express router/application that carries previously registered keys.\n * @returns Set of string keys describing registered routes.\n */\nfunction getRegisteredRouteStore(router: Router): RegisteredRouteStore {\n const existing = (router as any)[REGISTERED_ROUTES_SYMBOL] as RegisteredRouteStore | undefined;\n if (existing) return existing;\n const store: RegisteredRouteStore = new Set();\n (router as any)[REGISTERED_ROUTES_SYMBOL] = store;\n return store;\n}\n\n/**\n * Inspect the Express layer stack to discover already-registered routes.\n * @param appOrRouter Express application or router to inspect.\n * @returns All keys in the form `\"METHOD /path\"` found on the stack.\n */\nfunction collectRoutesFromStack(appOrRouter: Router): string[] {\n const result: string[] = [];\n const stack: any[] =\n (appOrRouter as any).stack ??\n ((appOrRouter as any)._router ? (appOrRouter as any)._router.stack : undefined) ??\n [];\n\n if (!Array.isArray(stack)) return result;\n\n for (const layer of stack) {\n const route = layer && layer.route;\n if (!route) continue;\n\n const paths = Array.isArray(route.path) ? route.path : [route.path];\n const methodEntries = Object.entries(route.methods ?? {}).filter(([, enabled]) => enabled);\n\n for (const path of paths) {\n for (const [method] of methodEntries) {\n result.push(`${method.toUpperCase()} ${path}`);\n }\n }\n }\n\n return result;\n}\n\n/** Runtime helpers returned by `createRRRoute`. */\nexport type RouteServer<Ctx = unknown, Names extends string = string> = {\n router: Router;\n register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>): void;\n registerControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n ): void;\n warnMissingControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n logger: { warn: (...args: any[]) => void },\n ): void;\n getRegisteredKeys(): string[];\n};\n\n/**\n * Create an Express binding helper that keeps routes and controllers in sync.\n * @param router Express router or app to register handlers on.\n * @param config Optional configuration controlling ctx building, auth, uploads, etc.\n * @returns Object with helpers to register controllers and inspect registered keys.\n */\nexport function createRRRoute<Ctx = unknown, Names extends string = string>(\n router: Router,\n config: RouteServerConfig<Ctx, Names>,\n): RouteServer<Ctx, Names> {\n const validateOutput = config.validateOutput ?? true;\n const send = config.send ?? defaultSend;\n const logger = config.logger;\n const { emit: defaultEmitDebug, mode: defaultDebugMode } =\n createServerDebugEmitter<Names>(config.debug);\n const decorateDebugEvent = <T extends RouteServerDebugEvent>(\n isVerbose: boolean,\n event: T,\n details?: Partial<RouteServerDebugEvent>,\n ): RouteServerDebugEvent => {\n if (!isVerbose || !details) return event;\n return { ...event, ...details } as RouteServerDebugEvent;\n };\n\n const globalBeforeMws = [...(config.global ?? []), ...(config.globalMiddleware?.before ?? [])].map(\n (mw) => adaptCtxMw<Ctx>(mw),\n );\n const globalAfterMws = mapAfterMiddleware<Ctx>(config.globalMiddleware?.after);\n const registered = getRegisteredRouteStore(router);\n\n const buildDerived = (leaf: AnyLeaf): RequestHandler[] => {\n const derived: RequestHandler[] = [];\n if (\n config.fromCfg?.upload &&\n Array.isArray(leaf.cfg.bodyFiles) &&\n leaf.cfg.bodyFiles.length > 0\n ) {\n derived.push(...config.fromCfg.upload(leaf.cfg.bodyFiles, leaf));\n }\n\n return derived;\n };\n\n /** Register a single leaf/controller pair on the underlying router. */\n function register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>) {\n const method = leaf.method as HttpMethod;\n const methodUpper = method.toUpperCase() as Uppercase<HttpMethod>;\n const path = leaf.path as string;\n const key = keyOf(leaf);\n const defDebug = def.debug;\n let debugName = def.debugName as Names | undefined;\n let routeDebugEmitter: ServerDebugEmitter<Names> | undefined;\n if (defDebug) {\n const { debugName: overrideName, ...rest } = defDebug;\n const hasOverrides = Object.values(rest).some((value) => value !== undefined);\n if (hasOverrides) {\n routeDebugEmitter = createServerDebugEmitter<Names>(\n rest as RouteServerDebugOptions<Names>,\n );\n }\n debugName = (overrideName ?? debugName) as Names | undefined;\n }\n const activeEmit = routeDebugEmitter?.emit ?? defaultEmitDebug;\n const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;\n const emit = (event: RouteServerDebugEvent) => activeEmit(event, debugName);\n const isVerboseDebug = activeDebugMode === 'complete';\n emit({ type: 'register', method: methodUpper, path });\n\n const routeSpecific = (def?.use ?? []).map((mw) => adaptCtxMw<Ctx>(mw));\n const derived = buildDerived(leaf);\n const ctxMw: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl ?? path;\n const startedAt = Date.now();\n emit({ type: 'buildCtx', stage: 'start', method: methodUpper, path, url: requestUrl });\n try {\n const ctx = await config.buildCtx(req, res);\n (res.locals as any)[CTX_SYMBOL] = ctx;\n emit({\n type: 'buildCtx',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n });\n next();\n } catch (err) {\n emit({\n type: 'buildCtx',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n logger?.error?.('buildCtx error', err);\n next(err as any);\n }\n };\n const before: RequestHandler[] = [ctxMw, ...globalBeforeMws, ...derived, ...routeSpecific];\n\n const wrapped: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl.split('?')[0] ?? path;\n const startedAt = Date.now();\n emit({ type: 'request', stage: 'start', method: methodUpper, path, url: requestUrl });\n let params: ArgParams<L> | undefined;\n let query: ArgQuery<L> | undefined;\n let body: ArgBody<L> | undefined;\n let responsePayload: InferOutput<L> | undefined;\n let hasResponsePayload = false;\n setAfterHandlerNext(res, false);\n let handlerCalledNext = false;\n const downstreamNext: express.NextFunction = (err?: any) => {\n handlerCalledNext = true;\n setAfterHandlerNext(res, true);\n next(err);\n };\n\n logger?.info?.(`${methodUpper}@${path} (${requestUrl})`);\n const ctx = (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n const ctxRoutesLogger = ctx?.routesLogger;\n const emitWithCtx = (\n event: RouteServerDebugEvent,\n details?: Partial<RouteServerDebugEvent>,\n ) => {\n const decorated = decorateDebugEvent(isVerboseDebug, event, details);\n if (decorated.type === 'handler' && ctxRoutesLogger) {\n logHandlerDebugWithRoutesLogger(ctxRoutesLogger, decorated);\n }else{\n emit(decorated);\n }\n };\n try {\n params = (\n leaf.cfg.paramsSchema\n ? (leaf.cfg.paramsSchema as ZodType).parse(req.params)\n : Object.keys(req.params || {}).length\n ? (req.params as any)\n : undefined\n ) as ArgParams<L>;\n\n try {\n query = leaf.cfg.querySchema\n ? (leaf.cfg.querySchema as ZodType).parse(req.query)\n : Object.keys(req.query || {}).length\n ? (req.query as any)\n : undefined;\n } catch (e) {\n logger?.error?.('Query parsing error', {\n path,\n method: methodUpper,\n error: e,\n raw: JSON.stringify(req.query),\n });\n throw e;\n }\n\n body = (\n leaf.cfg.bodySchema\n ? (leaf.cfg.bodySchema as ZodType).parse(req.body)\n : req.body !== undefined\n ? (req.body as any)\n : undefined\n ) as ArgBody<L>;\n\n logger?.verbose?.(`${methodUpper}@${path} (${requestUrl})`, {\n params,\n query,\n body,\n });\n\n const handlerStartedAt = Date.now();\n emitWithCtx(\n {\n type: 'handler',\n stage: 'start',\n method: methodUpper,\n path,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n\n let result;\n try {\n result = await def.handler({\n req,\n res,\n next: downstreamNext,\n ctx,\n params: params as ArgParams<L>,\n query: query as ArgQuery<L>,\n body: body as ArgBody<L>,\n });\n emitWithCtx(\n {\n type: 'handler',\n stage: 'success',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(result !== undefined ? { output: result } : {}),\n }\n : undefined,\n );\n } catch (e) {\n emitWithCtx(\n {\n type: 'handler',\n stage: 'error',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n error: e,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Handler error', e);\n throw e;\n }\n\n if (!res.headersSent) {\n if (result !== undefined) {\n const out =\n validateOutput && leaf.cfg.outputSchema\n ? (leaf.cfg.outputSchema as ZodType).parse(result)\n : result;\n responsePayload = out as InferOutput<L>;\n hasResponsePayload = true;\n logger?.verbose?.(`${methodUpper}@${path} result`, out);\n send(res, out);\n } else if (!handlerCalledNext) {\n next();\n }\n }\n\n emitWithCtx(\n {\n type: 'request',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(hasResponsePayload ? { output: responsePayload } : {}),\n }\n : undefined,\n );\n } catch (err) {\n emitWithCtx(\n {\n type: 'request',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Route error', err);\n next(err as any);\n }\n };\n\n const after = [...mapAfterMiddleware<Ctx>(def?.after), ...globalAfterMws];\n (router as any)[method](path, ...before, wrapped, ...after);\n registered.add(key);\n }\n\n /**\n * Register controller definitions for the provided keys.\n * @param registry Finalized registry of leaves.\n * @param controllers Partial controller map keyed by `\"METHOD /path\"`.\n */\n function registerControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n >(registry: R, controllers: PartialControllerMap<R, Ctx, Names>) {\n (Object.keys(controllers) as Array<KeysOfRegistry<R>>).forEach((key) => {\n const leaf = registry.byKey[key] as unknown as LeafFromKey<R, typeof key> | undefined;\n if (!leaf) {\n logger?.warn?.(`No leaf found for controller key: ${key}. Not registering route.`);\n return;\n }\n const def = controllers[key];\n if (!def) return;\n register(leaf as LeafFromKey<R, typeof key>, def);\n });\n }\n\n /**\n * Warn about leaves that do not have a registered controller.\n * @param registry Finalized registry of leaves.\n * @param warnLogger Logger used for warning output.\n */\n function warnMissing<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n warnLogger: { warn: (...args: any[]) => void },\n ) {\n const registeredFromStore = new Set<string>(Array.from(registered));\n if (registeredFromStore.size === 0) {\n collectRoutesFromStack(router).forEach((key) => registeredFromStore.add(key));\n }\n for (const leaf of registry.all) {\n const key = keyOf(leaf);\n if (!registeredFromStore.has(key)) {\n warnLogger.warn(`No controller registered for route: ${key}`);\n }\n }\n }\n\n return {\n router,\n register,\n registerControllers,\n warnMissingControllers: warnMissing,\n getRegisteredKeys: () => Array.from(registered),\n };\n}\n\n/**\n * Bind only the controllers that are present in the provided map.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Partial map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindExpressRoutes<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n/**\n * Bind controllers for every leaf. Missing entries fail at compile time.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Complete map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindAll<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: { [K in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, K>, Ctx, Names> },\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// DX helpers\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Helper for great IntelliSense when authoring controller maps.\n * @returns Function that enforces key names while preserving partial flexibility.\n */\nexport const defineControllers =\n <\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n >() =>\n <M extends PartialControllerMap<R, Ctx, Names>>(m: M) =>\n m;\n\n/**\n * Warn about leaves that don't have controllers.\n * Call this during startup to surface missing routes.\n * @param router Express router or app to inspect.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param logger Logger where warnings are emitted.\n */\nexport function warnMissingControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n>(router: Router, registry: R, logger: { warn: (...args: any[]) => void }) {\n const registeredStore = (router as any)[REGISTERED_ROUTES_SYMBOL] as Set<string> | undefined;\n const initial = registeredStore ? Array.from(registeredStore) : collectRoutesFromStack(router);\n const registeredKeys = new Set<string>(initial);\n\n for (const leaf of registry.all) {\n const k = keyOf(leaf);\n if (!registeredKeys.has(k)) {\n logger.warn(`No controller registered for route: ${k}`);\n }\n }\n}\n","// socket.server.index.ts\nimport { Server, Socket } from 'socket.io';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\ntype EventMap = Record<string, SocketEvent>;\ntype Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type HandlerCtx = {\n sentAt: Date;\n socket: Socket;\n socketId: string;\n nsp: string;\n rooms: string[];\n user?: unknown;\n scopes?: string[];\n ack?: (data?: unknown) => void; \n};\n\nexport interface SocketConnection<T extends EventMap> {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void;\n\n off<K extends keyof T & string>(eventName: K): void;\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n toRooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void;\n}\n\nexport type SocketServerDebugEvent<Ping extends ZodType=ZodType, Pong extends ZodType=ZodType> =\n | { type: 'register'; event: string }\n | { type: 'unregister'; event: string }\n | { type: 'receive'; event: string; socketId: string; nsp: string; rooms: string[]; raw?: unknown }\n | { type: 'validation_error'; event: string; issues: any[] }\n | { type: 'handler_success'; event: string }\n | { type: 'handler_error'; event: string; error: unknown }\n | { type: 'emit'; event: string; rooms: string[]; envelope?: unknown }\n | { type: 'room_join'; rooms: string[]; socketId: string }\n | { type: 'room_leave'; rooms: string[]; socketId: string }\n | { type: 'auth_ok'; socketId: string; sub?: string }\n | { type: 'auth_error'; socketId?: string; err: string }\n | { type: 'ping'; socketId: string; payload?: NoInfer<z.infer<Ping>> }\n | { type: 'pong'; socketId: string; payload?: NoInfer<z.infer<Pong>>; issues?: any[], error:boolean };\n\nexport type SocketDebugOptions<Ping extends ZodType, Pong extends ZodType> = {\n verbose?: boolean;\n only?: string[];\n logger?: (e: SocketServerDebugEvent<Ping, Pong>) => void;\n} & {\n [K in SocketServerDebugEvent<Ping, Pong>['type']]?: boolean;\n};\n\nexport type BuiltInRoomHooks = {\n onRoomJoin?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n onRoomLeave?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n};\n\n/** === NEW: enforced heartbeat config with schemas and callback */\nexport type HeartbeatServerOptions<Ping extends ZodType, Pong extends ZodType> = {\n pingEvent?: string; // default 'sys:ping'\n pongEvent?: string; // default 'sys:pong'\n\n /** Validate incoming ping payload: event will be { payload: <ping> }. */\n pingSchema: Ping;\n\n /** Build the pong payload you emit. It will receive the validated ping and socket. */\n makePongPayload: (args: { socket: Socket; ping: z.infer<Ping> }) => NoInfer<z.infer<Pong>>;\n\n /** Optionally validate the outgoing pong object before emit. */\n pongSchema?: Pong;\n};\n\nexport function createSocketConnections<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(\n io: Server,\n events: T,\n opts?: {\n debug?: SocketDebugOptions<Ping, Pong>;\n rooms?: BuiltInRoomHooks;\n heartbeat: HeartbeatServerOptions<Ping, Pong>;\n }\n): SocketConnection<T> {\n const debug = opts?.debug ?? {};\n const dbg = (name: string, e: SocketServerDebugEvent<Ping, Pong>) => {\n if (!debug.logger || !debug[e.type]) return;\n if (debug.only && !debug.only.includes(name) && e.type === 'receive') return;\n debug.logger(e);\n };\n\n const getSchema = <K extends keyof T & string>(k: K) => events[k].message;\n const toArray = (rooms?: string[] | string): string[] =>\n rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n\n // registrations\n const registrations = new Map<\n string,\n Set<{\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }>\n >();\n\n const addRegistration = (eventName: string, reg: {\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }) => {\n let set = registrations.get(eventName);\n if (!set) {\n set = new Set();\n registrations.set(eventName, set);\n }\n set.add(reg);\n };\n\n const removeAllForEvent = (eventName: string) => {\n const set = registrations.get(eventName);\n if (!set) return;\n for (const reg of set) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n }\n registrations.delete(eventName);\n dbg(eventName, { type: 'unregister', event: eventName });\n };\n\n const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? 'room:join';\n const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? 'room:leave';\n\n const hb = opts?.heartbeat;\n if (!hb) {\n throw new Error('createSocketConnections: heartbeat config is required');\n }\n const pingEvent = hb.pingEvent ?? 'sys:ping';\n const pongEvent = hb.pongEvent ?? 'sys:pong';\n\n io.on('connection', (socket) => {\n if ((socket.data as any)?.user) {\n dbg('auth_ok', { type: 'auth_ok', socketId: socket.id, sub: (socket.data as any).user?.sub });\n }\n\n // built-in room handlers\n const joinHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n \n if (!opts?.rooms?.onRoomJoin ||( await opts.rooms.onRoomJoin({ room: r, socket }))==true){\n await socket.join(r);\n dbg('room_join', { type: 'room_join', rooms: [r], socketId: socket.id });\n } \n }\n };\n const leaveHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n \n \n if (!opts?.rooms?.onRoomLeave || ( await opts.rooms.onRoomLeave({ room: r, socket }))==true){\n dbg('room_leave', { type: 'room_leave', rooms: [r], socketId: socket.id });\nawait socket.leave(r);\n }\n }\n };\n socket.on(roomJoinEvent, joinHandler);\n socket.on(roomLeaveEvent, leaveHandler);\n socket.once('disconnect', () => {\n socket.off(roomJoinEvent, joinHandler);\n socket.off(roomLeaveEvent, leaveHandler);\n });\n\n socket.on(pingEvent, (msg: { payload?: unknown } = {}, ack?: (x: { serverNow: string }) => void) => {\n\n const parsed = hb.pingSchema.safeParse(msg?.payload);\n if (!parsed.success) {\n socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });\n return;\n }\n\n dbg('ping', { type: 'ping', socketId: socket.id, payload: parsed.data });\n\n const pong = hb.makePongPayload({ socket, ping: parsed.data })\n\n if (hb.pongSchema) {\n const check = hb.pongSchema.safeParse(pong);\n if (!check.success) {\n socket.emit(`${pongEvent}:error`, { issues: check.error.issues });\n dbg('pong', { type: 'pong', socketId: socket.id, issues: check.error.issues, error:true });\n return;\n }\n }\n\n socket.emit(pongEvent, pong);\n dbg('pong', { type: 'pong', socketId: socket.id, payload: pong , error:false});\n\n if (typeof ack === 'function') ack({ serverNow: new Date().toISOString() });\n });\n });\n\n const conn = {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void {\n const socketListeners = new WeakMap<Socket, (raw: unknown) => void>();\n\nconst connectionListener = (socket: Socket) => {\n const wrapped = async (raw: unknown, maybeAck?: (data?: unknown) => void) => {\n const schema = getSchema(eventName);\n const parsed = schema.safeParse(raw);\n\n const ctx: HandlerCtx = {\n sentAt: new Date(),\n socket,\n socketId: socket.id,\n nsp: socket.nsp.name,\n rooms: Array.from(socket.rooms),\n user: (socket.data )?.user,\n scopes: (socket.data )?.scopes,\n ack: typeof maybeAck === 'function' ? (d?: unknown) => { try { maybeAck(d); } catch { /* noop */ } } : undefined, // NEW\n };\n\n dbg(String(eventName), {\n type: 'receive',\n event: String(eventName),\n socketId: ctx.socketId,\n nsp: ctx.nsp,\n rooms: ctx.rooms,\n raw: debug.verbose ? raw : undefined,\n });\n\n if (!parsed.success) {\n socket.emit(`${String(eventName)}:error`, { eventName, sentAt: ctx.sentAt, issues: parsed.error.issues });\n dbg(String(eventName), { type: 'validation_error', event: String(eventName), issues: parsed.error.issues });\n return;\n }\n\n try {\n await handler(parsed.data as Payload<T, K>, ctx);\n dbg(String(eventName), { type: 'handler_success', event: String(eventName) });\n } catch (error) {\n dbg(String(eventName), { type: 'handler_error', event: String(eventName), error });\n throw error;\n }\n };\n\n socketListeners.set(socket, wrapped);\n socket.on(String(eventName), wrapped);\n socket.once('disconnect', () => {\n const w = socketListeners.get(socket);\n if (w) socket.off(String(eventName), w);\n });\n};\n\n\n io.on('connection', connectionListener);\n addRegistration(String(eventName), { connectionListener, socketListeners });\n dbg(String(eventName), { type: 'register', event: String(eventName) });\n\n return () => {\n const set = registrations.get(String(eventName));\n if (!set) return;\n for (const reg of Array.from(set)) {\n if (reg.connectionListener === connectionListener) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n set.delete(reg);\n break;\n }\n }\n if (set.size === 0) registrations.delete(String(eventName));\n dbg(String(eventName), { type: 'unregister', event: String(eventName) });\n };\n },\n\n off<K extends keyof T & string>(eventName: K): void {\n removeAllForEvent(String(eventName));\n },\n\nemit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n rooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void, \n): void {\n const schema = getSchema(eventName);\n const check = schema.safeParse(payload);\n if (!check.success) throw new Error(`Invalid payload for \"${String(eventName)}\": ${check.error.message}`);\n\n const targets = toArray(rooms);\n const envelope = {\n eventName,\n sentAt: new Date(),\n sentTo: targets,\n data: check.data as Payload<T, K>,\n metadata: metadata ?? {},\n };\n\n // If exactly one target socket, use per-socket emit with ack\n if (targets.length === 1) {\n const sid = targets[0]!;\n const sock = io.sockets.sockets.get(sid as any);\n if (sock) {\n if (onAck) sock.emit(String(eventName), envelope, (ack: unknown) => { try { onAck(ack); } catch { /* noop */ } });\n else sock.emit(String(eventName), envelope);\n dbg(String(eventName), { type: 'emit', event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : undefined });\n return;\n }\n }\n\n // Fallback: broadcast, no ack possible\n if (targets.length === 0) io.emit(String(eventName), envelope);\n else io.to(targets).emit(String(eventName), envelope);\n\n dbg(String(eventName), { type: 'emit', event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : undefined });\n}\n,\n } as const satisfies SocketConnection<T>;\n\n return conn;\n}\n\nexport type { EventMap, Payload };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/routesV3.server.ts","../src/sockets/socket.server.index.ts"],"sourcesContent":["export * from './routesV3.server';\nexport * from './sockets/socket.server.index';","/**\n * routesV3.server.ts\n * -----------------------------------------------------------------------------\n * Bind an Express router/app to a `finalize(...)` registry of AnyLeafs.\n * - Fully typed handlers (params/query/body/output)\n * - Zod parsing + optional output validation\n * - buildCtx runs as a middleware *first*, before all other middlewares\n * - Global, per-route, and cfg-derived middlewares (auth, uploads)\n * - Helper to warn about unimplemented routes\n * - DX helpers to use `ctx` in any middleware with proper types\n */\n\nimport { AnyLeaf, FileField, HttpMethod, InferBody, InferOutput, InferParams, InferQuery, MethodCfg } from '@emeryld/rrroutes-contract';\nimport type * as express from 'express';\nimport type { RequestHandler, Router } from 'express';\nimport type { ZodType } from 'zod';\n\n\n\n/** Shape expected from optional logger implementations. */\nexport type LoggerLike = {\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n debug?: (...args: any[]) => void;\n verbose?: (...args: any[]) => void;\n system?: (...args: any[]) => void;\n log?: (...args: any[]) => void;\n};\n\ntype RoutesLoggerCarrier = {\n routesLogger?: LoggerLike;\n};\n\ntype CtxWithRoutesLogger<Ctx> = Ctx & RoutesLoggerCarrier;\n\n// Debug logging --------------------------------------------------------------\nexport type RouteServerDebugMode = 'minimal' | 'complete';\n\ntype RouteServerDebugEventBase = {\n /** Optional logical name assigned via `RouteDef.debug?.debugName` (or `RouteDef.debugName`). */\n name?: string;\n};\n\nexport type RouteServerDebugEvent =\n | (RouteServerDebugEventBase & {\n type: 'request';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'register';\n method: Uppercase<HttpMethod>;\n path: string;\n })\n | (RouteServerDebugEventBase & {\n type: 'buildCtx';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'handler';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n });\n\nexport type RouteServerDebugLogger = (event: RouteServerDebugEvent) => void;\n\n/**\n * Configure server-side debug logging.\n * - Use booleans or `'minimal'/'complete'` for quick toggles.\n * - Pass a custom logger function to redirect structured events.\n * - Provide a map to enable specific event types, opt into verbose payload logging, or restrict logs via `only`.\n */\nexport type RouteServerDebugOptions<Names extends string = string> = RouteServerDebugToggleOptions<Names>;\n\n/**\n * Fine-grained toggle map for server debug logging.\n * Enable individual event types, opt into verbose payload logging, override the logger, or restrict to named routes.\n * Use `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`) to set the name that `only` will match against.\n */\nexport type RouteServerDebugToggleOptions<Names extends string = string> = Partial<\n Record<RouteServerDebugEvent['type'], boolean>\n> & {\n verbose?: boolean;\n logger?: RouteServerDebugLogger;\n only?: Names[];\n};\n\n/**\n * Per-route debug overrides. Same toggles as `RouteServerDebugOptions`, but limited to a single route\n * and therefore replaces the `only` filter with a local `debugName`.\n */\nexport type RouteDefDebugOptions<Names extends string = string> = Omit<\n RouteServerDebugToggleOptions<Names>,\n 'only'\n> & {\n debugName?: Names;\n};\n\nconst serverDebugEventTypes: RouteServerDebugEvent['type'][] = [\n 'register',\n 'request',\n 'buildCtx',\n 'handler',\n];\n\ntype ServerDebugEmitter<Names extends string> = {\n emit: (event: RouteServerDebugEvent, name?: Names) => void;\n mode: RouteServerDebugMode;\n};\n\nconst noopServerEmit = () => {};\n\nfunction createServerDebugEmitter<Names extends string>(\n option?: RouteServerDebugOptions<Names>,\n): ServerDebugEmitter<Names> {\n const disabled: ServerDebugEmitter<Names> = { emit: noopServerEmit, mode: 'minimal' };\n if (!option) return disabled;\n\n if (typeof option === 'object') {\n const toggles = option as RouteServerDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = serverDebugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopServerEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteServerDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger;\n const emit: ServerDebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type) || !logger) return;\n if (onlySet && (!name || !onlySet.has(name))) return;\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Keys + leaf helpers (derive keys from byKey to avoid template-literal pitfalls)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Keys like \"GET /v1/foo\" that *actually* exist in the registry */\nexport type KeysOfRegistry<R extends { byKey: Record<string, AnyLeaf> }> = keyof R['byKey'] &\n string;\n\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n/** Given a registry and a key, pick the exact leaf for that method+path */\nexport type LeafFromKey<R extends { all: readonly AnyLeaf[] }, K extends string> = Extract<\n R['all'][number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/** Optional-ify types if your core returns `never` when a schema isn't defined */\ntype Maybe<T> = [T] extends [never] ? undefined : T;\n\n/** Typed params argument exposed to handlers. */\nexport type ArgParams<L extends AnyLeaf> = Maybe<InferParams<L>>;\n/** Typed query argument exposed to handlers. */\nexport type ArgQuery<L extends AnyLeaf> = Maybe<InferQuery<L>>;\n/** Typed body argument exposed to handlers. */\nexport type ArgBody<L extends AnyLeaf> = Maybe<InferBody<L>>;\n\n/**\n * Convenience to compute a `\"METHOD /path\"` key from a leaf.\n * @param leaf Leaf describing the route.\n * @returns Uppercase method + path key.\n */\nexport const keyOf = (leaf: AnyLeaf) => `${leaf.method.toUpperCase()} ${leaf.path}` as const;\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Context typing & DX helpers (so ctx is usable in *any* middleware)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Unique symbol used to stash ctx on res.locals.\n * (Symbols are safer than string keys against collisions.)\n */\nexport const CTX_SYMBOL: unique symbol = Symbol.for('typedLeaves.ctx');\n\nconst AFTER_HANDLER_NEXT_SYMBOL: unique symbol = Symbol.for('typedLeaves.afterHandlerNext');\n\nfunction setAfterHandlerNext(res: express.Response, value: boolean) {\n (res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL] = value;\n}\n\nfunction handlerInvokedNext(res: express.Response): boolean {\n return Boolean((res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL]);\n}\n\n/** Response type that *has* a ctx on locals for DX in middlewares */\nexport type ResponseWithCtx<Ctx> =\n // Replace locals with an intersection that guarantees CTX_SYMBOL exists\n Omit<express.Response, 'locals'> & {\n locals: express.Response['locals'] & { [CTX_SYMBOL]: CtxWithRoutesLogger<Ctx> };\n };\n\n/** A middleware signature that can *use* ctx via `res.locals[CTX_SYMBOL]` */\nexport type CtxRequestHandler<Ctx> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n}) => any;\n\n/**\n * Safely read ctx from any Response.\n * @param res Express response whose locals contain the ctx symbol.\n * @returns Strongly typed context object.\n */\nexport function getCtx<Ctx = unknown>(res: express.Response): CtxWithRoutesLogger<Ctx> {\n return (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n}\n\n/**\n * Wrap a ctx-typed middleware to a plain RequestHandler (for arrays, etc.).\n * @param mw Middleware that expects a typed response with ctx available.\n * @returns Standard Express request handler.\n */\nfunction adaptCtxMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n return (req, res, next) => {\n try {\n const result = mw({ req, res, next, ctx: getCtx<Ctx>(res) });\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).catch((err) => next(err));\n }\n return result as any;\n } catch (err) {\n next(err as any);\n return undefined;\n }\n };\n}\n\nfunction adaptAfterMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n const adapted = adaptCtxMw<Ctx>(mw);\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n if (!handlerInvokedNext(res)) {\n next();\n return;\n }\n adapted(req, res, next);\n };\n}\n\nfunction mapAfterMiddleware<Ctx>(mws?: Array<CtxRequestHandler<Ctx>>): RequestHandler[] {\n return (mws ?? []).map((mw) => adaptAfterMw<Ctx>(mw));\n}\n\nfunction logHandlerDebugWithRoutesLogger(\n logger: LoggerLike | undefined,\n event: RouteServerDebugEvent,\n) {\n if (!logger || event.type !== 'handler') return;\n const payload: [string, RouteServerDebugEvent] = [\n `[rrroutes-server][handler:${event.stage}] ${event.method} ${event.path}`,\n event,\n ];\n if (event.stage === 'error') {\n (\n logger.error ??\n logger.warn ??\n logger.debug ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n return;\n }\n (\n logger.debug ??\n logger.verbose ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Controller types — object form only (simpler, clearer typings)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Typed route handler for a specific leaf */\nexport type Handler<L extends AnyLeaf, Ctx = unknown> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n params: ArgParams<L>;\n query: ArgQuery<L>;\n body: ArgBody<L>;\n}) => Promise<InferOutput<L>> | InferOutput<L>;\n\n/** Route definition for one key */\nexport type RouteDef<L extends AnyLeaf, Ctx = unknown, Names extends string = string> = {\n /** Middlewares before the handler (run after buildCtx/global/derived) */\n use?: Array<CtxRequestHandler<Ctx>>;\n /** Middlewares after the handler *if* it calls next() */\n after?: Array<CtxRequestHandler<Ctx>>;\n /** Your business logic */\n handler: Handler<L, Ctx>;\n /**\n * Optional per-route debug overrides. When provided, these replace the global debug options.\n */\n debug?: RouteDefDebugOptions<Names>;\n /**\n * Optional logical name used for debug filtering. Prefer `debug.debugName`; this field remains for backwards compatibility.\n */\n debugName?: Names;\n};\n\n/** Map of registry keys -> route defs */\nexport type ControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = {\n [P in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, P>, Ctx, Names>;\n};\n\nexport type PartialControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = Partial<ControllerMap<R, Ctx, Names>>;\n\n// ──────────────────────────────────────────────────────────────────────────────\n/** Options + derivation helpers */\n// ──────────────────────────────────────────────────────────────────────────────\n\nexport type RouteServerConfig<Ctx = unknown, Names extends string = string> = {\n /**\n * Build a request-scoped context. We wrap this in a middleware that runs\n * *first* (before global/derived/route middlewares), and stash it on\n * `res.locals[CTX_SYMBOL]` so *all* later middlewares can use it.\n * You can optionally include `routesLogger` to override handler debug logging per request.\n */\n buildCtx: (\n req: express.Request,\n res: express.Response,\n ) => CtxWithRoutesLogger<Ctx> | Promise<CtxWithRoutesLogger<Ctx>>;\n\n /**\n * Grouped global middlewares that run *before* or *after* the handler for every route.\n */\n globalMiddleware?: {\n before?: Array<CtxRequestHandler<Ctx>>;\n after?: Array<CtxRequestHandler<Ctx>>;\n };\n\n /**\n * Derive middleware from MethodCfg.\n * - `upload` runs when cfg.bodyFiles has entries\n */\n fromCfg?: {\n upload?: (files: FileField[] | undefined, leaf: AnyLeaf) => RequestHandler[];\n };\n\n /** Validate handler return values with outputSchema (default: true) */\n validateOutput?: boolean;\n\n /** Custom responder (default: res.json(data)) */\n send?: (res: express.Response, data: unknown) => void;\n\n /** Optional logger hooks */\n logger?: LoggerLike;\n\n /**\n * Optional debug logging for the request lifecycle.\n * Supports booleans/modes/loggers, or a toggle map with per-event enabling, verbose payload logging,\n * and `only` filters tied to `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`).\n */\n debug?: RouteServerDebugOptions<Names>;\n};\n\n/** Default JSON responder (typed to avoid implicit-any diagnostics) */\nconst defaultSend: (res: express.Response, data: unknown) => void = (res, data) => {\n res.json(data as any);\n};\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Core builder\n// ──────────────────────────────────────────────────────────────────────────────\n\nconst REGISTERED_ROUTES_SYMBOL = Symbol.for('routesV3.registeredRoutes');\n\ntype RegisteredRouteStore = Set<string>;\n\n/**\n * Retrieve or initialize the shared store of registered route keys.\n * @param router Express router/application that carries previously registered keys.\n * @returns Set of string keys describing registered routes.\n */\nfunction getRegisteredRouteStore(router: Router): RegisteredRouteStore {\n const existing = (router as any)[REGISTERED_ROUTES_SYMBOL] as RegisteredRouteStore | undefined;\n if (existing) return existing;\n const store: RegisteredRouteStore = new Set();\n (router as any)[REGISTERED_ROUTES_SYMBOL] = store;\n return store;\n}\n\n/**\n * Inspect the Express layer stack to discover already-registered routes.\n * @param appOrRouter Express application or router to inspect.\n * @returns All keys in the form `\"METHOD /path\"` found on the stack.\n */\nfunction collectRoutesFromStack(appOrRouter: Router): string[] {\n const result: string[] = [];\n const stack: any[] =\n (appOrRouter as any).stack ??\n ((appOrRouter as any)._router ? (appOrRouter as any)._router.stack : undefined) ??\n [];\n\n if (!Array.isArray(stack)) return result;\n\n for (const layer of stack) {\n const route = layer && layer.route;\n if (!route) continue;\n\n const paths = Array.isArray(route.path) ? route.path : [route.path];\n const methodEntries = Object.entries(route.methods ?? {}).filter(([, enabled]) => enabled);\n\n for (const path of paths) {\n for (const [method] of methodEntries) {\n result.push(`${method.toUpperCase()} ${path}`);\n }\n }\n }\n\n return result;\n}\n\n/** Runtime helpers returned by `createRRRoute`. */\nexport type RouteServer<Ctx = unknown, Names extends string = string> = {\n router: Router;\n register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>): void;\n registerControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n ): void;\n warnMissingControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n logger: { warn: (...args: any[]) => void },\n ): void;\n getRegisteredKeys(): string[];\n};\n\n/**\n * Create an Express binding helper that keeps routes and controllers in sync.\n * @param router Express router or app to register handlers on.\n * @param config Optional configuration controlling ctx building, auth, uploads, etc.\n * @returns Object with helpers to register controllers and inspect registered keys.\n */\nexport function createRRRoute<Ctx = unknown, Names extends string = string>(\n router: Router,\n config: RouteServerConfig<Ctx, Names>,\n): RouteServer<Ctx, Names> {\n const validateOutput = config.validateOutput ?? true;\n const send = config.send ?? defaultSend;\n const logger = config.logger;\n const { emit: defaultEmitDebug, mode: defaultDebugMode } =\n createServerDebugEmitter<Names>(config.debug);\n const decorateDebugEvent = <T extends RouteServerDebugEvent>(\n isVerbose: boolean,\n event: T,\n details?: Partial<RouteServerDebugEvent>,\n ): RouteServerDebugEvent => {\n if (!isVerbose || !details) return event;\n return { ...event, ...details } as RouteServerDebugEvent;\n };\n\n const globalBeforeMws = [ ...(config.globalMiddleware?.before ?? [])].map(\n (mw) => adaptCtxMw<Ctx>(mw),\n );\n const globalAfterMws = mapAfterMiddleware<Ctx>(config.globalMiddleware?.after);\n const registered = getRegisteredRouteStore(router);\n\n const buildDerived = (leaf: AnyLeaf): RequestHandler[] => {\n const derived: RequestHandler[] = [];\n if (\n config.fromCfg?.upload &&\n Array.isArray(leaf.cfg.bodyFiles) &&\n leaf.cfg.bodyFiles.length > 0\n ) {\n derived.push(...config.fromCfg.upload(leaf.cfg.bodyFiles, leaf));\n }\n\n return derived;\n };\n\n /** Register a single leaf/controller pair on the underlying router. */\n function register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>) {\n const method = leaf.method as HttpMethod;\n const methodUpper = method.toUpperCase() as Uppercase<HttpMethod>;\n const path = leaf.path as string;\n const key = keyOf(leaf);\n const defDebug = def.debug;\n let debugName = def.debugName as Names | undefined;\n let routeDebugEmitter: ServerDebugEmitter<Names> | undefined;\n if (defDebug) {\n const { debugName: overrideName, ...rest } = defDebug;\n const hasOverrides = Object.values(rest).some((value) => value !== undefined);\n if (hasOverrides) {\n routeDebugEmitter = createServerDebugEmitter<Names>(\n rest as RouteServerDebugOptions<Names>,\n );\n }\n debugName = (overrideName ?? debugName) as Names | undefined;\n }\n const activeEmit = routeDebugEmitter?.emit ?? defaultEmitDebug;\n const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;\n const emit = (event: RouteServerDebugEvent) => activeEmit(event, debugName);\n const isVerboseDebug = activeDebugMode === 'complete';\n emit({ type: 'register', method: methodUpper, path });\n\n const routeSpecific = (def?.use ?? []).map((mw) => adaptCtxMw<Ctx>(mw));\n const derived = buildDerived(leaf);\n const ctxMw: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl ?? path;\n const startedAt = Date.now();\n emit({ type: 'buildCtx', stage: 'start', method: methodUpper, path, url: requestUrl });\n try {\n const ctx = await config.buildCtx(req, res);\n (res.locals as any)[CTX_SYMBOL] = ctx;\n emit({\n type: 'buildCtx',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n });\n next();\n } catch (err) {\n emit({\n type: 'buildCtx',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n logger?.error?.('buildCtx error', err);\n next(err as any);\n }\n };\n const before: RequestHandler[] = [ctxMw, ...globalBeforeMws, ...derived, ...routeSpecific];\n\n const wrapped: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl.split('?')[0] ?? path;\n const startedAt = Date.now();\n emit({ type: 'request', stage: 'start', method: methodUpper, path, url: requestUrl });\n let params: ArgParams<L> | undefined;\n let query: ArgQuery<L> | undefined;\n let body: ArgBody<L> | undefined;\n let responsePayload: InferOutput<L> | undefined;\n let hasResponsePayload = false;\n setAfterHandlerNext(res, false);\n let handlerCalledNext = false;\n const downstreamNext: express.NextFunction = (err?: any) => {\n handlerCalledNext = true;\n setAfterHandlerNext(res, true);\n next(err);\n };\n\n logger?.info?.(`${methodUpper}@${path} (${requestUrl})`);\n const ctx = (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n const ctxRoutesLogger = ctx?.routesLogger;\n const emitWithCtx = (\n event: RouteServerDebugEvent,\n details?: Partial<RouteServerDebugEvent>,\n ) => {\n const decorated = decorateDebugEvent(isVerboseDebug, event, details);\n if (decorated.type === 'handler' && ctxRoutesLogger) {\n logHandlerDebugWithRoutesLogger(ctxRoutesLogger, decorated);\n }else{\n emit(decorated);\n }\n };\n try {\n params = (\n leaf.cfg.paramsSchema\n ? (leaf.cfg.paramsSchema as ZodType).parse(req.params)\n : Object.keys(req.params || {}).length\n ? (req.params as any)\n : undefined\n ) as ArgParams<L>;\n\n try {\n query = leaf.cfg.querySchema\n ? (leaf.cfg.querySchema as ZodType).parse(req.query)\n : Object.keys(req.query || {}).length\n ? (req.query as any)\n : undefined;\n } catch (e) {\n logger?.error?.('Query parsing error', {\n path,\n method: methodUpper,\n error: e,\n raw: JSON.stringify(req.query),\n });\n throw e;\n }\n\n body = (\n leaf.cfg.bodySchema\n ? (leaf.cfg.bodySchema as ZodType).parse(req.body)\n : req.body !== undefined\n ? (req.body as any)\n : undefined\n ) as ArgBody<L>;\n\n logger?.verbose?.(`${methodUpper}@${path} (${requestUrl})`, {\n params,\n query,\n body,\n });\n\n const handlerStartedAt = Date.now();\n emitWithCtx(\n {\n type: 'handler',\n stage: 'start',\n method: methodUpper,\n path,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n\n let result;\n try {\n result = await def.handler({\n req,\n res,\n next: downstreamNext,\n ctx,\n params: params as ArgParams<L>,\n query: query as ArgQuery<L>,\n body: body as ArgBody<L>,\n });\n emitWithCtx(\n {\n type: 'handler',\n stage: 'success',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(result !== undefined ? { output: result } : {}),\n }\n : undefined,\n );\n } catch (e) {\n emitWithCtx(\n {\n type: 'handler',\n stage: 'error',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n error: e,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Handler error', e);\n throw e;\n }\n\n if (!res.headersSent) {\n if (result !== undefined) {\n const out =\n validateOutput && leaf.cfg.outputSchema\n ? (leaf.cfg.outputSchema as ZodType).parse(result)\n : result;\n responsePayload = out as InferOutput<L>;\n hasResponsePayload = true;\n logger?.verbose?.(`${methodUpper}@${path} result`, out);\n send(res, out);\n } else if (!handlerCalledNext) {\n next();\n }\n }\n\n emitWithCtx(\n {\n type: 'request',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(hasResponsePayload ? { output: responsePayload } : {}),\n }\n : undefined,\n );\n } catch (err) {\n emitWithCtx(\n {\n type: 'request',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Route error', err);\n next(err as any);\n }\n };\n\n const after = [...mapAfterMiddleware<Ctx>(def?.after), ...globalAfterMws];\n (router as any)[method](path, ...before, wrapped, ...after);\n registered.add(key);\n }\n\n /**\n * Register controller definitions for the provided keys.\n * @param registry Finalized registry of leaves.\n * @param controllers Partial controller map keyed by `\"METHOD /path\"`.\n */\n function registerControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n >(registry: R, controllers: PartialControllerMap<R, Ctx, Names>) {\n (Object.keys(controllers) as Array<KeysOfRegistry<R>>).forEach((key) => {\n const leaf = registry.byKey[key] as unknown as LeafFromKey<R, typeof key> | undefined;\n if (!leaf) {\n logger?.warn?.(`No leaf found for controller key: ${key}. Not registering route.`);\n return;\n }\n const def = controllers[key];\n if (!def) return;\n register(leaf as LeafFromKey<R, typeof key>, def);\n });\n }\n\n /**\n * Warn about leaves that do not have a registered controller.\n * @param registry Finalized registry of leaves.\n * @param warnLogger Logger used for warning output.\n */\n function warnMissing<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n warnLogger: { warn: (...args: any[]) => void },\n ) {\n const registeredFromStore = new Set<string>(Array.from(registered));\n if (registeredFromStore.size === 0) {\n collectRoutesFromStack(router).forEach((key) => registeredFromStore.add(key));\n }\n for (const leaf of registry.all) {\n const key = keyOf(leaf);\n if (!registeredFromStore.has(key)) {\n warnLogger.warn(`No controller registered for route: ${key}`);\n }\n }\n }\n\n return {\n router,\n register,\n registerControllers,\n warnMissingControllers: warnMissing,\n getRegisteredKeys: () => Array.from(registered),\n };\n}\n\n/**\n * Bind only the controllers that are present in the provided map.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Partial map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindExpressRoutes<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n/**\n * Bind controllers for every leaf. Missing entries fail at compile time.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Complete map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindAll<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: { [K in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, K>, Ctx, Names> },\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// DX helpers\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Helper for great IntelliSense when authoring controller maps.\n * @returns Function that enforces key names while preserving partial flexibility.\n */\nexport const defineControllers =\n <\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n >() =>\n <M extends PartialControllerMap<R, Ctx, Names>>(m: M) =>\n m;\n\n/**\n * Warn about leaves that don't have controllers.\n * Call this during startup to surface missing routes.\n * @param router Express router or app to inspect.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param logger Logger where warnings are emitted.\n */\nexport function warnMissingControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n>(router: Router, registry: R, logger: { warn: (...args: any[]) => void }) {\n const registeredStore = (router as any)[REGISTERED_ROUTES_SYMBOL] as Set<string> | undefined;\n const initial = registeredStore ? Array.from(registeredStore) : collectRoutesFromStack(router);\n const registeredKeys = new Set<string>(initial);\n\n for (const leaf of registry.all) {\n const k = keyOf(leaf);\n if (!registeredKeys.has(k)) {\n logger.warn(`No controller registered for route: ${k}`);\n }\n }\n}\n","// socket.server.index.ts\nimport { Server, Socket } from 'socket.io';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\ntype EventMap = Record<string, SocketEvent>;\ntype Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\n// helper for generics that shouldn't be widened\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\nexport type HandlerCtx = {\n sentAt: Date;\n socket: Socket;\n socketId: string;\n nsp: string;\n rooms: string[];\n user?: unknown;\n scopes?: string[];\n ack?: (data?: unknown) => void;\n};\n\nexport interface SocketConnection<T extends EventMap> {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void;\n\n off<K extends keyof T & string>(eventName: K): void;\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n toRooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void;\n}\n\n/**\n * Grouped debug types:\n * - register: register, unregister\n * - handler: receive, validation_error, handler_success, handler_error\n * - emit: emit\n * - rooms: room_join, room_leave\n * - auth: auth_ok, auth_error\n * - heartbeat: ping, pong\n */\nexport type SocketServerDebugEvent<Ping extends ZodType = ZodType, Pong extends ZodType = ZodType> =\n | {\n type: 'register';\n action: 'register' | 'unregister';\n event: string;\n }\n | {\n type: 'handler';\n phase: 'receive' | 'validation_error' | 'handler_success' | 'handler_error';\n event: string;\n socketId?: string;\n nsp?: string;\n rooms?: string[];\n raw?: unknown;\n issues?: any[];\n error?: unknown;\n }\n | {\n type: 'emit';\n event: string;\n rooms: string[];\n envelope?: unknown;\n }\n | {\n type: 'rooms';\n action: 'join' | 'leave';\n rooms: string[];\n socketId: string;\n }\n | {\n type: 'auth';\n phase: 'ok' | 'error';\n socketId?: string;\n sub?: string;\n err?: string;\n }\n | {\n type: 'heartbeat';\n phase: 'ping' | 'pong';\n socketId: string;\n payload?: NoInfer<z.infer<Ping>> | NoInfer<z.infer<Pong>>;\n issues?: any[];\n error?: boolean;\n };\n\nexport type SocketDebugOptions<Ping extends ZodType, Pong extends ZodType> = {\n verbose?: boolean;\n only?: string[]; // filters event-based logs (register/handler/emit) by event name\n logger?: (e: SocketServerDebugEvent<Ping, Pong>) => void;\n} & {\n [P in SocketServerDebugEvent['type']]?: boolean;\n};\n\nexport type BuiltInRoomHooks = {\n onRoomJoin?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n onRoomLeave?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n};\n\n/** === NEW: enforced heartbeat config with schemas and callback */\nexport type HeartbeatServerOptions<Ping extends ZodType, Pong extends ZodType> = {\n pingEvent?: string; // default 'sys:ping'\n pongEvent?: string; // default 'sys:pong'\n\n /** Validate incoming ping payload: event will be { payload: <ping> }. */\n pingSchema: Ping;\n\n /** Build the pong payload you emit. It will receive the validated ping and socket. */\n makePongPayload: (args: { socket: Socket; ping: z.infer<Ping> }) => NoInfer<z.infer<Pong>>;\n\n /** Optionally validate the outgoing pong object before emit. */\n pongSchema?: Pong;\n};\n\nexport function createSocketConnections<\n Ping extends ZodType,\n Pong extends ZodType,\n T extends EventMap\n>(\n io: Server,\n events: T,\n opts?: {\n debug?: SocketDebugOptions<Ping, Pong>;\n rooms?: BuiltInRoomHooks;\n heartbeat: HeartbeatServerOptions<Ping, Pong>;\n }\n): SocketConnection<T> {\n const debug = opts?.debug ?? {};\n const dbg = (maybeEvent: string | null, e: SocketServerDebugEvent<Ping, Pong>) => {\n if (!debug.logger) return;\n if (!debug[e.type]) return;\n\n // only filter applies to event-shaped logs\n if (\n debug.only &&\n maybeEvent &&\n (e.type === 'register' || e.type === 'handler' || e.type === 'emit') &&\n !debug.only.includes(maybeEvent)\n ) {\n return;\n }\n\n debug.logger(e);\n };\n\n const getSchema = <K extends keyof T & string>(k: K) => events[k].message;\n const toArray = (rooms?: string[] | string): string[] =>\n rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n\n // registrations\n const registrations = new Map<\n string,\n Set<{\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }>\n >();\n\n const addRegistration = (\n eventName: string,\n reg: {\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }\n ) => {\n let set = registrations.get(eventName);\n if (!set) {\n set = new Set();\n registrations.set(eventName, set);\n }\n set.add(reg);\n };\n\n const removeAllForEvent = (eventName: string) => {\n const set = registrations.get(eventName);\n if (!set) return;\n for (const reg of set) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n }\n registrations.delete(eventName);\n dbg(eventName, { type: 'register', action: 'unregister', event: eventName });\n };\n\n const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? 'room:join';\n const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? 'room:leave';\n\n const hb = opts?.heartbeat;\n if (!hb) throw new Error('createSocketConnections: heartbeat config is required');\n const pingEvent = hb.pingEvent ?? 'sys:ping';\n const pongEvent = hb.pongEvent ?? 'sys:pong';\n\n io.on('connection', (socket) => {\n // auth summary\n if ((socket.data as any)?.user) {\n dbg(null, {\n type: 'auth',\n phase: 'ok',\n socketId: socket.id,\n sub: (socket.data as any).user?.sub,\n });\n }\n\n // built-in room handlers\n const joinHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n const allowed =\n !opts?.rooms?.onRoomJoin || (await opts.rooms.onRoomJoin({ room: r, socket })) === true;\n if (allowed) {\n await socket.join(r);\n dbg(null, { type: 'rooms', action: 'join', rooms: [r], socketId: socket.id });\n }\n }\n };\n const leaveHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n const allowed =\n !opts?.rooms?.onRoomLeave || (await opts.rooms.onRoomLeave({ room: r, socket })) === true;\n if (allowed) {\n dbg(null, { type: 'rooms', action: 'leave', rooms: [r], socketId: socket.id });\n await socket.leave(r);\n }\n }\n };\n socket.on(roomJoinEvent, joinHandler);\n socket.on(roomLeaveEvent, leaveHandler);\n socket.once('disconnect', () => {\n socket.off(roomJoinEvent, joinHandler);\n socket.off(roomLeaveEvent, leaveHandler);\n });\n\n // heartbeat → single bucket\n socket.on(pingEvent, (msg: { payload?: unknown } = {}, ack?: (x: { serverNow: string }) => void) => {\n const parsed = hb.pingSchema.safeParse(msg?.payload);\n if (!parsed.success) {\n socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });\n dbg(null, {\n type: 'heartbeat',\n phase: 'ping',\n socketId: socket.id,\n payload: msg?.payload as any,\n issues: parsed.error.issues,\n error: true,\n });\n return;\n }\n\n dbg(null, {\n type: 'heartbeat',\n phase: 'ping',\n socketId: socket.id,\n payload: parsed.data as any,\n });\n\n const pong = hb.makePongPayload({ socket, ping: parsed.data });\n\n if (hb.pongSchema) {\n const check = hb.pongSchema.safeParse(pong);\n if (!check.success) {\n socket.emit(`${pongEvent}:error`, { issues: check.error.issues });\n dbg(null, {\n type: 'heartbeat',\n phase: 'pong',\n socketId: socket.id,\n issues: check.error.issues,\n error: true,\n });\n return;\n }\n }\n\n socket.emit(pongEvent, pong);\n dbg(null, {\n type: 'heartbeat',\n phase: 'pong',\n socketId: socket.id,\n payload: pong as any,\n error: false,\n });\n\n if (typeof ack === 'function') {\n ack({ serverNow: new Date().toISOString() });\n }\n });\n });\n\n const conn = {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void {\n const socketListeners = new WeakMap<Socket, (raw: unknown) => void>();\n\n const connectionListener = (socket: Socket) => {\n const wrapped = async (raw: unknown, maybeAck?: (data?: unknown) => void) => {\n const schema = getSchema(eventName);\n const parsed = schema.safeParse(raw);\n\n const ctx: HandlerCtx = {\n sentAt: new Date(),\n socket,\n socketId: socket.id,\n nsp: socket.nsp.name,\n rooms: Array.from(socket.rooms),\n user: socket.data?.user,\n scopes: socket.data?.scopes,\n ack:\n typeof maybeAck === 'function'\n ? (d?: unknown) => {\n try {\n maybeAck(d);\n } catch {\n /* noop */\n }\n }\n : undefined,\n };\n\n dbg(String(eventName), {\n type: 'handler',\n phase: 'receive',\n event: String(eventName),\n socketId: ctx.socketId,\n nsp: ctx.nsp,\n rooms: ctx.rooms,\n raw: debug.verbose ? raw : undefined,\n });\n\n if (!parsed.success) {\n socket.emit(`${String(eventName)}:error`, {\n eventName,\n sentAt: ctx.sentAt,\n issues: parsed.error.issues,\n });\n dbg(String(eventName), {\n type: 'handler',\n phase: 'validation_error',\n event: String(eventName),\n issues: parsed.error.issues,\n });\n return;\n }\n\n try {\n await handler(parsed.data as Payload<T, K>, ctx);\n dbg(String(eventName), {\n type: 'handler',\n phase: 'handler_success',\n event: String(eventName),\n });\n } catch (error) {\n dbg(String(eventName), {\n type: 'handler',\n phase: 'handler_error',\n event: String(eventName),\n error,\n });\n throw error;\n }\n };\n\n socketListeners.set(socket, wrapped);\n socket.on(String(eventName), wrapped);\n socket.once('disconnect', () => {\n const w = socketListeners.get(socket);\n if (w) socket.off(String(eventName), w);\n });\n };\n\n io.on('connection', connectionListener);\n addRegistration(String(eventName), { connectionListener, socketListeners });\n dbg(String(eventName), {\n type: 'register',\n action: 'register',\n event: String(eventName),\n });\n\n return () => {\n const set = registrations.get(String(eventName));\n if (!set) return;\n for (const reg of Array.from(set)) {\n if (reg.connectionListener === connectionListener) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n set.delete(reg);\n break;\n }\n }\n if (set.size === 0) registrations.delete(String(eventName));\n dbg(String(eventName), {\n type: 'register',\n action: 'unregister',\n event: String(eventName),\n });\n };\n },\n\n off<K extends keyof T & string>(eventName: K): void {\n removeAllForEvent(String(eventName));\n },\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n rooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void {\n const schema = getSchema(eventName);\n const check = schema.safeParse(payload);\n if (!check.success)\n throw new Error(`Invalid payload for \"${String(eventName)}\": ${check.error.message}`);\n\n const targets = toArray(rooms);\n const envelope = {\n eventName,\n sentAt: new Date(),\n sentTo: targets,\n data: check.data as Payload<T, K>,\n metadata: metadata ?? {},\n };\n\n // single socket target\n if (targets.length === 1) {\n const sid = targets[0]!;\n const sock = io.sockets.sockets.get(sid as any);\n if (sock) {\n if (onAck) {\n sock.emit(String(eventName), envelope, (ack: unknown) => {\n try {\n onAck(ack);\n } catch {\n /* noop */\n }\n });\n } else {\n sock.emit(String(eventName), envelope);\n }\n dbg(String(eventName), {\n type: 'emit',\n event: String(eventName),\n rooms: targets,\n envelope: debug.verbose ? envelope : undefined,\n });\n return;\n }\n }\n\n // broadcast\n if (targets.length === 0) io.emit(String(eventName), envelope);\n else io.to(targets).emit(String(eventName), envelope);\n\n dbg(String(eventName), {\n type: 'emit',\n event: String(eventName),\n rooms: targets,\n envelope: debug.verbose ? envelope : undefined,\n });\n },\n } as const satisfies SocketConnection<T>;\n\n return conn;\n}\n\nexport type { EventMap, Payload };\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -117,7 +117,7 @@ function createRRRoute(router, config) {
117
117
  if (!isVerbose || !details) return event;
118
118
  return { ...event, ...details };
119
119
  };
120
- const globalBeforeMws = [...config.global ?? [], ...config.globalMiddleware?.before ?? []].map(
120
+ const globalBeforeMws = [...config.globalMiddleware?.before ?? []].map(
121
121
  (mw) => adaptCtxMw(mw)
122
122
  );
123
123
  const globalAfterMws = mapAfterMiddleware(config.globalMiddleware?.after);
@@ -388,9 +388,12 @@ function warnMissingControllers(router, registry, logger) {
388
388
  // src/sockets/socket.server.index.ts
389
389
  function createSocketConnections(io, events, opts) {
390
390
  const debug = opts?.debug ?? {};
391
- const dbg = (name, e) => {
392
- if (!debug.logger || !debug[e.type]) return;
393
- if (debug.only && !debug.only.includes(name) && e.type === "receive") return;
391
+ const dbg = (maybeEvent, e) => {
392
+ if (!debug.logger) return;
393
+ if (!debug[e.type]) return;
394
+ if (debug.only && maybeEvent && (e.type === "register" || e.type === "handler" || e.type === "emit") && !debug.only.includes(maybeEvent)) {
395
+ return;
396
+ }
394
397
  debug.logger(e);
395
398
  };
396
399
  const getSchema = (k) => events[k].message;
@@ -415,34 +418,39 @@ function createSocketConnections(io, events, opts) {
415
418
  }
416
419
  }
417
420
  registrations.delete(eventName);
418
- dbg(eventName, { type: "unregister", event: eventName });
421
+ dbg(eventName, { type: "register", action: "unregister", event: eventName });
419
422
  };
420
423
  const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? "room:join";
421
424
  const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? "room:leave";
422
425
  const hb = opts?.heartbeat;
423
- if (!hb) {
424
- throw new Error("createSocketConnections: heartbeat config is required");
425
- }
426
+ if (!hb) throw new Error("createSocketConnections: heartbeat config is required");
426
427
  const pingEvent = hb.pingEvent ?? "sys:ping";
427
428
  const pongEvent = hb.pongEvent ?? "sys:pong";
428
429
  io.on("connection", (socket) => {
429
430
  if (socket.data?.user) {
430
- dbg("auth_ok", { type: "auth_ok", socketId: socket.id, sub: socket.data.user?.sub });
431
+ dbg(null, {
432
+ type: "auth",
433
+ phase: "ok",
434
+ socketId: socket.id,
435
+ sub: socket.data.user?.sub
436
+ });
431
437
  }
432
438
  const joinHandler = async ({ rooms }) => {
433
439
  const list = toArray(rooms);
434
440
  for (const r of list) {
435
- if (!opts?.rooms?.onRoomJoin || await opts.rooms.onRoomJoin({ room: r, socket }) == true) {
441
+ const allowed = !opts?.rooms?.onRoomJoin || await opts.rooms.onRoomJoin({ room: r, socket }) === true;
442
+ if (allowed) {
436
443
  await socket.join(r);
437
- dbg("room_join", { type: "room_join", rooms: [r], socketId: socket.id });
444
+ dbg(null, { type: "rooms", action: "join", rooms: [r], socketId: socket.id });
438
445
  }
439
446
  }
440
447
  };
441
448
  const leaveHandler = async ({ rooms }) => {
442
449
  const list = toArray(rooms);
443
450
  for (const r of list) {
444
- if (!opts?.rooms?.onRoomLeave || await opts.rooms.onRoomLeave({ room: r, socket }) == true) {
445
- dbg("room_leave", { type: "room_leave", rooms: [r], socketId: socket.id });
451
+ const allowed = !opts?.rooms?.onRoomLeave || await opts.rooms.onRoomLeave({ room: r, socket }) === true;
452
+ if (allowed) {
453
+ dbg(null, { type: "rooms", action: "leave", rooms: [r], socketId: socket.id });
446
454
  await socket.leave(r);
447
455
  }
448
456
  }
@@ -457,21 +465,48 @@ function createSocketConnections(io, events, opts) {
457
465
  const parsed = hb.pingSchema.safeParse(msg?.payload);
458
466
  if (!parsed.success) {
459
467
  socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });
468
+ dbg(null, {
469
+ type: "heartbeat",
470
+ phase: "ping",
471
+ socketId: socket.id,
472
+ payload: msg?.payload,
473
+ issues: parsed.error.issues,
474
+ error: true
475
+ });
460
476
  return;
461
477
  }
462
- dbg("ping", { type: "ping", socketId: socket.id, payload: parsed.data });
478
+ dbg(null, {
479
+ type: "heartbeat",
480
+ phase: "ping",
481
+ socketId: socket.id,
482
+ payload: parsed.data
483
+ });
463
484
  const pong = hb.makePongPayload({ socket, ping: parsed.data });
464
485
  if (hb.pongSchema) {
465
486
  const check = hb.pongSchema.safeParse(pong);
466
487
  if (!check.success) {
467
488
  socket.emit(`${pongEvent}:error`, { issues: check.error.issues });
468
- dbg("pong", { type: "pong", socketId: socket.id, issues: check.error.issues, error: true });
489
+ dbg(null, {
490
+ type: "heartbeat",
491
+ phase: "pong",
492
+ socketId: socket.id,
493
+ issues: check.error.issues,
494
+ error: true
495
+ });
469
496
  return;
470
497
  }
471
498
  }
472
499
  socket.emit(pongEvent, pong);
473
- dbg("pong", { type: "pong", socketId: socket.id, payload: pong, error: false });
474
- if (typeof ack === "function") ack({ serverNow: (/* @__PURE__ */ new Date()).toISOString() });
500
+ dbg(null, {
501
+ type: "heartbeat",
502
+ phase: "pong",
503
+ socketId: socket.id,
504
+ payload: pong,
505
+ error: false
506
+ });
507
+ if (typeof ack === "function") {
508
+ ack({ serverNow: (/* @__PURE__ */ new Date()).toISOString() });
509
+ }
475
510
  });
476
511
  });
477
512
  const conn = {
@@ -495,10 +530,10 @@ function createSocketConnections(io, events, opts) {
495
530
  } catch {
496
531
  }
497
532
  } : void 0
498
- // NEW
499
533
  };
500
534
  dbg(String(eventName), {
501
- type: "receive",
535
+ type: "handler",
536
+ phase: "receive",
502
537
  event: String(eventName),
503
538
  socketId: ctx.socketId,
504
539
  nsp: ctx.nsp,
@@ -506,15 +541,33 @@ function createSocketConnections(io, events, opts) {
506
541
  raw: debug.verbose ? raw : void 0
507
542
  });
508
543
  if (!parsed.success) {
509
- socket.emit(`${String(eventName)}:error`, { eventName, sentAt: ctx.sentAt, issues: parsed.error.issues });
510
- dbg(String(eventName), { type: "validation_error", event: String(eventName), issues: parsed.error.issues });
544
+ socket.emit(`${String(eventName)}:error`, {
545
+ eventName,
546
+ sentAt: ctx.sentAt,
547
+ issues: parsed.error.issues
548
+ });
549
+ dbg(String(eventName), {
550
+ type: "handler",
551
+ phase: "validation_error",
552
+ event: String(eventName),
553
+ issues: parsed.error.issues
554
+ });
511
555
  return;
512
556
  }
513
557
  try {
514
558
  await handler(parsed.data, ctx);
515
- dbg(String(eventName), { type: "handler_success", event: String(eventName) });
559
+ dbg(String(eventName), {
560
+ type: "handler",
561
+ phase: "handler_success",
562
+ event: String(eventName)
563
+ });
516
564
  } catch (error) {
517
- dbg(String(eventName), { type: "handler_error", event: String(eventName), error });
565
+ dbg(String(eventName), {
566
+ type: "handler",
567
+ phase: "handler_error",
568
+ event: String(eventName),
569
+ error
570
+ });
518
571
  throw error;
519
572
  }
520
573
  };
@@ -527,7 +580,11 @@ function createSocketConnections(io, events, opts) {
527
580
  };
528
581
  io.on("connection", connectionListener);
529
582
  addRegistration(String(eventName), { connectionListener, socketListeners });
530
- dbg(String(eventName), { type: "register", event: String(eventName) });
583
+ dbg(String(eventName), {
584
+ type: "register",
585
+ action: "register",
586
+ event: String(eventName)
587
+ });
531
588
  return () => {
532
589
  const set = registrations.get(String(eventName));
533
590
  if (!set) return;
@@ -543,7 +600,11 @@ function createSocketConnections(io, events, opts) {
543
600
  }
544
601
  }
545
602
  if (set.size === 0) registrations.delete(String(eventName));
546
- dbg(String(eventName), { type: "unregister", event: String(eventName) });
603
+ dbg(String(eventName), {
604
+ type: "register",
605
+ action: "unregister",
606
+ event: String(eventName)
607
+ });
547
608
  };
548
609
  },
549
610
  off(eventName) {
@@ -552,7 +613,8 @@ function createSocketConnections(io, events, opts) {
552
613
  emit(eventName, payload, rooms, metadata, onAck) {
553
614
  const schema = getSchema(eventName);
554
615
  const check = schema.safeParse(payload);
555
- if (!check.success) throw new Error(`Invalid payload for "${String(eventName)}": ${check.error.message}`);
616
+ if (!check.success)
617
+ throw new Error(`Invalid payload for "${String(eventName)}": ${check.error.message}`);
556
618
  const targets = toArray(rooms);
557
619
  const envelope = {
558
620
  eventName,
@@ -565,20 +627,33 @@ function createSocketConnections(io, events, opts) {
565
627
  const sid = targets[0];
566
628
  const sock = io.sockets.sockets.get(sid);
567
629
  if (sock) {
568
- if (onAck) sock.emit(String(eventName), envelope, (ack) => {
569
- try {
570
- onAck(ack);
571
- } catch {
572
- }
630
+ if (onAck) {
631
+ sock.emit(String(eventName), envelope, (ack) => {
632
+ try {
633
+ onAck(ack);
634
+ } catch {
635
+ }
636
+ });
637
+ } else {
638
+ sock.emit(String(eventName), envelope);
639
+ }
640
+ dbg(String(eventName), {
641
+ type: "emit",
642
+ event: String(eventName),
643
+ rooms: targets,
644
+ envelope: debug.verbose ? envelope : void 0
573
645
  });
574
- else sock.emit(String(eventName), envelope);
575
- dbg(String(eventName), { type: "emit", event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : void 0 });
576
646
  return;
577
647
  }
578
648
  }
579
649
  if (targets.length === 0) io.emit(String(eventName), envelope);
580
650
  else io.to(targets).emit(String(eventName), envelope);
581
- dbg(String(eventName), { type: "emit", event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : void 0 });
651
+ dbg(String(eventName), {
652
+ type: "emit",
653
+ event: String(eventName),
654
+ rooms: targets,
655
+ envelope: debug.verbose ? envelope : void 0
656
+ });
582
657
  }
583
658
  };
584
659
  return conn;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/routesV3.server.ts","../src/sockets/socket.server.index.ts"],"sourcesContent":["/**\n * routesV3.server.ts\n * -----------------------------------------------------------------------------\n * Bind an Express router/app to a `finalize(...)` registry of AnyLeafs.\n * - Fully typed handlers (params/query/body/output)\n * - Zod parsing + optional output validation\n * - buildCtx runs as a middleware *first*, before all other middlewares\n * - Global, per-route, and cfg-derived middlewares (auth, uploads)\n * - Helper to warn about unimplemented routes\n * - DX helpers to use `ctx` in any middleware with proper types\n */\n\nimport { AnyLeaf, FileField, HttpMethod, InferBody, InferOutput, InferParams, InferQuery, MethodCfg } from '@emeryld/rrroutes-contract';\nimport type * as express from 'express';\nimport type { RequestHandler, Router } from 'express';\nimport type { ZodType } from 'zod';\n\n\n\n/** Shape expected from optional logger implementations. */\nexport type LoggerLike = {\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n debug?: (...args: any[]) => void;\n verbose?: (...args: any[]) => void;\n system?: (...args: any[]) => void;\n log?: (...args: any[]) => void;\n};\n\ntype RoutesLoggerCarrier = {\n routesLogger?: LoggerLike;\n};\n\ntype CtxWithRoutesLogger<Ctx> = Ctx & RoutesLoggerCarrier;\n\n// Debug logging --------------------------------------------------------------\nexport type RouteServerDebugMode = 'minimal' | 'complete';\n\ntype RouteServerDebugEventBase = {\n /** Optional logical name assigned via `RouteDef.debug?.debugName` (or `RouteDef.debugName`). */\n name?: string;\n};\n\nexport type RouteServerDebugEvent =\n | (RouteServerDebugEventBase & {\n type: 'request';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'register';\n method: Uppercase<HttpMethod>;\n path: string;\n })\n | (RouteServerDebugEventBase & {\n type: 'buildCtx';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'handler';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n });\n\nexport type RouteServerDebugLogger = (event: RouteServerDebugEvent) => void;\n\n/**\n * Configure server-side debug logging.\n * - Use booleans or `'minimal'/'complete'` for quick toggles.\n * - Pass a custom logger function to redirect structured events.\n * - Provide a map to enable specific event types, opt into verbose payload logging, or restrict logs via `only`.\n */\nexport type RouteServerDebugOptions<Names extends string = string> = RouteServerDebugToggleOptions<Names>;\n\n/**\n * Fine-grained toggle map for server debug logging.\n * Enable individual event types, opt into verbose payload logging, override the logger, or restrict to named routes.\n * Use `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`) to set the name that `only` will match against.\n */\nexport type RouteServerDebugToggleOptions<Names extends string = string> = Partial<\n Record<RouteServerDebugEvent['type'], boolean>\n> & {\n verbose?: boolean;\n logger?: RouteServerDebugLogger;\n only?: Names[];\n};\n\n/**\n * Per-route debug overrides. Same toggles as `RouteServerDebugOptions`, but limited to a single route\n * and therefore replaces the `only` filter with a local `debugName`.\n */\nexport type RouteDefDebugOptions<Names extends string = string> = Omit<\n RouteServerDebugToggleOptions<Names>,\n 'only'\n> & {\n debugName?: Names;\n};\n\nconst serverDebugEventTypes: RouteServerDebugEvent['type'][] = [\n 'register',\n 'request',\n 'buildCtx',\n 'handler',\n];\n\ntype ServerDebugEmitter<Names extends string> = {\n emit: (event: RouteServerDebugEvent, name?: Names) => void;\n mode: RouteServerDebugMode;\n};\n\nconst noopServerEmit = () => {};\n\nfunction createServerDebugEmitter<Names extends string>(\n option?: RouteServerDebugOptions<Names>,\n): ServerDebugEmitter<Names> {\n const disabled: ServerDebugEmitter<Names> = { emit: noopServerEmit, mode: 'minimal' };\n if (!option) return disabled;\n\n if (typeof option === 'object') {\n const toggles = option as RouteServerDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = serverDebugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopServerEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteServerDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger;\n const emit: ServerDebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type) || !logger) return;\n if (onlySet && (!name || !onlySet.has(name))) return;\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Keys + leaf helpers (derive keys from byKey to avoid template-literal pitfalls)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Keys like \"GET /v1/foo\" that *actually* exist in the registry */\nexport type KeysOfRegistry<R extends { byKey: Record<string, AnyLeaf> }> = keyof R['byKey'] &\n string;\n\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n/** Given a registry and a key, pick the exact leaf for that method+path */\nexport type LeafFromKey<R extends { all: readonly AnyLeaf[] }, K extends string> = Extract<\n R['all'][number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/** Optional-ify types if your core returns `never` when a schema isn't defined */\ntype Maybe<T> = [T] extends [never] ? undefined : T;\n\n/** Typed params argument exposed to handlers. */\nexport type ArgParams<L extends AnyLeaf> = Maybe<InferParams<L>>;\n/** Typed query argument exposed to handlers. */\nexport type ArgQuery<L extends AnyLeaf> = Maybe<InferQuery<L>>;\n/** Typed body argument exposed to handlers. */\nexport type ArgBody<L extends AnyLeaf> = Maybe<InferBody<L>>;\n\n/**\n * Convenience to compute a `\"METHOD /path\"` key from a leaf.\n * @param leaf Leaf describing the route.\n * @returns Uppercase method + path key.\n */\nexport const keyOf = (leaf: AnyLeaf) => `${leaf.method.toUpperCase()} ${leaf.path}` as const;\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Context typing & DX helpers (so ctx is usable in *any* middleware)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Unique symbol used to stash ctx on res.locals.\n * (Symbols are safer than string keys against collisions.)\n */\nexport const CTX_SYMBOL: unique symbol = Symbol.for('typedLeaves.ctx');\n\nconst AFTER_HANDLER_NEXT_SYMBOL: unique symbol = Symbol.for('typedLeaves.afterHandlerNext');\n\nfunction setAfterHandlerNext(res: express.Response, value: boolean) {\n (res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL] = value;\n}\n\nfunction handlerInvokedNext(res: express.Response): boolean {\n return Boolean((res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL]);\n}\n\n/** Response type that *has* a ctx on locals for DX in middlewares */\nexport type ResponseWithCtx<Ctx> =\n // Replace locals with an intersection that guarantees CTX_SYMBOL exists\n Omit<express.Response, 'locals'> & {\n locals: express.Response['locals'] & { [CTX_SYMBOL]: CtxWithRoutesLogger<Ctx> };\n };\n\n/** A middleware signature that can *use* ctx via `res.locals[CTX_SYMBOL]` */\nexport type CtxRequestHandler<Ctx> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n}) => any;\n\n/**\n * Safely read ctx from any Response.\n * @param res Express response whose locals contain the ctx symbol.\n * @returns Strongly typed context object.\n */\nexport function getCtx<Ctx = unknown>(res: express.Response): CtxWithRoutesLogger<Ctx> {\n return (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n}\n\n/**\n * Wrap a ctx-typed middleware to a plain RequestHandler (for arrays, etc.).\n * @param mw Middleware that expects a typed response with ctx available.\n * @returns Standard Express request handler.\n */\nfunction adaptCtxMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n return (req, res, next) => {\n try {\n const result = mw({ req, res, next, ctx: getCtx<Ctx>(res) });\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).catch((err) => next(err));\n }\n return result as any;\n } catch (err) {\n next(err as any);\n return undefined;\n }\n };\n}\n\nfunction adaptAfterMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n const adapted = adaptCtxMw<Ctx>(mw);\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n if (!handlerInvokedNext(res)) {\n next();\n return;\n }\n adapted(req, res, next);\n };\n}\n\nfunction mapAfterMiddleware<Ctx>(mws?: Array<CtxRequestHandler<Ctx>>): RequestHandler[] {\n return (mws ?? []).map((mw) => adaptAfterMw<Ctx>(mw));\n}\n\nfunction logHandlerDebugWithRoutesLogger(\n logger: LoggerLike | undefined,\n event: RouteServerDebugEvent,\n) {\n if (!logger || event.type !== 'handler') return;\n const payload: [string, RouteServerDebugEvent] = [\n `[rrroutes-server][handler:${event.stage}] ${event.method} ${event.path}`,\n event,\n ];\n if (event.stage === 'error') {\n (\n logger.error ??\n logger.warn ??\n logger.debug ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n return;\n }\n (\n logger.debug ??\n logger.verbose ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Controller types — object form only (simpler, clearer typings)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Typed route handler for a specific leaf */\nexport type Handler<L extends AnyLeaf, Ctx = unknown> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n params: ArgParams<L>;\n query: ArgQuery<L>;\n body: ArgBody<L>;\n}) => Promise<InferOutput<L>> | InferOutput<L>;\n\n/** Route definition for one key */\nexport type RouteDef<L extends AnyLeaf, Ctx = unknown, Names extends string = string> = {\n /** Middlewares before the handler (run after buildCtx/global/derived) */\n use?: Array<CtxRequestHandler<Ctx>>;\n /** Middlewares after the handler *if* it calls next() */\n after?: Array<CtxRequestHandler<Ctx>>;\n /** Your business logic */\n handler: Handler<L, Ctx>;\n /**\n * Optional per-route debug overrides. When provided, these replace the global debug options.\n */\n debug?: RouteDefDebugOptions<Names>;\n /**\n * Optional logical name used for debug filtering. Prefer `debug.debugName`; this field remains for backwards compatibility.\n */\n debugName?: Names;\n};\n\n/** Map of registry keys -> route defs */\nexport type ControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = {\n [P in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, P>, Ctx, Names>;\n};\n\nexport type PartialControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = Partial<ControllerMap<R, Ctx, Names>>;\n\n// ──────────────────────────────────────────────────────────────────────────────\n/** Options + derivation helpers */\n// ──────────────────────────────────────────────────────────────────────────────\n\nexport type RouteServerConfig<Ctx = unknown, Names extends string = string> = {\n /**\n * Build a request-scoped context. We wrap this in a middleware that runs\n * *first* (before global/derived/route middlewares), and stash it on\n * `res.locals[CTX_SYMBOL]` so *all* later middlewares can use it.\n * You can optionally include `routesLogger` to override handler debug logging per request.\n */\n buildCtx: (\n req: express.Request,\n res: express.Response,\n ) => CtxWithRoutesLogger<Ctx> | Promise<CtxWithRoutesLogger<Ctx>>;\n\n /**\n * Global middlewares for every bound route (run *after* buildCtx). Prefer `globalMiddleware.before`.\n */\n global?: Array<CtxRequestHandler<Ctx>>;\n\n /**\n * Grouped global middlewares that run *before* or *after* the handler for every route.\n */\n globalMiddleware?: {\n before?: Array<CtxRequestHandler<Ctx>>;\n after?: Array<CtxRequestHandler<Ctx>>;\n };\n\n /**\n * Derive middleware from MethodCfg.\n * - `upload` runs when cfg.bodyFiles has entries\n */\n fromCfg?: {\n upload?: (files: FileField[] | undefined, leaf: AnyLeaf) => RequestHandler[];\n };\n\n /** Validate handler return values with outputSchema (default: true) */\n validateOutput?: boolean;\n\n /** Custom responder (default: res.json(data)) */\n send?: (res: express.Response, data: unknown) => void;\n\n /** Optional logger hooks */\n logger?: LoggerLike;\n\n /**\n * Optional debug logging for the request lifecycle.\n * Supports booleans/modes/loggers, or a toggle map with per-event enabling, verbose payload logging,\n * and `only` filters tied to `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`).\n */\n debug?: RouteServerDebugOptions<Names>;\n};\n\n/** Default JSON responder (typed to avoid implicit-any diagnostics) */\nconst defaultSend: (res: express.Response, data: unknown) => void = (res, data) => {\n res.json(data as any);\n};\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Core builder\n// ──────────────────────────────────────────────────────────────────────────────\n\nconst REGISTERED_ROUTES_SYMBOL = Symbol.for('routesV3.registeredRoutes');\n\ntype RegisteredRouteStore = Set<string>;\n\n/**\n * Retrieve or initialize the shared store of registered route keys.\n * @param router Express router/application that carries previously registered keys.\n * @returns Set of string keys describing registered routes.\n */\nfunction getRegisteredRouteStore(router: Router): RegisteredRouteStore {\n const existing = (router as any)[REGISTERED_ROUTES_SYMBOL] as RegisteredRouteStore | undefined;\n if (existing) return existing;\n const store: RegisteredRouteStore = new Set();\n (router as any)[REGISTERED_ROUTES_SYMBOL] = store;\n return store;\n}\n\n/**\n * Inspect the Express layer stack to discover already-registered routes.\n * @param appOrRouter Express application or router to inspect.\n * @returns All keys in the form `\"METHOD /path\"` found on the stack.\n */\nfunction collectRoutesFromStack(appOrRouter: Router): string[] {\n const result: string[] = [];\n const stack: any[] =\n (appOrRouter as any).stack ??\n ((appOrRouter as any)._router ? (appOrRouter as any)._router.stack : undefined) ??\n [];\n\n if (!Array.isArray(stack)) return result;\n\n for (const layer of stack) {\n const route = layer && layer.route;\n if (!route) continue;\n\n const paths = Array.isArray(route.path) ? route.path : [route.path];\n const methodEntries = Object.entries(route.methods ?? {}).filter(([, enabled]) => enabled);\n\n for (const path of paths) {\n for (const [method] of methodEntries) {\n result.push(`${method.toUpperCase()} ${path}`);\n }\n }\n }\n\n return result;\n}\n\n/** Runtime helpers returned by `createRRRoute`. */\nexport type RouteServer<Ctx = unknown, Names extends string = string> = {\n router: Router;\n register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>): void;\n registerControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n ): void;\n warnMissingControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n logger: { warn: (...args: any[]) => void },\n ): void;\n getRegisteredKeys(): string[];\n};\n\n/**\n * Create an Express binding helper that keeps routes and controllers in sync.\n * @param router Express router or app to register handlers on.\n * @param config Optional configuration controlling ctx building, auth, uploads, etc.\n * @returns Object with helpers to register controllers and inspect registered keys.\n */\nexport function createRRRoute<Ctx = unknown, Names extends string = string>(\n router: Router,\n config: RouteServerConfig<Ctx, Names>,\n): RouteServer<Ctx, Names> {\n const validateOutput = config.validateOutput ?? true;\n const send = config.send ?? defaultSend;\n const logger = config.logger;\n const { emit: defaultEmitDebug, mode: defaultDebugMode } =\n createServerDebugEmitter<Names>(config.debug);\n const decorateDebugEvent = <T extends RouteServerDebugEvent>(\n isVerbose: boolean,\n event: T,\n details?: Partial<RouteServerDebugEvent>,\n ): RouteServerDebugEvent => {\n if (!isVerbose || !details) return event;\n return { ...event, ...details } as RouteServerDebugEvent;\n };\n\n const globalBeforeMws = [...(config.global ?? []), ...(config.globalMiddleware?.before ?? [])].map(\n (mw) => adaptCtxMw<Ctx>(mw),\n );\n const globalAfterMws = mapAfterMiddleware<Ctx>(config.globalMiddleware?.after);\n const registered = getRegisteredRouteStore(router);\n\n const buildDerived = (leaf: AnyLeaf): RequestHandler[] => {\n const derived: RequestHandler[] = [];\n if (\n config.fromCfg?.upload &&\n Array.isArray(leaf.cfg.bodyFiles) &&\n leaf.cfg.bodyFiles.length > 0\n ) {\n derived.push(...config.fromCfg.upload(leaf.cfg.bodyFiles, leaf));\n }\n\n return derived;\n };\n\n /** Register a single leaf/controller pair on the underlying router. */\n function register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>) {\n const method = leaf.method as HttpMethod;\n const methodUpper = method.toUpperCase() as Uppercase<HttpMethod>;\n const path = leaf.path as string;\n const key = keyOf(leaf);\n const defDebug = def.debug;\n let debugName = def.debugName as Names | undefined;\n let routeDebugEmitter: ServerDebugEmitter<Names> | undefined;\n if (defDebug) {\n const { debugName: overrideName, ...rest } = defDebug;\n const hasOverrides = Object.values(rest).some((value) => value !== undefined);\n if (hasOverrides) {\n routeDebugEmitter = createServerDebugEmitter<Names>(\n rest as RouteServerDebugOptions<Names>,\n );\n }\n debugName = (overrideName ?? debugName) as Names | undefined;\n }\n const activeEmit = routeDebugEmitter?.emit ?? defaultEmitDebug;\n const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;\n const emit = (event: RouteServerDebugEvent) => activeEmit(event, debugName);\n const isVerboseDebug = activeDebugMode === 'complete';\n emit({ type: 'register', method: methodUpper, path });\n\n const routeSpecific = (def?.use ?? []).map((mw) => adaptCtxMw<Ctx>(mw));\n const derived = buildDerived(leaf);\n const ctxMw: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl ?? path;\n const startedAt = Date.now();\n emit({ type: 'buildCtx', stage: 'start', method: methodUpper, path, url: requestUrl });\n try {\n const ctx = await config.buildCtx(req, res);\n (res.locals as any)[CTX_SYMBOL] = ctx;\n emit({\n type: 'buildCtx',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n });\n next();\n } catch (err) {\n emit({\n type: 'buildCtx',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n logger?.error?.('buildCtx error', err);\n next(err as any);\n }\n };\n const before: RequestHandler[] = [ctxMw, ...globalBeforeMws, ...derived, ...routeSpecific];\n\n const wrapped: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl.split('?')[0] ?? path;\n const startedAt = Date.now();\n emit({ type: 'request', stage: 'start', method: methodUpper, path, url: requestUrl });\n let params: ArgParams<L> | undefined;\n let query: ArgQuery<L> | undefined;\n let body: ArgBody<L> | undefined;\n let responsePayload: InferOutput<L> | undefined;\n let hasResponsePayload = false;\n setAfterHandlerNext(res, false);\n let handlerCalledNext = false;\n const downstreamNext: express.NextFunction = (err?: any) => {\n handlerCalledNext = true;\n setAfterHandlerNext(res, true);\n next(err);\n };\n\n logger?.info?.(`${methodUpper}@${path} (${requestUrl})`);\n const ctx = (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n const ctxRoutesLogger = ctx?.routesLogger;\n const emitWithCtx = (\n event: RouteServerDebugEvent,\n details?: Partial<RouteServerDebugEvent>,\n ) => {\n const decorated = decorateDebugEvent(isVerboseDebug, event, details);\n if (decorated.type === 'handler' && ctxRoutesLogger) {\n logHandlerDebugWithRoutesLogger(ctxRoutesLogger, decorated);\n }else{\n emit(decorated);\n }\n };\n try {\n params = (\n leaf.cfg.paramsSchema\n ? (leaf.cfg.paramsSchema as ZodType).parse(req.params)\n : Object.keys(req.params || {}).length\n ? (req.params as any)\n : undefined\n ) as ArgParams<L>;\n\n try {\n query = leaf.cfg.querySchema\n ? (leaf.cfg.querySchema as ZodType).parse(req.query)\n : Object.keys(req.query || {}).length\n ? (req.query as any)\n : undefined;\n } catch (e) {\n logger?.error?.('Query parsing error', {\n path,\n method: methodUpper,\n error: e,\n raw: JSON.stringify(req.query),\n });\n throw e;\n }\n\n body = (\n leaf.cfg.bodySchema\n ? (leaf.cfg.bodySchema as ZodType).parse(req.body)\n : req.body !== undefined\n ? (req.body as any)\n : undefined\n ) as ArgBody<L>;\n\n logger?.verbose?.(`${methodUpper}@${path} (${requestUrl})`, {\n params,\n query,\n body,\n });\n\n const handlerStartedAt = Date.now();\n emitWithCtx(\n {\n type: 'handler',\n stage: 'start',\n method: methodUpper,\n path,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n\n let result;\n try {\n result = await def.handler({\n req,\n res,\n next: downstreamNext,\n ctx,\n params: params as ArgParams<L>,\n query: query as ArgQuery<L>,\n body: body as ArgBody<L>,\n });\n emitWithCtx(\n {\n type: 'handler',\n stage: 'success',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(result !== undefined ? { output: result } : {}),\n }\n : undefined,\n );\n } catch (e) {\n emitWithCtx(\n {\n type: 'handler',\n stage: 'error',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n error: e,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Handler error', e);\n throw e;\n }\n\n if (!res.headersSent) {\n if (result !== undefined) {\n const out =\n validateOutput && leaf.cfg.outputSchema\n ? (leaf.cfg.outputSchema as ZodType).parse(result)\n : result;\n responsePayload = out as InferOutput<L>;\n hasResponsePayload = true;\n logger?.verbose?.(`${methodUpper}@${path} result`, out);\n send(res, out);\n } else if (!handlerCalledNext) {\n next();\n }\n }\n\n emitWithCtx(\n {\n type: 'request',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(hasResponsePayload ? { output: responsePayload } : {}),\n }\n : undefined,\n );\n } catch (err) {\n emitWithCtx(\n {\n type: 'request',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Route error', err);\n next(err as any);\n }\n };\n\n const after = [...mapAfterMiddleware<Ctx>(def?.after), ...globalAfterMws];\n (router as any)[method](path, ...before, wrapped, ...after);\n registered.add(key);\n }\n\n /**\n * Register controller definitions for the provided keys.\n * @param registry Finalized registry of leaves.\n * @param controllers Partial controller map keyed by `\"METHOD /path\"`.\n */\n function registerControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n >(registry: R, controllers: PartialControllerMap<R, Ctx, Names>) {\n (Object.keys(controllers) as Array<KeysOfRegistry<R>>).forEach((key) => {\n const leaf = registry.byKey[key] as unknown as LeafFromKey<R, typeof key> | undefined;\n if (!leaf) {\n logger?.warn?.(`No leaf found for controller key: ${key}. Not registering route.`);\n return;\n }\n const def = controllers[key];\n if (!def) return;\n register(leaf as LeafFromKey<R, typeof key>, def);\n });\n }\n\n /**\n * Warn about leaves that do not have a registered controller.\n * @param registry Finalized registry of leaves.\n * @param warnLogger Logger used for warning output.\n */\n function warnMissing<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n warnLogger: { warn: (...args: any[]) => void },\n ) {\n const registeredFromStore = new Set<string>(Array.from(registered));\n if (registeredFromStore.size === 0) {\n collectRoutesFromStack(router).forEach((key) => registeredFromStore.add(key));\n }\n for (const leaf of registry.all) {\n const key = keyOf(leaf);\n if (!registeredFromStore.has(key)) {\n warnLogger.warn(`No controller registered for route: ${key}`);\n }\n }\n }\n\n return {\n router,\n register,\n registerControllers,\n warnMissingControllers: warnMissing,\n getRegisteredKeys: () => Array.from(registered),\n };\n}\n\n/**\n * Bind only the controllers that are present in the provided map.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Partial map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindExpressRoutes<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n/**\n * Bind controllers for every leaf. Missing entries fail at compile time.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Complete map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindAll<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: { [K in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, K>, Ctx, Names> },\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// DX helpers\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Helper for great IntelliSense when authoring controller maps.\n * @returns Function that enforces key names while preserving partial flexibility.\n */\nexport const defineControllers =\n <\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n >() =>\n <M extends PartialControllerMap<R, Ctx, Names>>(m: M) =>\n m;\n\n/**\n * Warn about leaves that don't have controllers.\n * Call this during startup to surface missing routes.\n * @param router Express router or app to inspect.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param logger Logger where warnings are emitted.\n */\nexport function warnMissingControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n>(router: Router, registry: R, logger: { warn: (...args: any[]) => void }) {\n const registeredStore = (router as any)[REGISTERED_ROUTES_SYMBOL] as Set<string> | undefined;\n const initial = registeredStore ? Array.from(registeredStore) : collectRoutesFromStack(router);\n const registeredKeys = new Set<string>(initial);\n\n for (const leaf of registry.all) {\n const k = keyOf(leaf);\n if (!registeredKeys.has(k)) {\n logger.warn(`No controller registered for route: ${k}`);\n }\n }\n}\n","// socket.server.index.ts\nimport { Server, Socket } from 'socket.io';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\ntype EventMap = Record<string, SocketEvent>;\ntype Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\nexport type HandlerCtx = {\n sentAt: Date;\n socket: Socket;\n socketId: string;\n nsp: string;\n rooms: string[];\n user?: unknown;\n scopes?: string[];\n ack?: (data?: unknown) => void; \n};\n\nexport interface SocketConnection<T extends EventMap> {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void;\n\n off<K extends keyof T & string>(eventName: K): void;\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n toRooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void;\n}\n\nexport type SocketServerDebugEvent<Ping extends ZodType=ZodType, Pong extends ZodType=ZodType> =\n | { type: 'register'; event: string }\n | { type: 'unregister'; event: string }\n | { type: 'receive'; event: string; socketId: string; nsp: string; rooms: string[]; raw?: unknown }\n | { type: 'validation_error'; event: string; issues: any[] }\n | { type: 'handler_success'; event: string }\n | { type: 'handler_error'; event: string; error: unknown }\n | { type: 'emit'; event: string; rooms: string[]; envelope?: unknown }\n | { type: 'room_join'; rooms: string[]; socketId: string }\n | { type: 'room_leave'; rooms: string[]; socketId: string }\n | { type: 'auth_ok'; socketId: string; sub?: string }\n | { type: 'auth_error'; socketId?: string; err: string }\n | { type: 'ping'; socketId: string; payload?: NoInfer<z.infer<Ping>> }\n | { type: 'pong'; socketId: string; payload?: NoInfer<z.infer<Pong>>; issues?: any[], error:boolean };\n\nexport type SocketDebugOptions<Ping extends ZodType, Pong extends ZodType> = {\n verbose?: boolean;\n only?: string[];\n logger?: (e: SocketServerDebugEvent<Ping, Pong>) => void;\n} & {\n [K in SocketServerDebugEvent<Ping, Pong>['type']]?: boolean;\n};\n\nexport type BuiltInRoomHooks = {\n onRoomJoin?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n onRoomLeave?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n};\n\n/** === NEW: enforced heartbeat config with schemas and callback */\nexport type HeartbeatServerOptions<Ping extends ZodType, Pong extends ZodType> = {\n pingEvent?: string; // default 'sys:ping'\n pongEvent?: string; // default 'sys:pong'\n\n /** Validate incoming ping payload: event will be { payload: <ping> }. */\n pingSchema: Ping;\n\n /** Build the pong payload you emit. It will receive the validated ping and socket. */\n makePongPayload: (args: { socket: Socket; ping: z.infer<Ping> }) => NoInfer<z.infer<Pong>>;\n\n /** Optionally validate the outgoing pong object before emit. */\n pongSchema?: Pong;\n};\n\nexport function createSocketConnections<Ping extends ZodType, Pong extends ZodType, T extends EventMap>(\n io: Server,\n events: T,\n opts?: {\n debug?: SocketDebugOptions<Ping, Pong>;\n rooms?: BuiltInRoomHooks;\n heartbeat: HeartbeatServerOptions<Ping, Pong>;\n }\n): SocketConnection<T> {\n const debug = opts?.debug ?? {};\n const dbg = (name: string, e: SocketServerDebugEvent<Ping, Pong>) => {\n if (!debug.logger || !debug[e.type]) return;\n if (debug.only && !debug.only.includes(name) && e.type === 'receive') return;\n debug.logger(e);\n };\n\n const getSchema = <K extends keyof T & string>(k: K) => events[k].message;\n const toArray = (rooms?: string[] | string): string[] =>\n rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n\n // registrations\n const registrations = new Map<\n string,\n Set<{\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }>\n >();\n\n const addRegistration = (eventName: string, reg: {\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }) => {\n let set = registrations.get(eventName);\n if (!set) {\n set = new Set();\n registrations.set(eventName, set);\n }\n set.add(reg);\n };\n\n const removeAllForEvent = (eventName: string) => {\n const set = registrations.get(eventName);\n if (!set) return;\n for (const reg of set) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n }\n registrations.delete(eventName);\n dbg(eventName, { type: 'unregister', event: eventName });\n };\n\n const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? 'room:join';\n const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? 'room:leave';\n\n const hb = opts?.heartbeat;\n if (!hb) {\n throw new Error('createSocketConnections: heartbeat config is required');\n }\n const pingEvent = hb.pingEvent ?? 'sys:ping';\n const pongEvent = hb.pongEvent ?? 'sys:pong';\n\n io.on('connection', (socket) => {\n if ((socket.data as any)?.user) {\n dbg('auth_ok', { type: 'auth_ok', socketId: socket.id, sub: (socket.data as any).user?.sub });\n }\n\n // built-in room handlers\n const joinHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n \n if (!opts?.rooms?.onRoomJoin ||( await opts.rooms.onRoomJoin({ room: r, socket }))==true){\n await socket.join(r);\n dbg('room_join', { type: 'room_join', rooms: [r], socketId: socket.id });\n } \n }\n };\n const leaveHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n \n \n if (!opts?.rooms?.onRoomLeave || ( await opts.rooms.onRoomLeave({ room: r, socket }))==true){\n dbg('room_leave', { type: 'room_leave', rooms: [r], socketId: socket.id });\nawait socket.leave(r);\n }\n }\n };\n socket.on(roomJoinEvent, joinHandler);\n socket.on(roomLeaveEvent, leaveHandler);\n socket.once('disconnect', () => {\n socket.off(roomJoinEvent, joinHandler);\n socket.off(roomLeaveEvent, leaveHandler);\n });\n\n socket.on(pingEvent, (msg: { payload?: unknown } = {}, ack?: (x: { serverNow: string }) => void) => {\n\n const parsed = hb.pingSchema.safeParse(msg?.payload);\n if (!parsed.success) {\n socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });\n return;\n }\n\n dbg('ping', { type: 'ping', socketId: socket.id, payload: parsed.data });\n\n const pong = hb.makePongPayload({ socket, ping: parsed.data })\n\n if (hb.pongSchema) {\n const check = hb.pongSchema.safeParse(pong);\n if (!check.success) {\n socket.emit(`${pongEvent}:error`, { issues: check.error.issues });\n dbg('pong', { type: 'pong', socketId: socket.id, issues: check.error.issues, error:true });\n return;\n }\n }\n\n socket.emit(pongEvent, pong);\n dbg('pong', { type: 'pong', socketId: socket.id, payload: pong , error:false});\n\n if (typeof ack === 'function') ack({ serverNow: new Date().toISOString() });\n });\n });\n\n const conn = {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void {\n const socketListeners = new WeakMap<Socket, (raw: unknown) => void>();\n\nconst connectionListener = (socket: Socket) => {\n const wrapped = async (raw: unknown, maybeAck?: (data?: unknown) => void) => {\n const schema = getSchema(eventName);\n const parsed = schema.safeParse(raw);\n\n const ctx: HandlerCtx = {\n sentAt: new Date(),\n socket,\n socketId: socket.id,\n nsp: socket.nsp.name,\n rooms: Array.from(socket.rooms),\n user: (socket.data )?.user,\n scopes: (socket.data )?.scopes,\n ack: typeof maybeAck === 'function' ? (d?: unknown) => { try { maybeAck(d); } catch { /* noop */ } } : undefined, // NEW\n };\n\n dbg(String(eventName), {\n type: 'receive',\n event: String(eventName),\n socketId: ctx.socketId,\n nsp: ctx.nsp,\n rooms: ctx.rooms,\n raw: debug.verbose ? raw : undefined,\n });\n\n if (!parsed.success) {\n socket.emit(`${String(eventName)}:error`, { eventName, sentAt: ctx.sentAt, issues: parsed.error.issues });\n dbg(String(eventName), { type: 'validation_error', event: String(eventName), issues: parsed.error.issues });\n return;\n }\n\n try {\n await handler(parsed.data as Payload<T, K>, ctx);\n dbg(String(eventName), { type: 'handler_success', event: String(eventName) });\n } catch (error) {\n dbg(String(eventName), { type: 'handler_error', event: String(eventName), error });\n throw error;\n }\n };\n\n socketListeners.set(socket, wrapped);\n socket.on(String(eventName), wrapped);\n socket.once('disconnect', () => {\n const w = socketListeners.get(socket);\n if (w) socket.off(String(eventName), w);\n });\n};\n\n\n io.on('connection', connectionListener);\n addRegistration(String(eventName), { connectionListener, socketListeners });\n dbg(String(eventName), { type: 'register', event: String(eventName) });\n\n return () => {\n const set = registrations.get(String(eventName));\n if (!set) return;\n for (const reg of Array.from(set)) {\n if (reg.connectionListener === connectionListener) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n set.delete(reg);\n break;\n }\n }\n if (set.size === 0) registrations.delete(String(eventName));\n dbg(String(eventName), { type: 'unregister', event: String(eventName) });\n };\n },\n\n off<K extends keyof T & string>(eventName: K): void {\n removeAllForEvent(String(eventName));\n },\n\nemit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n rooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void, \n): void {\n const schema = getSchema(eventName);\n const check = schema.safeParse(payload);\n if (!check.success) throw new Error(`Invalid payload for \"${String(eventName)}\": ${check.error.message}`);\n\n const targets = toArray(rooms);\n const envelope = {\n eventName,\n sentAt: new Date(),\n sentTo: targets,\n data: check.data as Payload<T, K>,\n metadata: metadata ?? {},\n };\n\n // If exactly one target socket, use per-socket emit with ack\n if (targets.length === 1) {\n const sid = targets[0]!;\n const sock = io.sockets.sockets.get(sid as any);\n if (sock) {\n if (onAck) sock.emit(String(eventName), envelope, (ack: unknown) => { try { onAck(ack); } catch { /* noop */ } });\n else sock.emit(String(eventName), envelope);\n dbg(String(eventName), { type: 'emit', event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : undefined });\n return;\n }\n }\n\n // Fallback: broadcast, no ack possible\n if (targets.length === 0) io.emit(String(eventName), envelope);\n else io.to(targets).emit(String(eventName), envelope);\n\n dbg(String(eventName), { type: 'emit', event: String(eventName), rooms: targets, envelope: debug.verbose ? envelope : undefined });\n}\n,\n } as const satisfies SocketConnection<T>;\n\n return conn;\n}\n\nexport type { EventMap, Payload };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/routesV3.server.ts","../src/sockets/socket.server.index.ts"],"sourcesContent":["/**\n * routesV3.server.ts\n * -----------------------------------------------------------------------------\n * Bind an Express router/app to a `finalize(...)` registry of AnyLeafs.\n * - Fully typed handlers (params/query/body/output)\n * - Zod parsing + optional output validation\n * - buildCtx runs as a middleware *first*, before all other middlewares\n * - Global, per-route, and cfg-derived middlewares (auth, uploads)\n * - Helper to warn about unimplemented routes\n * - DX helpers to use `ctx` in any middleware with proper types\n */\n\nimport { AnyLeaf, FileField, HttpMethod, InferBody, InferOutput, InferParams, InferQuery, MethodCfg } from '@emeryld/rrroutes-contract';\nimport type * as express from 'express';\nimport type { RequestHandler, Router } from 'express';\nimport type { ZodType } from 'zod';\n\n\n\n/** Shape expected from optional logger implementations. */\nexport type LoggerLike = {\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n debug?: (...args: any[]) => void;\n verbose?: (...args: any[]) => void;\n system?: (...args: any[]) => void;\n log?: (...args: any[]) => void;\n};\n\ntype RoutesLoggerCarrier = {\n routesLogger?: LoggerLike;\n};\n\ntype CtxWithRoutesLogger<Ctx> = Ctx & RoutesLoggerCarrier;\n\n// Debug logging --------------------------------------------------------------\nexport type RouteServerDebugMode = 'minimal' | 'complete';\n\ntype RouteServerDebugEventBase = {\n /** Optional logical name assigned via `RouteDef.debug?.debugName` (or `RouteDef.debugName`). */\n name?: string;\n};\n\nexport type RouteServerDebugEvent =\n | (RouteServerDebugEventBase & {\n type: 'request';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'register';\n method: Uppercase<HttpMethod>;\n path: string;\n })\n | (RouteServerDebugEventBase & {\n type: 'buildCtx';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n url: string;\n durationMs?: number;\n error?: unknown;\n })\n | (RouteServerDebugEventBase & {\n type: 'handler';\n stage: 'start' | 'success' | 'error';\n method: Uppercase<HttpMethod>;\n path: string;\n durationMs?: number;\n params?: unknown;\n query?: unknown;\n body?: unknown;\n output?: unknown;\n error?: unknown;\n });\n\nexport type RouteServerDebugLogger = (event: RouteServerDebugEvent) => void;\n\n/**\n * Configure server-side debug logging.\n * - Use booleans or `'minimal'/'complete'` for quick toggles.\n * - Pass a custom logger function to redirect structured events.\n * - Provide a map to enable specific event types, opt into verbose payload logging, or restrict logs via `only`.\n */\nexport type RouteServerDebugOptions<Names extends string = string> = RouteServerDebugToggleOptions<Names>;\n\n/**\n * Fine-grained toggle map for server debug logging.\n * Enable individual event types, opt into verbose payload logging, override the logger, or restrict to named routes.\n * Use `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`) to set the name that `only` will match against.\n */\nexport type RouteServerDebugToggleOptions<Names extends string = string> = Partial<\n Record<RouteServerDebugEvent['type'], boolean>\n> & {\n verbose?: boolean;\n logger?: RouteServerDebugLogger;\n only?: Names[];\n};\n\n/**\n * Per-route debug overrides. Same toggles as `RouteServerDebugOptions`, but limited to a single route\n * and therefore replaces the `only` filter with a local `debugName`.\n */\nexport type RouteDefDebugOptions<Names extends string = string> = Omit<\n RouteServerDebugToggleOptions<Names>,\n 'only'\n> & {\n debugName?: Names;\n};\n\nconst serverDebugEventTypes: RouteServerDebugEvent['type'][] = [\n 'register',\n 'request',\n 'buildCtx',\n 'handler',\n];\n\ntype ServerDebugEmitter<Names extends string> = {\n emit: (event: RouteServerDebugEvent, name?: Names) => void;\n mode: RouteServerDebugMode;\n};\n\nconst noopServerEmit = () => {};\n\nfunction createServerDebugEmitter<Names extends string>(\n option?: RouteServerDebugOptions<Names>,\n): ServerDebugEmitter<Names> {\n const disabled: ServerDebugEmitter<Names> = { emit: noopServerEmit, mode: 'minimal' };\n if (!option) return disabled;\n\n if (typeof option === 'object') {\n const toggles = option as RouteServerDebugToggleOptions<Names>;\n const verbose = Boolean(toggles.verbose);\n const enabledTypes = serverDebugEventTypes.filter((type) => toggles[type]);\n if (enabledTypes.length === 0) {\n return { emit: noopServerEmit, mode: verbose ? 'complete' : 'minimal' };\n }\n const whitelist = new Set<RouteServerDebugEvent['type']>(enabledTypes);\n const onlySet =\n toggles.only && toggles.only.length > 0 ? new Set<Names>(toggles.only) : undefined;\n const logger = toggles.logger;\n const emit: ServerDebugEmitter<Names>['emit'] = (event, name) => {\n if (!whitelist.has(event.type) || !logger) return;\n if (onlySet && (!name || !onlySet.has(name))) return;\n logger(name ? { ...event, name } : event);\n };\n return { emit, mode: verbose ? 'complete' : 'minimal' };\n }\n\n return disabled;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Keys + leaf helpers (derive keys from byKey to avoid template-literal pitfalls)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Keys like \"GET /v1/foo\" that *actually* exist in the registry */\nexport type KeysOfRegistry<R extends { byKey: Record<string, AnyLeaf> }> = keyof R['byKey'] &\n string;\n\ntype MethodFromKey<K extends string> = K extends `${infer M} ${string}` ? Lowercase<M> : never;\ntype PathFromKey<K extends string> = K extends `${string} ${infer P}` ? P : never;\n\n/** Given a registry and a key, pick the exact leaf for that method+path */\nexport type LeafFromKey<R extends { all: readonly AnyLeaf[] }, K extends string> = Extract<\n R['all'][number],\n { method: MethodFromKey<K> & HttpMethod; path: PathFromKey<K> }\n>;\n\n/** Optional-ify types if your core returns `never` when a schema isn't defined */\ntype Maybe<T> = [T] extends [never] ? undefined : T;\n\n/** Typed params argument exposed to handlers. */\nexport type ArgParams<L extends AnyLeaf> = Maybe<InferParams<L>>;\n/** Typed query argument exposed to handlers. */\nexport type ArgQuery<L extends AnyLeaf> = Maybe<InferQuery<L>>;\n/** Typed body argument exposed to handlers. */\nexport type ArgBody<L extends AnyLeaf> = Maybe<InferBody<L>>;\n\n/**\n * Convenience to compute a `\"METHOD /path\"` key from a leaf.\n * @param leaf Leaf describing the route.\n * @returns Uppercase method + path key.\n */\nexport const keyOf = (leaf: AnyLeaf) => `${leaf.method.toUpperCase()} ${leaf.path}` as const;\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Context typing & DX helpers (so ctx is usable in *any* middleware)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Unique symbol used to stash ctx on res.locals.\n * (Symbols are safer than string keys against collisions.)\n */\nexport const CTX_SYMBOL: unique symbol = Symbol.for('typedLeaves.ctx');\n\nconst AFTER_HANDLER_NEXT_SYMBOL: unique symbol = Symbol.for('typedLeaves.afterHandlerNext');\n\nfunction setAfterHandlerNext(res: express.Response, value: boolean) {\n (res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL] = value;\n}\n\nfunction handlerInvokedNext(res: express.Response): boolean {\n return Boolean((res.locals as any)[AFTER_HANDLER_NEXT_SYMBOL]);\n}\n\n/** Response type that *has* a ctx on locals for DX in middlewares */\nexport type ResponseWithCtx<Ctx> =\n // Replace locals with an intersection that guarantees CTX_SYMBOL exists\n Omit<express.Response, 'locals'> & {\n locals: express.Response['locals'] & { [CTX_SYMBOL]: CtxWithRoutesLogger<Ctx> };\n };\n\n/** A middleware signature that can *use* ctx via `res.locals[CTX_SYMBOL]` */\nexport type CtxRequestHandler<Ctx> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n}) => any;\n\n/**\n * Safely read ctx from any Response.\n * @param res Express response whose locals contain the ctx symbol.\n * @returns Strongly typed context object.\n */\nexport function getCtx<Ctx = unknown>(res: express.Response): CtxWithRoutesLogger<Ctx> {\n return (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n}\n\n/**\n * Wrap a ctx-typed middleware to a plain RequestHandler (for arrays, etc.).\n * @param mw Middleware that expects a typed response with ctx available.\n * @returns Standard Express request handler.\n */\nfunction adaptCtxMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n return (req, res, next) => {\n try {\n const result = mw({ req, res, next, ctx: getCtx<Ctx>(res) });\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n return (result as Promise<unknown>).catch((err) => next(err));\n }\n return result as any;\n } catch (err) {\n next(err as any);\n return undefined;\n }\n };\n}\n\nfunction adaptAfterMw<Ctx>(mw: CtxRequestHandler<Ctx>): RequestHandler {\n const adapted = adaptCtxMw<Ctx>(mw);\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n if (!handlerInvokedNext(res)) {\n next();\n return;\n }\n adapted(req, res, next);\n };\n}\n\nfunction mapAfterMiddleware<Ctx>(mws?: Array<CtxRequestHandler<Ctx>>): RequestHandler[] {\n return (mws ?? []).map((mw) => adaptAfterMw<Ctx>(mw));\n}\n\nfunction logHandlerDebugWithRoutesLogger(\n logger: LoggerLike | undefined,\n event: RouteServerDebugEvent,\n) {\n if (!logger || event.type !== 'handler') return;\n const payload: [string, RouteServerDebugEvent] = [\n `[rrroutes-server][handler:${event.stage}] ${event.method} ${event.path}`,\n event,\n ];\n if (event.stage === 'error') {\n (\n logger.error ??\n logger.warn ??\n logger.debug ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n return;\n }\n (\n logger.debug ??\n logger.verbose ??\n logger.info ??\n logger.log ??\n logger.system\n )?.call(logger, ...payload);\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Controller types — object form only (simpler, clearer typings)\n// ──────────────────────────────────────────────────────────────────────────────\n\n/** Typed route handler for a specific leaf */\nexport type Handler<L extends AnyLeaf, Ctx = unknown> = (args: {\n req: express.Request;\n res: express.Response;\n next: express.NextFunction;\n ctx: CtxWithRoutesLogger<Ctx>;\n params: ArgParams<L>;\n query: ArgQuery<L>;\n body: ArgBody<L>;\n}) => Promise<InferOutput<L>> | InferOutput<L>;\n\n/** Route definition for one key */\nexport type RouteDef<L extends AnyLeaf, Ctx = unknown, Names extends string = string> = {\n /** Middlewares before the handler (run after buildCtx/global/derived) */\n use?: Array<CtxRequestHandler<Ctx>>;\n /** Middlewares after the handler *if* it calls next() */\n after?: Array<CtxRequestHandler<Ctx>>;\n /** Your business logic */\n handler: Handler<L, Ctx>;\n /**\n * Optional per-route debug overrides. When provided, these replace the global debug options.\n */\n debug?: RouteDefDebugOptions<Names>;\n /**\n * Optional logical name used for debug filtering. Prefer `debug.debugName`; this field remains for backwards compatibility.\n */\n debugName?: Names;\n};\n\n/** Map of registry keys -> route defs */\nexport type ControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = {\n [P in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, P>, Ctx, Names>;\n};\n\nexport type PartialControllerMap<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n> = Partial<ControllerMap<R, Ctx, Names>>;\n\n// ──────────────────────────────────────────────────────────────────────────────\n/** Options + derivation helpers */\n// ──────────────────────────────────────────────────────────────────────────────\n\nexport type RouteServerConfig<Ctx = unknown, Names extends string = string> = {\n /**\n * Build a request-scoped context. We wrap this in a middleware that runs\n * *first* (before global/derived/route middlewares), and stash it on\n * `res.locals[CTX_SYMBOL]` so *all* later middlewares can use it.\n * You can optionally include `routesLogger` to override handler debug logging per request.\n */\n buildCtx: (\n req: express.Request,\n res: express.Response,\n ) => CtxWithRoutesLogger<Ctx> | Promise<CtxWithRoutesLogger<Ctx>>;\n\n /**\n * Grouped global middlewares that run *before* or *after* the handler for every route.\n */\n globalMiddleware?: {\n before?: Array<CtxRequestHandler<Ctx>>;\n after?: Array<CtxRequestHandler<Ctx>>;\n };\n\n /**\n * Derive middleware from MethodCfg.\n * - `upload` runs when cfg.bodyFiles has entries\n */\n fromCfg?: {\n upload?: (files: FileField[] | undefined, leaf: AnyLeaf) => RequestHandler[];\n };\n\n /** Validate handler return values with outputSchema (default: true) */\n validateOutput?: boolean;\n\n /** Custom responder (default: res.json(data)) */\n send?: (res: express.Response, data: unknown) => void;\n\n /** Optional logger hooks */\n logger?: LoggerLike;\n\n /**\n * Optional debug logging for the request lifecycle.\n * Supports booleans/modes/loggers, or a toggle map with per-event enabling, verbose payload logging,\n * and `only` filters tied to `RouteDef.debug?.debugName` (or the deprecated `RouteDef.debugName`).\n */\n debug?: RouteServerDebugOptions<Names>;\n};\n\n/** Default JSON responder (typed to avoid implicit-any diagnostics) */\nconst defaultSend: (res: express.Response, data: unknown) => void = (res, data) => {\n res.json(data as any);\n};\n\n// ──────────────────────────────────────────────────────────────────────────────\n// Core builder\n// ──────────────────────────────────────────────────────────────────────────────\n\nconst REGISTERED_ROUTES_SYMBOL = Symbol.for('routesV3.registeredRoutes');\n\ntype RegisteredRouteStore = Set<string>;\n\n/**\n * Retrieve or initialize the shared store of registered route keys.\n * @param router Express router/application that carries previously registered keys.\n * @returns Set of string keys describing registered routes.\n */\nfunction getRegisteredRouteStore(router: Router): RegisteredRouteStore {\n const existing = (router as any)[REGISTERED_ROUTES_SYMBOL] as RegisteredRouteStore | undefined;\n if (existing) return existing;\n const store: RegisteredRouteStore = new Set();\n (router as any)[REGISTERED_ROUTES_SYMBOL] = store;\n return store;\n}\n\n/**\n * Inspect the Express layer stack to discover already-registered routes.\n * @param appOrRouter Express application or router to inspect.\n * @returns All keys in the form `\"METHOD /path\"` found on the stack.\n */\nfunction collectRoutesFromStack(appOrRouter: Router): string[] {\n const result: string[] = [];\n const stack: any[] =\n (appOrRouter as any).stack ??\n ((appOrRouter as any)._router ? (appOrRouter as any)._router.stack : undefined) ??\n [];\n\n if (!Array.isArray(stack)) return result;\n\n for (const layer of stack) {\n const route = layer && layer.route;\n if (!route) continue;\n\n const paths = Array.isArray(route.path) ? route.path : [route.path];\n const methodEntries = Object.entries(route.methods ?? {}).filter(([, enabled]) => enabled);\n\n for (const path of paths) {\n for (const [method] of methodEntries) {\n result.push(`${method.toUpperCase()} ${path}`);\n }\n }\n }\n\n return result;\n}\n\n/** Runtime helpers returned by `createRRRoute`. */\nexport type RouteServer<Ctx = unknown, Names extends string = string> = {\n router: Router;\n register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>): void;\n registerControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n ): void;\n warnMissingControllers<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n logger: { warn: (...args: any[]) => void },\n ): void;\n getRegisteredKeys(): string[];\n};\n\n/**\n * Create an Express binding helper that keeps routes and controllers in sync.\n * @param router Express router or app to register handlers on.\n * @param config Optional configuration controlling ctx building, auth, uploads, etc.\n * @returns Object with helpers to register controllers and inspect registered keys.\n */\nexport function createRRRoute<Ctx = unknown, Names extends string = string>(\n router: Router,\n config: RouteServerConfig<Ctx, Names>,\n): RouteServer<Ctx, Names> {\n const validateOutput = config.validateOutput ?? true;\n const send = config.send ?? defaultSend;\n const logger = config.logger;\n const { emit: defaultEmitDebug, mode: defaultDebugMode } =\n createServerDebugEmitter<Names>(config.debug);\n const decorateDebugEvent = <T extends RouteServerDebugEvent>(\n isVerbose: boolean,\n event: T,\n details?: Partial<RouteServerDebugEvent>,\n ): RouteServerDebugEvent => {\n if (!isVerbose || !details) return event;\n return { ...event, ...details } as RouteServerDebugEvent;\n };\n\n const globalBeforeMws = [ ...(config.globalMiddleware?.before ?? [])].map(\n (mw) => adaptCtxMw<Ctx>(mw),\n );\n const globalAfterMws = mapAfterMiddleware<Ctx>(config.globalMiddleware?.after);\n const registered = getRegisteredRouteStore(router);\n\n const buildDerived = (leaf: AnyLeaf): RequestHandler[] => {\n const derived: RequestHandler[] = [];\n if (\n config.fromCfg?.upload &&\n Array.isArray(leaf.cfg.bodyFiles) &&\n leaf.cfg.bodyFiles.length > 0\n ) {\n derived.push(...config.fromCfg.upload(leaf.cfg.bodyFiles, leaf));\n }\n\n return derived;\n };\n\n /** Register a single leaf/controller pair on the underlying router. */\n function register<L extends AnyLeaf>(leaf: L, def: RouteDef<L, Ctx, Names>) {\n const method = leaf.method as HttpMethod;\n const methodUpper = method.toUpperCase() as Uppercase<HttpMethod>;\n const path = leaf.path as string;\n const key = keyOf(leaf);\n const defDebug = def.debug;\n let debugName = def.debugName as Names | undefined;\n let routeDebugEmitter: ServerDebugEmitter<Names> | undefined;\n if (defDebug) {\n const { debugName: overrideName, ...rest } = defDebug;\n const hasOverrides = Object.values(rest).some((value) => value !== undefined);\n if (hasOverrides) {\n routeDebugEmitter = createServerDebugEmitter<Names>(\n rest as RouteServerDebugOptions<Names>,\n );\n }\n debugName = (overrideName ?? debugName) as Names | undefined;\n }\n const activeEmit = routeDebugEmitter?.emit ?? defaultEmitDebug;\n const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;\n const emit = (event: RouteServerDebugEvent) => activeEmit(event, debugName);\n const isVerboseDebug = activeDebugMode === 'complete';\n emit({ type: 'register', method: methodUpper, path });\n\n const routeSpecific = (def?.use ?? []).map((mw) => adaptCtxMw<Ctx>(mw));\n const derived = buildDerived(leaf);\n const ctxMw: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl ?? path;\n const startedAt = Date.now();\n emit({ type: 'buildCtx', stage: 'start', method: methodUpper, path, url: requestUrl });\n try {\n const ctx = await config.buildCtx(req, res);\n (res.locals as any)[CTX_SYMBOL] = ctx;\n emit({\n type: 'buildCtx',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n });\n next();\n } catch (err) {\n emit({\n type: 'buildCtx',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n logger?.error?.('buildCtx error', err);\n next(err as any);\n }\n };\n const before: RequestHandler[] = [ctxMw, ...globalBeforeMws, ...derived, ...routeSpecific];\n\n const wrapped: RequestHandler = async (req, res, next) => {\n const requestUrl = req.originalUrl.split('?')[0] ?? path;\n const startedAt = Date.now();\n emit({ type: 'request', stage: 'start', method: methodUpper, path, url: requestUrl });\n let params: ArgParams<L> | undefined;\n let query: ArgQuery<L> | undefined;\n let body: ArgBody<L> | undefined;\n let responsePayload: InferOutput<L> | undefined;\n let hasResponsePayload = false;\n setAfterHandlerNext(res, false);\n let handlerCalledNext = false;\n const downstreamNext: express.NextFunction = (err?: any) => {\n handlerCalledNext = true;\n setAfterHandlerNext(res, true);\n next(err);\n };\n\n logger?.info?.(`${methodUpper}@${path} (${requestUrl})`);\n const ctx = (res.locals as any)[CTX_SYMBOL] as CtxWithRoutesLogger<Ctx>;\n const ctxRoutesLogger = ctx?.routesLogger;\n const emitWithCtx = (\n event: RouteServerDebugEvent,\n details?: Partial<RouteServerDebugEvent>,\n ) => {\n const decorated = decorateDebugEvent(isVerboseDebug, event, details);\n if (decorated.type === 'handler' && ctxRoutesLogger) {\n logHandlerDebugWithRoutesLogger(ctxRoutesLogger, decorated);\n }else{\n emit(decorated);\n }\n };\n try {\n params = (\n leaf.cfg.paramsSchema\n ? (leaf.cfg.paramsSchema as ZodType).parse(req.params)\n : Object.keys(req.params || {}).length\n ? (req.params as any)\n : undefined\n ) as ArgParams<L>;\n\n try {\n query = leaf.cfg.querySchema\n ? (leaf.cfg.querySchema as ZodType).parse(req.query)\n : Object.keys(req.query || {}).length\n ? (req.query as any)\n : undefined;\n } catch (e) {\n logger?.error?.('Query parsing error', {\n path,\n method: methodUpper,\n error: e,\n raw: JSON.stringify(req.query),\n });\n throw e;\n }\n\n body = (\n leaf.cfg.bodySchema\n ? (leaf.cfg.bodySchema as ZodType).parse(req.body)\n : req.body !== undefined\n ? (req.body as any)\n : undefined\n ) as ArgBody<L>;\n\n logger?.verbose?.(`${methodUpper}@${path} (${requestUrl})`, {\n params,\n query,\n body,\n });\n\n const handlerStartedAt = Date.now();\n emitWithCtx(\n {\n type: 'handler',\n stage: 'start',\n method: methodUpper,\n path,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n\n let result;\n try {\n result = await def.handler({\n req,\n res,\n next: downstreamNext,\n ctx,\n params: params as ArgParams<L>,\n query: query as ArgQuery<L>,\n body: body as ArgBody<L>,\n });\n emitWithCtx(\n {\n type: 'handler',\n stage: 'success',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(result !== undefined ? { output: result } : {}),\n }\n : undefined,\n );\n } catch (e) {\n emitWithCtx(\n {\n type: 'handler',\n stage: 'error',\n method: methodUpper,\n path,\n durationMs: Date.now() - handlerStartedAt,\n error: e,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Handler error', e);\n throw e;\n }\n\n if (!res.headersSent) {\n if (result !== undefined) {\n const out =\n validateOutput && leaf.cfg.outputSchema\n ? (leaf.cfg.outputSchema as ZodType).parse(result)\n : result;\n responsePayload = out as InferOutput<L>;\n hasResponsePayload = true;\n logger?.verbose?.(`${methodUpper}@${path} result`, out);\n send(res, out);\n } else if (!handlerCalledNext) {\n next();\n }\n }\n\n emitWithCtx(\n {\n type: 'request',\n stage: 'success',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n },\n isVerboseDebug\n ? {\n params,\n query,\n body,\n ...(hasResponsePayload ? { output: responsePayload } : {}),\n }\n : undefined,\n );\n } catch (err) {\n emitWithCtx(\n {\n type: 'request',\n stage: 'error',\n method: methodUpper,\n path,\n url: requestUrl,\n durationMs: Date.now() - startedAt,\n error: err,\n },\n isVerboseDebug ? { params, query, body } : undefined,\n );\n logger?.error?.('Route error', err);\n next(err as any);\n }\n };\n\n const after = [...mapAfterMiddleware<Ctx>(def?.after), ...globalAfterMws];\n (router as any)[method](path, ...before, wrapped, ...after);\n registered.add(key);\n }\n\n /**\n * Register controller definitions for the provided keys.\n * @param registry Finalized registry of leaves.\n * @param controllers Partial controller map keyed by `\"METHOD /path\"`.\n */\n function registerControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n >(registry: R, controllers: PartialControllerMap<R, Ctx, Names>) {\n (Object.keys(controllers) as Array<KeysOfRegistry<R>>).forEach((key) => {\n const leaf = registry.byKey[key] as unknown as LeafFromKey<R, typeof key> | undefined;\n if (!leaf) {\n logger?.warn?.(`No leaf found for controller key: ${key}. Not registering route.`);\n return;\n }\n const def = controllers[key];\n if (!def) return;\n register(leaf as LeafFromKey<R, typeof key>, def);\n });\n }\n\n /**\n * Warn about leaves that do not have a registered controller.\n * @param registry Finalized registry of leaves.\n * @param warnLogger Logger used for warning output.\n */\n function warnMissing<R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> }>(\n registry: R,\n warnLogger: { warn: (...args: any[]) => void },\n ) {\n const registeredFromStore = new Set<string>(Array.from(registered));\n if (registeredFromStore.size === 0) {\n collectRoutesFromStack(router).forEach((key) => registeredFromStore.add(key));\n }\n for (const leaf of registry.all) {\n const key = keyOf(leaf);\n if (!registeredFromStore.has(key)) {\n warnLogger.warn(`No controller registered for route: ${key}`);\n }\n }\n }\n\n return {\n router,\n register,\n registerControllers,\n warnMissingControllers: warnMissing,\n getRegisteredKeys: () => Array.from(registered),\n };\n}\n\n/**\n * Bind only the controllers that are present in the provided map.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Partial map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindExpressRoutes<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: PartialControllerMap<R, Ctx, Names>,\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n/**\n * Bind controllers for every leaf. Missing entries fail at compile time.\n * @param router Express router or app.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param controllers Complete map of controllers keyed by `\"METHOD /path\"`.\n * @param config Optional route server configuration.\n * @returns The same router instance for chaining.\n */\nexport function bindAll<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n>(\n router: Router,\n registry: R,\n controllers: { [K in KeysOfRegistry<R>]: RouteDef<LeafFromKey<R, K>, Ctx, Names> },\n config: RouteServerConfig<Ctx, Names>,\n) {\n const server = createRRRoute<Ctx, Names>(router, config);\n server.registerControllers(registry, controllers);\n return router;\n}\n\n// ──────────────────────────────────────────────────────────────────────────────\n// DX helpers\n// ──────────────────────────────────────────────────────────────────────────────\n\n/**\n * Helper for great IntelliSense when authoring controller maps.\n * @returns Function that enforces key names while preserving partial flexibility.\n */\nexport const defineControllers =\n <\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n Ctx = unknown,\n Names extends string = string,\n >() =>\n <M extends PartialControllerMap<R, Ctx, Names>>(m: M) =>\n m;\n\n/**\n * Warn about leaves that don't have controllers.\n * Call this during startup to surface missing routes.\n * @param router Express router or app to inspect.\n * @param registry Finalized registry produced by `finalize(...)`.\n * @param logger Logger where warnings are emitted.\n */\nexport function warnMissingControllers<\n R extends { all: readonly AnyLeaf[]; byKey: Record<string, AnyLeaf> },\n>(router: Router, registry: R, logger: { warn: (...args: any[]) => void }) {\n const registeredStore = (router as any)[REGISTERED_ROUTES_SYMBOL] as Set<string> | undefined;\n const initial = registeredStore ? Array.from(registeredStore) : collectRoutesFromStack(router);\n const registeredKeys = new Set<string>(initial);\n\n for (const leaf of registry.all) {\n const k = keyOf(leaf);\n if (!registeredKeys.has(k)) {\n logger.warn(`No controller registered for route: ${k}`);\n }\n }\n}\n","// socket.server.index.ts\nimport { Server, Socket } from 'socket.io';\nimport { z, ZodType } from 'zod';\nimport type { SocketEvent } from '@emeryld/rrroutes-contract';\n\ntype EventMap = Record<string, SocketEvent>;\ntype Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;\n\n// helper for generics that shouldn't be widened\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\nexport type HandlerCtx = {\n sentAt: Date;\n socket: Socket;\n socketId: string;\n nsp: string;\n rooms: string[];\n user?: unknown;\n scopes?: string[];\n ack?: (data?: unknown) => void;\n};\n\nexport interface SocketConnection<T extends EventMap> {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void;\n\n off<K extends keyof T & string>(eventName: K): void;\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n toRooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void;\n}\n\n/**\n * Grouped debug types:\n * - register: register, unregister\n * - handler: receive, validation_error, handler_success, handler_error\n * - emit: emit\n * - rooms: room_join, room_leave\n * - auth: auth_ok, auth_error\n * - heartbeat: ping, pong\n */\nexport type SocketServerDebugEvent<Ping extends ZodType = ZodType, Pong extends ZodType = ZodType> =\n | {\n type: 'register';\n action: 'register' | 'unregister';\n event: string;\n }\n | {\n type: 'handler';\n phase: 'receive' | 'validation_error' | 'handler_success' | 'handler_error';\n event: string;\n socketId?: string;\n nsp?: string;\n rooms?: string[];\n raw?: unknown;\n issues?: any[];\n error?: unknown;\n }\n | {\n type: 'emit';\n event: string;\n rooms: string[];\n envelope?: unknown;\n }\n | {\n type: 'rooms';\n action: 'join' | 'leave';\n rooms: string[];\n socketId: string;\n }\n | {\n type: 'auth';\n phase: 'ok' | 'error';\n socketId?: string;\n sub?: string;\n err?: string;\n }\n | {\n type: 'heartbeat';\n phase: 'ping' | 'pong';\n socketId: string;\n payload?: NoInfer<z.infer<Ping>> | NoInfer<z.infer<Pong>>;\n issues?: any[];\n error?: boolean;\n };\n\nexport type SocketDebugOptions<Ping extends ZodType, Pong extends ZodType> = {\n verbose?: boolean;\n only?: string[]; // filters event-based logs (register/handler/emit) by event name\n logger?: (e: SocketServerDebugEvent<Ping, Pong>) => void;\n} & {\n [P in SocketServerDebugEvent['type']]?: boolean;\n};\n\nexport type BuiltInRoomHooks = {\n onRoomJoin?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n onRoomLeave?: (args: { room: string; socket: Socket }) => boolean | Promise<boolean>;\n roomJoinEvent?: string;\n roomLeaveEvent?: string;\n};\n\n/** === NEW: enforced heartbeat config with schemas and callback */\nexport type HeartbeatServerOptions<Ping extends ZodType, Pong extends ZodType> = {\n pingEvent?: string; // default 'sys:ping'\n pongEvent?: string; // default 'sys:pong'\n\n /** Validate incoming ping payload: event will be { payload: <ping> }. */\n pingSchema: Ping;\n\n /** Build the pong payload you emit. It will receive the validated ping and socket. */\n makePongPayload: (args: { socket: Socket; ping: z.infer<Ping> }) => NoInfer<z.infer<Pong>>;\n\n /** Optionally validate the outgoing pong object before emit. */\n pongSchema?: Pong;\n};\n\nexport function createSocketConnections<\n Ping extends ZodType,\n Pong extends ZodType,\n T extends EventMap\n>(\n io: Server,\n events: T,\n opts?: {\n debug?: SocketDebugOptions<Ping, Pong>;\n rooms?: BuiltInRoomHooks;\n heartbeat: HeartbeatServerOptions<Ping, Pong>;\n }\n): SocketConnection<T> {\n const debug = opts?.debug ?? {};\n const dbg = (maybeEvent: string | null, e: SocketServerDebugEvent<Ping, Pong>) => {\n if (!debug.logger) return;\n if (!debug[e.type]) return;\n\n // only filter applies to event-shaped logs\n if (\n debug.only &&\n maybeEvent &&\n (e.type === 'register' || e.type === 'handler' || e.type === 'emit') &&\n !debug.only.includes(maybeEvent)\n ) {\n return;\n }\n\n debug.logger(e);\n };\n\n const getSchema = <K extends keyof T & string>(k: K) => events[k].message;\n const toArray = (rooms?: string[] | string): string[] =>\n rooms == null ? [] : Array.isArray(rooms) ? rooms : [rooms];\n\n // registrations\n const registrations = new Map<\n string,\n Set<{\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }>\n >();\n\n const addRegistration = (\n eventName: string,\n reg: {\n connectionListener: (socket: Socket) => void;\n socketListeners: WeakMap<Socket, (raw: unknown) => void>;\n }\n ) => {\n let set = registrations.get(eventName);\n if (!set) {\n set = new Set();\n registrations.set(eventName, set);\n }\n set.add(reg);\n };\n\n const removeAllForEvent = (eventName: string) => {\n const set = registrations.get(eventName);\n if (!set) return;\n for (const reg of set) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n }\n registrations.delete(eventName);\n dbg(eventName, { type: 'register', action: 'unregister', event: eventName });\n };\n\n const roomJoinEvent = opts?.rooms?.roomJoinEvent ?? 'room:join';\n const roomLeaveEvent = opts?.rooms?.roomLeaveEvent ?? 'room:leave';\n\n const hb = opts?.heartbeat;\n if (!hb) throw new Error('createSocketConnections: heartbeat config is required');\n const pingEvent = hb.pingEvent ?? 'sys:ping';\n const pongEvent = hb.pongEvent ?? 'sys:pong';\n\n io.on('connection', (socket) => {\n // auth summary\n if ((socket.data as any)?.user) {\n dbg(null, {\n type: 'auth',\n phase: 'ok',\n socketId: socket.id,\n sub: (socket.data as any).user?.sub,\n });\n }\n\n // built-in room handlers\n const joinHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n const allowed =\n !opts?.rooms?.onRoomJoin || (await opts.rooms.onRoomJoin({ room: r, socket })) === true;\n if (allowed) {\n await socket.join(r);\n dbg(null, { type: 'rooms', action: 'join', rooms: [r], socketId: socket.id });\n }\n }\n };\n const leaveHandler = async ({ rooms }: { rooms?: string[] }) => {\n const list = toArray(rooms);\n for (const r of list) {\n const allowed =\n !opts?.rooms?.onRoomLeave || (await opts.rooms.onRoomLeave({ room: r, socket })) === true;\n if (allowed) {\n dbg(null, { type: 'rooms', action: 'leave', rooms: [r], socketId: socket.id });\n await socket.leave(r);\n }\n }\n };\n socket.on(roomJoinEvent, joinHandler);\n socket.on(roomLeaveEvent, leaveHandler);\n socket.once('disconnect', () => {\n socket.off(roomJoinEvent, joinHandler);\n socket.off(roomLeaveEvent, leaveHandler);\n });\n\n // heartbeat → single bucket\n socket.on(pingEvent, (msg: { payload?: unknown } = {}, ack?: (x: { serverNow: string }) => void) => {\n const parsed = hb.pingSchema.safeParse(msg?.payload);\n if (!parsed.success) {\n socket.emit(`${pingEvent}:error`, { issues: parsed.error.issues });\n dbg(null, {\n type: 'heartbeat',\n phase: 'ping',\n socketId: socket.id,\n payload: msg?.payload as any,\n issues: parsed.error.issues,\n error: true,\n });\n return;\n }\n\n dbg(null, {\n type: 'heartbeat',\n phase: 'ping',\n socketId: socket.id,\n payload: parsed.data as any,\n });\n\n const pong = hb.makePongPayload({ socket, ping: parsed.data });\n\n if (hb.pongSchema) {\n const check = hb.pongSchema.safeParse(pong);\n if (!check.success) {\n socket.emit(`${pongEvent}:error`, { issues: check.error.issues });\n dbg(null, {\n type: 'heartbeat',\n phase: 'pong',\n socketId: socket.id,\n issues: check.error.issues,\n error: true,\n });\n return;\n }\n }\n\n socket.emit(pongEvent, pong);\n dbg(null, {\n type: 'heartbeat',\n phase: 'pong',\n socketId: socket.id,\n payload: pong as any,\n error: false,\n });\n\n if (typeof ack === 'function') {\n ack({ serverNow: new Date().toISOString() });\n }\n });\n });\n\n const conn = {\n on<K extends keyof T & string>(\n eventName: K,\n handler: (payload: Payload<T, K>, ctx: HandlerCtx) => void | Promise<void>\n ): () => void {\n const socketListeners = new WeakMap<Socket, (raw: unknown) => void>();\n\n const connectionListener = (socket: Socket) => {\n const wrapped = async (raw: unknown, maybeAck?: (data?: unknown) => void) => {\n const schema = getSchema(eventName);\n const parsed = schema.safeParse(raw);\n\n const ctx: HandlerCtx = {\n sentAt: new Date(),\n socket,\n socketId: socket.id,\n nsp: socket.nsp.name,\n rooms: Array.from(socket.rooms),\n user: socket.data?.user,\n scopes: socket.data?.scopes,\n ack:\n typeof maybeAck === 'function'\n ? (d?: unknown) => {\n try {\n maybeAck(d);\n } catch {\n /* noop */\n }\n }\n : undefined,\n };\n\n dbg(String(eventName), {\n type: 'handler',\n phase: 'receive',\n event: String(eventName),\n socketId: ctx.socketId,\n nsp: ctx.nsp,\n rooms: ctx.rooms,\n raw: debug.verbose ? raw : undefined,\n });\n\n if (!parsed.success) {\n socket.emit(`${String(eventName)}:error`, {\n eventName,\n sentAt: ctx.sentAt,\n issues: parsed.error.issues,\n });\n dbg(String(eventName), {\n type: 'handler',\n phase: 'validation_error',\n event: String(eventName),\n issues: parsed.error.issues,\n });\n return;\n }\n\n try {\n await handler(parsed.data as Payload<T, K>, ctx);\n dbg(String(eventName), {\n type: 'handler',\n phase: 'handler_success',\n event: String(eventName),\n });\n } catch (error) {\n dbg(String(eventName), {\n type: 'handler',\n phase: 'handler_error',\n event: String(eventName),\n error,\n });\n throw error;\n }\n };\n\n socketListeners.set(socket, wrapped);\n socket.on(String(eventName), wrapped);\n socket.once('disconnect', () => {\n const w = socketListeners.get(socket);\n if (w) socket.off(String(eventName), w);\n });\n };\n\n io.on('connection', connectionListener);\n addRegistration(String(eventName), { connectionListener, socketListeners });\n dbg(String(eventName), {\n type: 'register',\n action: 'register',\n event: String(eventName),\n });\n\n return () => {\n const set = registrations.get(String(eventName));\n if (!set) return;\n for (const reg of Array.from(set)) {\n if (reg.connectionListener === connectionListener) {\n io.off('connection', reg.connectionListener);\n for (const socket of io.sockets.sockets.values()) {\n const wrapped = reg.socketListeners.get(socket);\n if (wrapped) socket.off(String(eventName), wrapped);\n }\n set.delete(reg);\n break;\n }\n }\n if (set.size === 0) registrations.delete(String(eventName));\n dbg(String(eventName), {\n type: 'register',\n action: 'unregister',\n event: String(eventName),\n });\n };\n },\n\n off<K extends keyof T & string>(eventName: K): void {\n removeAllForEvent(String(eventName));\n },\n\n emit<K extends keyof T & string>(\n eventName: K,\n payload: Payload<T, K>,\n rooms?: string[] | string,\n metadata?: Record<string, unknown>,\n onAck?: (ack: unknown) => void\n ): void {\n const schema = getSchema(eventName);\n const check = schema.safeParse(payload);\n if (!check.success)\n throw new Error(`Invalid payload for \"${String(eventName)}\": ${check.error.message}`);\n\n const targets = toArray(rooms);\n const envelope = {\n eventName,\n sentAt: new Date(),\n sentTo: targets,\n data: check.data as Payload<T, K>,\n metadata: metadata ?? {},\n };\n\n // single socket target\n if (targets.length === 1) {\n const sid = targets[0]!;\n const sock = io.sockets.sockets.get(sid as any);\n if (sock) {\n if (onAck) {\n sock.emit(String(eventName), envelope, (ack: unknown) => {\n try {\n onAck(ack);\n } catch {\n /* noop */\n }\n });\n } else {\n sock.emit(String(eventName), envelope);\n }\n dbg(String(eventName), {\n type: 'emit',\n event: String(eventName),\n rooms: targets,\n envelope: debug.verbose ? envelope : undefined,\n });\n return;\n }\n }\n\n // broadcast\n if (targets.length === 0) io.emit(String(eventName), envelope);\n else io.to(targets).emit(String(eventName), envelope);\n\n dbg(String(eventName), {\n type: 'emit',\n event: String(eventName),\n rooms: targets,\n envelope: debug.verbose ? envelope : undefined,\n });\n },\n } as const satisfies SocketConnection<T>;\n\n return conn;\n}\n\nexport type { EventMap, Payload };\n"],"mappings":"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","names":[]}
@@ -190,10 +190,6 @@ export type RouteServerConfig<Ctx = unknown, Names extends string = string> = {
190
190
  * You can optionally include `routesLogger` to override handler debug logging per request.
191
191
  */
192
192
  buildCtx: (req: express.Request, res: express.Response) => CtxWithRoutesLogger<Ctx> | Promise<CtxWithRoutesLogger<Ctx>>;
193
- /**
194
- * Global middlewares for every bound route (run *after* buildCtx). Prefer `globalMiddleware.before`.
195
- */
196
- global?: Array<CtxRequestHandler<Ctx>>;
197
193
  /**
198
194
  * Grouped global middlewares that run *before* or *after* the handler for every route.
199
195
  */
@@ -3,6 +3,7 @@ import { z, ZodType } from 'zod';
3
3
  import type { SocketEvent } from '@emeryld/rrroutes-contract';
4
4
  type EventMap = Record<string, SocketEvent>;
5
5
  type Payload<T extends EventMap, K extends keyof T> = z.infer<T[K]['message']>;
6
+ type NoInfer<T> = [T][T extends any ? 0 : never];
6
7
  export type HandlerCtx = {
7
8
  sentAt: Date;
8
9
  socket: Socket;
@@ -18,68 +19,59 @@ export interface SocketConnection<T extends EventMap> {
18
19
  off<K extends keyof T & string>(eventName: K): void;
19
20
  emit<K extends keyof T & string>(eventName: K, payload: Payload<T, K>, toRooms?: string[] | string, metadata?: Record<string, unknown>, onAck?: (ack: unknown) => void): void;
20
21
  }
22
+ /**
23
+ * Grouped debug types:
24
+ * - register: register, unregister
25
+ * - handler: receive, validation_error, handler_success, handler_error
26
+ * - emit: emit
27
+ * - rooms: room_join, room_leave
28
+ * - auth: auth_ok, auth_error
29
+ * - heartbeat: ping, pong
30
+ */
21
31
  export type SocketServerDebugEvent<Ping extends ZodType = ZodType, Pong extends ZodType = ZodType> = {
22
32
  type: 'register';
33
+ action: 'register' | 'unregister';
23
34
  event: string;
24
35
  } | {
25
- type: 'unregister';
36
+ type: 'handler';
37
+ phase: 'receive' | 'validation_error' | 'handler_success' | 'handler_error';
26
38
  event: string;
27
- } | {
28
- type: 'receive';
29
- event: string;
30
- socketId: string;
31
- nsp: string;
32
- rooms: string[];
39
+ socketId?: string;
40
+ nsp?: string;
41
+ rooms?: string[];
33
42
  raw?: unknown;
34
- } | {
35
- type: 'validation_error';
36
- event: string;
37
- issues: any[];
38
- } | {
39
- type: 'handler_success';
40
- event: string;
41
- } | {
42
- type: 'handler_error';
43
- event: string;
44
- error: unknown;
43
+ issues?: any[];
44
+ error?: unknown;
45
45
  } | {
46
46
  type: 'emit';
47
47
  event: string;
48
48
  rooms: string[];
49
49
  envelope?: unknown;
50
50
  } | {
51
- type: 'room_join';
52
- rooms: string[];
53
- socketId: string;
54
- } | {
55
- type: 'room_leave';
51
+ type: 'rooms';
52
+ action: 'join' | 'leave';
56
53
  rooms: string[];
57
54
  socketId: string;
58
55
  } | {
59
- type: 'auth_ok';
60
- socketId: string;
61
- sub?: string;
62
- } | {
63
- type: 'auth_error';
56
+ type: 'auth';
57
+ phase: 'ok' | 'error';
64
58
  socketId?: string;
65
- err: string;
66
- } | {
67
- type: 'ping';
68
- socketId: string;
69
- payload?: NoInfer<z.infer<Ping>>;
59
+ sub?: string;
60
+ err?: string;
70
61
  } | {
71
- type: 'pong';
62
+ type: 'heartbeat';
63
+ phase: 'ping' | 'pong';
72
64
  socketId: string;
73
- payload?: NoInfer<z.infer<Pong>>;
65
+ payload?: NoInfer<z.infer<Ping>> | NoInfer<z.infer<Pong>>;
74
66
  issues?: any[];
75
- error: boolean;
67
+ error?: boolean;
76
68
  };
77
69
  export type SocketDebugOptions<Ping extends ZodType, Pong extends ZodType> = {
78
70
  verbose?: boolean;
79
71
  only?: string[];
80
72
  logger?: (e: SocketServerDebugEvent<Ping, Pong>) => void;
81
73
  } & {
82
- [K in SocketServerDebugEvent<Ping, Pong>['type']]?: boolean;
74
+ [P in SocketServerDebugEvent['type']]?: boolean;
83
75
  };
84
76
  export type BuiltInRoomHooks = {
85
77
  onRoomJoin?: (args: {
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@emeryld/rrroutes-server",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
- "module": "dist/index.mjs",
7
+ "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
12
+ "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },