MCPcopy Create free account
hub / github.com/Openpanel-dev/openpanel / getInfoFromSession

Function getInfoFromSession

apps/api/src/utils/ids.ts:84–136  ·  view source on GitHub ↗
({
  projectId,
  deviceIds,
  eventTimeMs,
}: {
  projectId: string;
  /** Candidate device ids in priority order (e.g. [current, previous] salt
   *  windows, or just [override]). Deduped; the first is canonical. */
  deviceIds: string[];
  eventTimeMs: number;
})

Source from the content-addressed store, hash-verified

82}
83
84async function getInfoFromSession({
85 projectId,
86 deviceIds,
87 eventTimeMs,
88}: {
89 projectId: string;
90 /** Candidate device ids in priority order (e.g. [current, previous] salt
91 * windows, or just [override]). Deduped; the first is canonical. */
92 deviceIds: string[];
93 eventTimeMs: number;
94}): Promise<DeviceIdResult> {
95 const candidates = [...new Set(deviceIds.filter(Boolean))];
96 const primary = candidates[0] ?? '';
97
98 try {
99 // Reading the live blob is the source of truth for an active session — it's
100 // what keeps a visit on one id across page reloads and salt rotation. Don't
101 // drop this read to "save" a lookup or sessions split at the bucket boundary.
102 const sessions = await Promise.all(
103 candidates.map((deviceId) =>
104 sessionBuffer.getExistingSession({ projectId, deviceId })
105 )
106 );
107
108 for (const [i, session] of sessions.entries()) {
109 if (session && withinIdleWindow(session, eventTimeMs)) {
110 return { deviceId: candidates[i]!, sessionId: session.id };
111 }
112 }
113 } catch (error) {
114 console.error('Error resolving session for device id', error);
115 }
116
117 return {
118 deviceId: primary,
119 // Deterministic id for the first event of a session and to bridge the window
120 // before the worker persists the blob (API resolves synchronously, worker
121 // writes async — same bucket → same id, so they agree).
122 //
123 // The bucket window MUST track the idle timeout: a gap > the window has to
124 // land in a new bucket so a boundary mints a *fresh* id. If it didn't (e.g.
125 // a hardcoded 30min while SESSION_TIMEOUT_MS is shorter), the worker would
126 // reopen the just-closed id and its session_end would be skipped. Grace must
127 // stay < window or getSessionId throws.
128 sessionId: getSessionId({
129 projectId,
130 deviceId: primary,
131 eventMs: eventTimeMs,
132 graceMs: Math.min(5_000, Math.floor(SESSION_TIMEOUT_MS / 6)),
133 windowMs: SESSION_TIMEOUT_MS,
134 }),
135 };
136}
137
138/**
139 * Deterministic session id for (projectId, deviceId) within a time window,

Callers 1

getDeviceIdFunction · 0.85

Calls 5

withinIdleWindowFunction · 0.85
getSessionIdFunction · 0.85
getExistingSessionMethod · 0.80
mapMethod · 0.45
errorMethod · 0.45

Tested by

no test coverage detected