{"version":3,"file":"sessions.mjs","sources":["../src/sessions.ts"],"sourcesContent":["import { getDeviceName } from './user-agent';\n\nexport interface SessionEntryLike {\n  sessionId: string;\n  deviceId?: string;\n  createdAt?: Date | string | null;\n  metadata?: Record<string, unknown> | null;\n}\n\nexport interface SanitizedSessionEntry {\n  id: string;\n  deviceId?: string;\n  deviceName?: string;\n  current: boolean;\n  loginAt?: string;\n  lastActiveAt?: string;\n}\n\nexport interface SessionDisplayEntry {\n  current: boolean;\n  lastActiveAt?: string;\n}\n\n/**\n * Sanitizes a session record for client consumption (active devices UI/API).\n * Never includes tokens or internal database identifiers.\n */\nexport const sanitizeSessionEntry = (\n  session: SessionEntryLike,\n  currentSessionId?: string\n): SanitizedSessionEntry => {\n  const metadata = session.metadata ?? {};\n\n  return {\n    id: session.sessionId,\n    deviceId: session.deviceId,\n    deviceName: typeof metadata.deviceName === 'string' ? metadata.deviceName : undefined,\n    current: Boolean(currentSessionId) && session.sessionId === currentSessionId,\n    loginAt: typeof metadata.loginAt === 'string' ? metadata.loginAt : undefined,\n    // The active record is re-created on each rotation, so its creation time is the\n    // best available \"last used\" signal without an extra write per request.\n    lastActiveAt: session.createdAt ? new Date(session.createdAt).toISOString() : undefined,\n  };\n};\n\n/**\n * Builds origin-defined session metadata from generic request context fields.\n */\nexport const buildSessionMetadata = (params: {\n  userAgent?: string | null;\n  loginAt?: string;\n}): Record<string, unknown> => {\n  const deviceName = getDeviceName(params.userAgent);\n\n  return {\n    loginAt: params.loginAt ?? new Date().toISOString(),\n    ...(deviceName ? { deviceName } : {}),\n  };\n};\n\n/**\n * Sorts sessions for display: current session first, then most recently used.\n */\nexport const sortSessionsForDisplay = <T extends SessionDisplayEntry>(sessions: T[]): T[] =>\n  [...sessions].sort((a, b) => {\n    if (a.current !== b.current) {\n      return a.current ? -1 : 1;\n    }\n\n    return (b.lastActiveAt ?? '').localeCompare(a.lastActiveAt ?? '');\n  });\n"],"names":["sanitizeSessionEntry","session","currentSessionId","metadata","id","sessionId","deviceId","deviceName","undefined","current","Boolean","loginAt","lastActiveAt","createdAt","Date","toISOString","buildSessionMetadata","params","getDeviceName","userAgent","sortSessionsForDisplay","sessions","sort","a","b","localeCompare"],"mappings":";;AAuBA;;;AAGC,IACM,MAAMA,oBAAAA,GAAuB,CAClCC,OAAAA,EACAC,gBAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAAA,GAAWF,OAAAA,CAAQE,QAAQ,IAAI,EAAC;IAEtC,OAAO;AACLC,QAAAA,EAAAA,EAAIH,QAAQI,SAAS;AACrBC,QAAAA,QAAAA,EAAUL,QAAQK,QAAQ;AAC1BC,QAAAA,UAAAA,EAAY,OAAOJ,QAAAA,CAASI,UAAU,KAAK,QAAA,GAAWJ,QAAAA,CAASI,UAAU,GAAGC,SAAAA;AAC5EC,QAAAA,OAAAA,EAASC,OAAAA,CAAQR,gBAAAA,CAAAA,IAAqBD,OAAAA,CAAQI,SAAS,KAAKH,gBAAAA;AAC5DS,QAAAA,OAAAA,EAAS,OAAOR,QAAAA,CAASQ,OAAO,KAAK,QAAA,GAAWR,QAAAA,CAASQ,OAAO,GAAGH,SAAAA;;;QAGnEI,YAAAA,EAAcX,OAAAA,CAAQY,SAAS,GAAG,IAAIC,KAAKb,OAAAA,CAAQY,SAAS,CAAA,CAAEE,WAAW,EAAA,GAAKP;AAChF,KAAA;AACF;AAEA;;IAGO,MAAMQ,oBAAAA,GAAuB,CAACC,MAAAA,GAAAA;IAInC,MAAMV,UAAAA,GAAaW,aAAAA,CAAcD,MAAAA,CAAOE,SAAS,CAAA;IAEjD,OAAO;AACLR,QAAAA,OAAAA,EAASM,MAAAA,CAAON,OAAO,IAAI,IAAIG,OAAOC,WAAW,EAAA;AACjD,QAAA,GAAIR,UAAAA,GAAa;AAAEA,YAAAA;AAAW,SAAA,GAAI;AACpC,KAAA;AACF;AAEA;;AAEC,IACM,MAAMa,sBAAAA,GAAyB,CAAgCC,QAAAA,GACpE;AAAIA,QAAAA,GAAAA;KAAS,CAACC,IAAI,CAAC,CAACC,CAAAA,EAAGC,CAAAA,GAAAA;AACrB,QAAA,IAAID,CAAAA,CAAEd,OAAO,KAAKe,CAAAA,CAAEf,OAAO,EAAE;AAC3B,YAAA,OAAOc,CAAAA,CAAEd,OAAO,GAAG,EAAC,GAAI,CAAA;AAC1B,QAAA;QAEA,OAAQe,CAAAA,CAAAA,CAAEZ,YAAY,IAAI,EAAC,EAAGa,aAAa,CAACF,CAAAA,CAAEX,YAAY,IAAI,EAAA,CAAA;IAChE,CAAA;;;;"}