@polishd/next 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/AGENTS.md +262 -0
  2. package/DATABASE.md +147 -0
  3. package/LICENSE +21 -0
  4. package/README.md +310 -0
  5. package/bin/init.mjs +224 -0
  6. package/dist/ai/actions.d.ts +23 -0
  7. package/dist/ai/actions.d.ts.map +1 -0
  8. package/dist/ai/actions.js +58 -0
  9. package/dist/ai/actions.js.map +1 -0
  10. package/dist/ai/digest.d.ts +9 -0
  11. package/dist/ai/digest.d.ts.map +1 -0
  12. package/dist/ai/digest.js +108 -0
  13. package/dist/ai/digest.js.map +1 -0
  14. package/dist/ai/github.d.ts +51 -0
  15. package/dist/ai/github.d.ts.map +1 -0
  16. package/dist/ai/github.js +175 -0
  17. package/dist/ai/github.js.map +1 -0
  18. package/dist/ai/guard.d.ts +43 -0
  19. package/dist/ai/guard.d.ts.map +1 -0
  20. package/dist/ai/guard.js +57 -0
  21. package/dist/ai/guard.js.map +1 -0
  22. package/dist/ai/issues.d.ts +21 -0
  23. package/dist/ai/issues.d.ts.map +1 -0
  24. package/dist/ai/issues.js +291 -0
  25. package/dist/ai/issues.js.map +1 -0
  26. package/dist/ai/profile.d.ts +26 -0
  27. package/dist/ai/profile.d.ts.map +1 -0
  28. package/dist/ai/profile.js +160 -0
  29. package/dist/ai/profile.js.map +1 -0
  30. package/dist/ai/providers.d.ts +40 -0
  31. package/dist/ai/providers.d.ts.map +1 -0
  32. package/dist/ai/providers.js +142 -0
  33. package/dist/ai/providers.js.map +1 -0
  34. package/dist/ai/scan.d.ts +38 -0
  35. package/dist/ai/scan.d.ts.map +1 -0
  36. package/dist/ai/scan.js +274 -0
  37. package/dist/ai/scan.js.map +1 -0
  38. package/dist/ai/settings.d.ts +31 -0
  39. package/dist/ai/settings.d.ts.map +1 -0
  40. package/dist/ai/settings.js +182 -0
  41. package/dist/ai/settings.js.map +1 -0
  42. package/dist/ai/summary.d.ts +26 -0
  43. package/dist/ai/summary.d.ts.map +1 -0
  44. package/dist/ai/summary.js +260 -0
  45. package/dist/ai/summary.js.map +1 -0
  46. package/dist/ai/types.d.ts +188 -0
  47. package/dist/ai/types.d.ts.map +1 -0
  48. package/dist/ai/types.js +13 -0
  49. package/dist/ai/types.js.map +1 -0
  50. package/dist/client/init.d.ts +14 -0
  51. package/dist/client/init.d.ts.map +1 -0
  52. package/dist/client/init.js +274 -0
  53. package/dist/client/init.js.map +1 -0
  54. package/dist/client/monitor.d.ts +23 -0
  55. package/dist/client/monitor.d.ts.map +1 -0
  56. package/dist/client/monitor.js +176 -0
  57. package/dist/client/monitor.js.map +1 -0
  58. package/dist/client.d.ts +11 -0
  59. package/dist/client.d.ts.map +1 -0
  60. package/dist/client.js +10 -0
  61. package/dist/client.js.map +1 -0
  62. package/dist/config.d.ts +36 -0
  63. package/dist/config.d.ts.map +1 -0
  64. package/dist/config.js +26 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/dashboard/elements.d.ts +6 -0
  67. package/dist/dashboard/elements.d.ts.map +1 -0
  68. package/dist/dashboard/elements.js +35 -0
  69. package/dist/dashboard/elements.js.map +1 -0
  70. package/dist/dashboard/features.d.ts +6 -0
  71. package/dist/dashboard/features.d.ts.map +1 -0
  72. package/dist/dashboard/features.js +28 -0
  73. package/dist/dashboard/features.js.map +1 -0
  74. package/dist/dashboard/index.d.ts +54 -0
  75. package/dist/dashboard/index.d.ts.map +1 -0
  76. package/dist/dashboard/index.js +140 -0
  77. package/dist/dashboard/index.js.map +1 -0
  78. package/dist/dashboard/journeys.d.ts +5 -0
  79. package/dist/dashboard/journeys.d.ts.map +1 -0
  80. package/dist/dashboard/journeys.js +158 -0
  81. package/dist/dashboard/journeys.js.map +1 -0
  82. package/dist/dashboard/pages.d.ts +5 -0
  83. package/dist/dashboard/pages.d.ts.map +1 -0
  84. package/dist/dashboard/pages.js +104 -0
  85. package/dist/dashboard/pages.js.map +1 -0
  86. package/dist/dashboard/summary.d.ts +13 -0
  87. package/dist/dashboard/summary.d.ts.map +1 -0
  88. package/dist/dashboard/summary.js +333 -0
  89. package/dist/dashboard/summary.js.map +1 -0
  90. package/dist/index.d.ts +10 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +10 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/proxy.d.ts +40 -0
  95. package/dist/proxy.d.ts.map +1 -0
  96. package/dist/proxy.js +29 -0
  97. package/dist/proxy.js.map +1 -0
  98. package/dist/route.d.ts +24 -0
  99. package/dist/route.d.ts.map +1 -0
  100. package/dist/route.js +37 -0
  101. package/dist/route.js.map +1 -0
  102. package/dist/server/ingest.d.ts +14 -0
  103. package/dist/server/ingest.d.ts.map +1 -0
  104. package/dist/server/ingest.js +73 -0
  105. package/dist/server/ingest.js.map +1 -0
  106. package/dist/server/queries.d.ts +245 -0
  107. package/dist/server/queries.d.ts.map +1 -0
  108. package/dist/server/queries.js +547 -0
  109. package/dist/server/queries.js.map +1 -0
  110. package/dist/server/store.d.ts +29 -0
  111. package/dist/server/store.d.ts.map +1 -0
  112. package/dist/server/store.js +359 -0
  113. package/dist/server/store.js.map +1 -0
  114. package/dist/server.d.ts +15 -0
  115. package/dist/server.d.ts.map +1 -0
  116. package/dist/server.js +15 -0
  117. package/dist/server.js.map +1 -0
  118. package/dist/session.d.ts +16 -0
  119. package/dist/session.d.ts.map +1 -0
  120. package/dist/session.js +29 -0
  121. package/dist/session.js.map +1 -0
  122. package/dist/shared/types.d.ts +56 -0
  123. package/dist/shared/types.d.ts.map +1 -0
  124. package/dist/shared/types.js +27 -0
  125. package/dist/shared/types.js.map +1 -0
  126. package/docs/SETUP.md +315 -0
  127. package/package.json +92 -0
@@ -0,0 +1,547 @@
1
+ /**
2
+ * Polishd — dashboard aggregations (server only).
3
+ *
4
+ * Read-side queries over the events table. These power the `/polishd` dashboard
5
+ * and, later, feed the Stage-2 synthesis prompt. Everything degrades to empty
6
+ * results when the store is in no-op mode, so the dashboard always renders.
7
+ *
8
+ * SQL here is written in a portable subset that runs on both backends: `?`
9
+ * placeholders (the store rewrites them per dialect) and boolean aggregates
10
+ * spelled `SUM(CASE WHEN … THEN 1 ELSE 0 END)` rather than SQLite's `SUM(x = y)`.
11
+ */
12
+ import { parseMeta, query, storeReady } from "./store";
13
+ /** Coerce a DB cell to a finite number. Tolerates pg's stringified bigints. */
14
+ function num(row, key) {
15
+ const v = row?.[key];
16
+ if (typeof v === "number")
17
+ return Number.isFinite(v) ? v : 0;
18
+ if (typeof v === "string") {
19
+ const n = Number(v);
20
+ return Number.isFinite(n) ? n : 0;
21
+ }
22
+ return 0;
23
+ }
24
+ export async function getOverview() {
25
+ if (!(await storeReady())) {
26
+ return {
27
+ ready: false,
28
+ totalEvents: 0,
29
+ sessions: 0,
30
+ pageViews: 0,
31
+ rageClicks: 0,
32
+ deadClicks: 0,
33
+ jsErrors: 0,
34
+ };
35
+ }
36
+ const [row] = await query(`SELECT
37
+ COUNT(*) AS "totalEvents",
38
+ COUNT(DISTINCT session_id) AS sessions,
39
+ SUM(CASE WHEN type = 'page_view' THEN 1 ELSE 0 END) AS "pageViews",
40
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS "rageClicks",
41
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS "deadClicks",
42
+ SUM(CASE WHEN type = 'js_error' THEN 1 ELSE 0 END) AS "jsErrors"
43
+ FROM events`);
44
+ return {
45
+ ready: true,
46
+ totalEvents: num(row, "totalEvents"),
47
+ sessions: num(row, "sessions"),
48
+ pageViews: num(row, "pageViews"),
49
+ rageClicks: num(row, "rageClicks"),
50
+ deadClicks: num(row, "deadClicks"),
51
+ jsErrors: num(row, "jsErrors"),
52
+ };
53
+ }
54
+ /**
55
+ * Score ranking. Weights match the build plan's guidance — rage clicks are
56
+ * the strongest frustration signal, then dead clicks and errors; shallow
57
+ * scroll (content not reached) contributes mildly.
58
+ */
59
+ export async function getPageStats(limit = 5) {
60
+ const rows = await query(`SELECT
61
+ path,
62
+ COUNT(DISTINCT session_id) AS sessions,
63
+ SUM(CASE WHEN type = 'page_view' THEN 1 ELSE 0 END) AS "pageViews",
64
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS "rageClicks",
65
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS "deadClicks",
66
+ SUM(CASE WHEN type = 'js_error' THEN 1 ELSE 0 END) AS "jsErrors",
67
+ AVG(CASE WHEN type = 'scroll_depth' THEN value END) AS "avgScrollDepth"
68
+ FROM events
69
+ GROUP BY path`);
70
+ return rows
71
+ .map((r) => {
72
+ const rageClicks = num(r, "rageClicks");
73
+ const deadClicks = num(r, "deadClicks");
74
+ const jsErrors = num(r, "jsErrors");
75
+ const avg = r.avgScrollDepth;
76
+ const avgNum = typeof avg === "number" ? avg : typeof avg === "string" ? Number(avg) : NaN;
77
+ const avgScrollDepth = Number.isFinite(avgNum) ? Math.round(avgNum) : null;
78
+ const shallowPenalty = avgScrollDepth !== null && avgScrollDepth < 50 ? (50 - avgScrollDepth) / 25 : 0;
79
+ const score = rageClicks * 3 + deadClicks * 2 + jsErrors * 2.5 + shallowPenalty;
80
+ return {
81
+ path: r.path,
82
+ sessions: num(r, "sessions"),
83
+ pageViews: num(r, "pageViews"),
84
+ rageClicks,
85
+ deadClicks,
86
+ jsErrors,
87
+ avgScrollDepth,
88
+ score: Math.round(score * 10) / 10,
89
+ };
90
+ })
91
+ .sort((a, b) => b.score - a.score)
92
+ .slice(0, limit);
93
+ }
94
+ const DAY_MS = 86_400_000;
95
+ /** Cap the trend window so a wide date range stays a readable chart. */
96
+ const MAX_TREND_DAYS = 30;
97
+ function dayStart(ts) {
98
+ return ts - (ts % DAY_MS);
99
+ }
100
+ function fmtDay(ts) {
101
+ return new Date(ts).toLocaleDateString(undefined, {
102
+ month: "short",
103
+ day: "numeric",
104
+ timeZone: "UTC",
105
+ });
106
+ }
107
+ /**
108
+ * The most-visited pages, ranked by distinct sessions, each with a daily
109
+ * sessions-over-time series for the detail chart. The aggregate is one grouped
110
+ * query; the trend comes from a second pull of page_view events for just the
111
+ * top paths, bucketed by UTC day in JS so it stays backend-portable.
112
+ */
113
+ export async function getTopPages(limit = 8) {
114
+ if (!(await storeReady()))
115
+ return [];
116
+ const rows = await query(`SELECT
117
+ path,
118
+ COUNT(DISTINCT session_id) AS sessions,
119
+ SUM(CASE WHEN type = 'page_view' THEN 1 ELSE 0 END) AS "pageViews",
120
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS "rageClicks",
121
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS "deadClicks",
122
+ SUM(CASE WHEN type = 'js_error' THEN 1 ELSE 0 END) AS "jsErrors",
123
+ AVG(CASE WHEN type = 'scroll_depth' THEN value END) AS "avgScrollDepth"
124
+ FROM events
125
+ GROUP BY path`);
126
+ const pages = rows
127
+ .map((r) => {
128
+ const avg = r.avgScrollDepth;
129
+ const avgNum = typeof avg === "number" ? avg : typeof avg === "string" ? Number(avg) : NaN;
130
+ return {
131
+ path: r.path,
132
+ sessions: num(r, "sessions"),
133
+ pageViews: num(r, "pageViews"),
134
+ rageClicks: num(r, "rageClicks"),
135
+ deadClicks: num(r, "deadClicks"),
136
+ jsErrors: num(r, "jsErrors"),
137
+ avgScrollDepth: Number.isFinite(avgNum) ? Math.round(avgNum) : null,
138
+ };
139
+ })
140
+ // "Top" = most visited; page views break ties.
141
+ .sort((a, b) => b.sessions - a.sessions || b.pageViews - a.pageViews)
142
+ .slice(0, limit);
143
+ if (pages.length === 0)
144
+ return [];
145
+ // Pull page_view timestamps for just the top paths, then bucket by day.
146
+ const paths = pages.map((p) => p.path);
147
+ const placeholders = paths.map(() => "?").join(", ");
148
+ const views = await query(`SELECT path, ts, session_id
149
+ FROM events
150
+ WHERE type = 'page_view' AND path IN (${placeholders})`, paths);
151
+ // path → (UTC day → set of sessions seen that day)
152
+ const byPath = new Map();
153
+ for (const v of views) {
154
+ const p = v.path;
155
+ const day = dayStart(num(v, "ts"));
156
+ let days = byPath.get(p);
157
+ if (!days)
158
+ byPath.set(p, (days = new Map()));
159
+ let set = days.get(day);
160
+ if (!set)
161
+ days.set(day, (set = new Set()));
162
+ set.add(v.session_id);
163
+ }
164
+ // Build a gap-filled daily series (zeros included) over the page's range,
165
+ // capped to the most recent MAX_TREND_DAYS so wide ranges stay readable.
166
+ const buildTrend = (days) => {
167
+ if (!days || days.size === 0)
168
+ return [];
169
+ const keys = [...days.keys()].sort((a, b) => a - b);
170
+ const end = keys[keys.length - 1];
171
+ let start = keys[0];
172
+ if ((end - start) / DAY_MS > MAX_TREND_DAYS - 1)
173
+ start = end - (MAX_TREND_DAYS - 1) * DAY_MS;
174
+ const out = [];
175
+ for (let d = start; d <= end; d += DAY_MS) {
176
+ out.push({ ts: d, label: fmtDay(d), sessions: days.get(d)?.size ?? 0 });
177
+ }
178
+ return out;
179
+ };
180
+ return pages.map((p) => ({ ...p, trend: buildTrend(byPath.get(p.path)) }));
181
+ }
182
+ /**
183
+ * Per-element interaction breakdown, grouped by DOM selector path. This is the
184
+ * data that makes Stage 2 synthesis possible: it tells you *which UI element*
185
+ * the issues are, not just which page.
186
+ *
187
+ * Explicitly monitored components (those wrapped in <PolishdMonitor>, the only
188
+ * source of `data-component`) are excluded here — they get their own dedicated
189
+ * "Monitored components" section, so this table covers the rest of the UI.
190
+ */
191
+ export async function getElementStats(limit = 12) {
192
+ const rows = await query(`SELECT
193
+ COALESCE(selector, '(unknown)') AS label,
194
+ MAX(selector) AS selector,
195
+ MAX(text) AS "sampleText",
196
+ COUNT(DISTINCT path) AS pages,
197
+ SUM(CASE WHEN type = 'click' THEN 1 ELSE 0 END) AS clicks,
198
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS "rageClicks",
199
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS "deadClicks",
200
+ MAX(ts) AS "lastTs"
201
+ FROM events
202
+ WHERE type IN ('click', 'rage_click', 'dead_click') AND component IS NULL
203
+ GROUP BY COALESCE(selector, '(unknown)')`);
204
+ return rows
205
+ .map((r) => {
206
+ const rageClicks = num(r, "rageClicks");
207
+ const deadClicks = num(r, "deadClicks");
208
+ const clicks = num(r, "clicks");
209
+ return {
210
+ label: r.label,
211
+ selector: r.selector ?? null,
212
+ sampleText: r.sampleText ?? null,
213
+ pages: num(r, "pages"),
214
+ clicks,
215
+ rageClicks,
216
+ deadClicks,
217
+ score: Math.round((rageClicks * 3 + deadClicks * 2) * 10) / 10,
218
+ lastInteraction: num(r, "lastTs"),
219
+ };
220
+ })
221
+ // Most recently interacted-with element first; score breaks ties.
222
+ .sort((a, b) => b.lastInteraction - a.lastInteraction || b.score - a.score)
223
+ .slice(0, limit);
224
+ }
225
+ /**
226
+ * Device-size distribution. One `viewport` event is sampled per session at
227
+ * start; its category lives in the `text` column (mobile/tablet/desktop) and
228
+ * its width in `value`, so this groups without touching the JSON `meta` column.
229
+ * Sorted most-used device first — answers "what size screen do visitors use?".
230
+ */
231
+ export async function getDeviceBreakdown() {
232
+ const rows = await query(`SELECT
233
+ text AS category,
234
+ COUNT(DISTINCT session_id) AS sessions,
235
+ AVG(value) AS "avgWidth"
236
+ FROM events
237
+ WHERE type = 'viewport' AND text IS NOT NULL
238
+ GROUP BY text`);
239
+ const buckets = rows.map((r) => {
240
+ const avg = r.avgWidth;
241
+ const avgNum = typeof avg === "number" ? avg : typeof avg === "string" ? Number(avg) : NaN;
242
+ return {
243
+ category: r.category,
244
+ sessions: num(r, "sessions"),
245
+ avgWidth: Number.isFinite(avgNum) ? Math.round(avgNum) : null,
246
+ };
247
+ });
248
+ const total = buckets.reduce((sum, b) => sum + b.sessions, 0);
249
+ return buckets
250
+ .map((b) => ({
251
+ ...b,
252
+ pct: total > 0 ? Math.round((b.sessions / total) * 1000) / 10 : 0,
253
+ }))
254
+ .sort((a, b) => b.sessions - a.sessions);
255
+ }
256
+ /**
257
+ * Most-used features. Ranks interactive elements by raw click volume (the
258
+ * `click` type — successful clicks on links/buttons/etc., excluding rage and
259
+ * dead clicks). Where `getElementStats` surfaces what's *broken*, this
260
+ * surfaces what's *popular* — which buttons and features people actually use.
261
+ */
262
+ export async function getTopInteractions(limit = 12) {
263
+ const rows = await query(`SELECT
264
+ COALESCE(component, selector, '(unknown)') AS label,
265
+ MAX(CASE WHEN component IS NOT NULL THEN 1 ELSE 0 END) AS "isComponent",
266
+ MAX(selector) AS selector,
267
+ MAX(text) AS "sampleText",
268
+ COUNT(*) AS clicks,
269
+ COUNT(DISTINCT session_id) AS sessions,
270
+ COUNT(DISTINCT path) AS pages
271
+ FROM events
272
+ WHERE type = 'click'
273
+ GROUP BY COALESCE(component, selector, '(unknown)')`);
274
+ return rows
275
+ .map((r) => ({
276
+ label: r.label,
277
+ isComponent: num(r, "isComponent") === 1,
278
+ selector: r.selector ?? null,
279
+ sampleText: r.sampleText ?? null,
280
+ clicks: num(r, "clicks"),
281
+ sessions: num(r, "sessions"),
282
+ pages: num(r, "pages"),
283
+ }))
284
+ .sort((a, b) => b.clicks - a.clicks || b.sessions - a.sessions)
285
+ .slice(0, limit);
286
+ }
287
+ /** Event types that are user *actions* (the flow-chart nodes). */
288
+ const JOURNEY_ACTION_TYPES = new Set([
289
+ "page_view",
290
+ "click",
291
+ "rage_click",
292
+ "dead_click",
293
+ "js_error",
294
+ "session_end",
295
+ ]);
296
+ /** Keep journeys readable: cap how many steps a single flow chart renders. */
297
+ const MAX_JOURNEY_STEPS = 50;
298
+ /**
299
+ * Sampled full-session recordings, reconstructed as ordered action flows.
300
+ *
301
+ * Sampling strategy: every session is scored by a composite score (rage×3 + dead×2 +
302
+ * errors×2.5). We surface the highest-scoring sessions first — those are the
303
+ * ones worth replaying — preferring *complete* recordings (a `session_end` was
304
+ * captured) and breaking ties by recency. When there's no signal at all this
305
+ * degrades to "most recent complete sessions", so the section is still useful.
306
+ */
307
+ export async function getSessionJourneys(limit = 6) {
308
+ if (!(await storeReady()))
309
+ return [];
310
+ // 1. Score and rank candidate sessions for sampling.
311
+ const summary = await query(`SELECT
312
+ session_id AS id,
313
+ MIN(ts) AS started,
314
+ MAX(ts) AS ended,
315
+ COUNT(DISTINCT path) AS pages,
316
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS rage,
317
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS dead,
318
+ SUM(CASE WHEN type = 'js_error' THEN 1 ELSE 0 END) AS errors,
319
+ MAX(CASE WHEN type = 'session_end' THEN 1 ELSE 0 END) AS complete,
320
+ SUM(CASE WHEN type IN ('page_view','click','rage_click','dead_click')
321
+ THEN 1 ELSE 0 END) AS actions
322
+ FROM events
323
+ GROUP BY session_id`);
324
+ const ranked = summary
325
+ .map((r) => {
326
+ const rage = num(r, "rage");
327
+ const dead = num(r, "dead");
328
+ const errors = num(r, "errors");
329
+ return {
330
+ id: r.id,
331
+ started: num(r, "started"),
332
+ ended: num(r, "ended"),
333
+ pages: num(r, "pages"),
334
+ rage,
335
+ dead,
336
+ errors,
337
+ complete: num(r, "complete") === 1,
338
+ actions: num(r, "actions"),
339
+ score: Math.round((rage * 3 + dead * 2 + errors * 2.5) * 10) / 10,
340
+ };
341
+ })
342
+ // A "journey" needs at least a couple of actions to be worth showing.
343
+ .filter((s) => s.actions >= 2)
344
+ .sort((a, b) => b.score - a.score ||
345
+ Number(b.complete) - Number(a.complete) ||
346
+ b.ended - a.ended)
347
+ .slice(0, limit);
348
+ if (ranked.length === 0)
349
+ return [];
350
+ // 2. Pull every event for just the sampled sessions, in chronological order.
351
+ const ids = ranked.map((s) => s.id);
352
+ const placeholders = ids.map(() => "?").join(", ");
353
+ const rows = await query(`SELECT session_id, type, ts, path, selector, component, text, value, meta
354
+ FROM events
355
+ WHERE session_id IN (${placeholders})
356
+ ORDER BY session_id, id`, ids);
357
+ // 3. Group rows back into per-session event streams.
358
+ const streams = new Map();
359
+ for (const r of rows) {
360
+ const sid = r.session_id;
361
+ const list = streams.get(sid);
362
+ if (list)
363
+ list.push(r);
364
+ else
365
+ streams.set(sid, [r]);
366
+ }
367
+ // 4. Build a journey per ranked session, preserving the ranking order.
368
+ return ranked.map((s) => {
369
+ const events = streams.get(s.id) ?? [];
370
+ const device = { category: null, width: null, height: null, dpr: null };
371
+ const steps = [];
372
+ for (const e of events) {
373
+ const type = e.type;
374
+ // The viewport event isn't an action — it carries device details.
375
+ if (type === "viewport") {
376
+ device.category = e.text ?? null;
377
+ device.width = e.value == null ? null : Number(e.value);
378
+ const meta = parseMeta(e.meta);
379
+ if (meta) {
380
+ if (typeof meta.h === "number")
381
+ device.height = meta.h;
382
+ if (typeof meta.dpr === "number")
383
+ device.dpr = meta.dpr;
384
+ }
385
+ continue;
386
+ }
387
+ if (!JOURNEY_ACTION_TYPES.has(type))
388
+ continue; // scroll_depth, web_vital
389
+ if (steps.length >= MAX_JOURNEY_STEPS)
390
+ break;
391
+ let label = null;
392
+ let detail = null;
393
+ if (type === "js_error") {
394
+ const meta = parseMeta(e.meta);
395
+ detail = meta && typeof meta.message === "string" ? meta.message : "Unknown error";
396
+ }
397
+ else if (type !== "page_view" && type !== "session_end") {
398
+ label =
399
+ e.component || e.text || e.selector || null;
400
+ }
401
+ steps.push({ type, ts: num(e, "ts"), path: e.path, label, detail });
402
+ }
403
+ return {
404
+ id: s.id.slice(0, 8),
405
+ startedAt: s.started,
406
+ endedAt: s.ended,
407
+ durationMs: Math.max(0, s.ended - s.started),
408
+ complete: s.complete,
409
+ pages: s.pages,
410
+ device,
411
+ rageClicks: s.rage,
412
+ deadClicks: s.dead,
413
+ jsErrors: s.errors,
414
+ score: s.score,
415
+ steps,
416
+ truncated: steps.length >= MAX_JOURNEY_STEPS,
417
+ };
418
+ });
419
+ }
420
+ /**
421
+ * Fetch all components explicitly wrapped in <PolishdMonitor>. The definitive
422
+ * marker is the presence of at least one "hover", "component_view", or "mount"
423
+ * event for that component name (all three are only emitted by PolishdMonitor).
424
+ * We then pull all event types for those components so the table shows the
425
+ * complete picture.
426
+ *
427
+ * The per-view `meta` (height, scrollDepth) is aggregated in JS rather than SQL:
428
+ * the two backends spell JSON extraction differently (SQLite `JSON_EXTRACT`,
429
+ * Postgres `->>`), and there's no portable function, so we parse meta here —
430
+ * matching how `getSessionJourneys` and `getTopPages` keep their SQL portable.
431
+ */
432
+ export async function getMonitoredComponents() {
433
+ if (!(await storeReady()))
434
+ return [];
435
+ const rows = await query(`SELECT
436
+ component AS name,
437
+ SUM(CASE WHEN type = 'click' THEN 1 ELSE 0 END) AS clicks,
438
+ SUM(CASE WHEN type = 'rage_click' THEN 1 ELSE 0 END) AS "rageClicks",
439
+ SUM(CASE WHEN type = 'dead_click' THEN 1 ELSE 0 END) AS "deadClicks",
440
+ SUM(CASE WHEN type = 'hover' THEN 1 ELSE 0 END) AS hovers,
441
+ AVG(CASE WHEN type = 'hover' THEN value END) AS "avgHoverMs",
442
+ SUM(CASE WHEN type = 'mount' THEN 1 ELSE 0 END) AS mounts,
443
+ SUM(CASE WHEN type = 'component_view' THEN 1 ELSE 0 END) AS "componentViews",
444
+ AVG(CASE WHEN type = 'component_view' THEN value END) AS "avgViewMs",
445
+ COUNT(DISTINCT session_id) AS sessions,
446
+ COUNT(DISTINCT path) AS pages
447
+ FROM events
448
+ WHERE component IN (
449
+ SELECT DISTINCT component FROM events
450
+ WHERE type IN ('hover', 'component_view', 'mount') AND component IS NOT NULL
451
+ )
452
+ GROUP BY component
453
+ ORDER BY "componentViews" DESC, mounts DESC, hovers DESC`);
454
+ if (rows.length === 0)
455
+ return [];
456
+ // Pull component_view meta for just these components and reduce the
457
+ // height/scrollDepth in JS (portable across SQLite + Postgres). Height takes
458
+ // the max observed (the tallest element the name was attached to — the
459
+ // article body); scroll depth is averaged across visits.
460
+ const names = rows.map((r) => r.name);
461
+ const placeholders = names.map(() => "?").join(", ");
462
+ const viewRows = await query(`SELECT component, meta FROM events
463
+ WHERE type = 'component_view' AND component IN (${placeholders})`, names);
464
+ // component → max height + running scroll-depth average.
465
+ const dims = new Map();
466
+ for (const v of viewRows) {
467
+ const name = v.component;
468
+ const meta = parseMeta(v.meta);
469
+ if (!meta)
470
+ continue;
471
+ let acc = dims.get(name);
472
+ if (!acc)
473
+ dims.set(name, (acc = { hMax: 0, hSeen: false, dSum: 0, dN: 0 }));
474
+ if (typeof meta.height === "number") {
475
+ acc.hSeen = true;
476
+ if (meta.height > acc.hMax)
477
+ acc.hMax = meta.height;
478
+ }
479
+ if (typeof meta.scrollDepth === "number") {
480
+ acc.dSum += meta.scrollDepth;
481
+ acc.dN++;
482
+ }
483
+ }
484
+ const toNum = (v) => {
485
+ if (typeof v === "number")
486
+ return Number.isFinite(v) ? v : null;
487
+ if (typeof v === "string") {
488
+ const n = Number(v);
489
+ return Number.isFinite(n) ? n : null;
490
+ }
491
+ return null;
492
+ };
493
+ return rows.map((r) => {
494
+ const name = r.name;
495
+ const acc = dims.get(name);
496
+ return {
497
+ name,
498
+ clicks: num(r, "clicks"),
499
+ rageClicks: num(r, "rageClicks"),
500
+ deadClicks: num(r, "deadClicks"),
501
+ hovers: num(r, "hovers"),
502
+ avgHoverMs: toNum(r.avgHoverMs) !== null ? Math.round(toNum(r.avgHoverMs)) : null,
503
+ mounts: num(r, "mounts"),
504
+ componentViews: num(r, "componentViews"),
505
+ avgViewMs: toNum(r.avgViewMs) !== null ? Math.round(toNum(r.avgViewMs)) : null,
506
+ heightPx: acc && acc.hSeen ? Math.round(acc.hMax) : null,
507
+ avgScrollDepth: acc && acc.dN > 0 ? Math.round(acc.dSum / acc.dN) : null,
508
+ sessions: num(r, "sessions"),
509
+ pages: num(r, "pages"),
510
+ };
511
+ });
512
+ }
513
+ /**
514
+ * Fetch every dashboard query in parallel. Server-only. Lives here (not in the
515
+ * dashboard) so the AI summary layer can reuse it without importing React.
516
+ */
517
+ export async function loadPolishdDashboardData() {
518
+ const [overview, pages, elements, devices, topUsed, journeys, errors, monitored] = await Promise.all([
519
+ getOverview(),
520
+ getTopPages(8),
521
+ getElementStats(12),
522
+ getDeviceBreakdown(),
523
+ getTopInteractions(12),
524
+ getSessionJourneys(6),
525
+ getRecentErrors(8),
526
+ getMonitoredComponents(),
527
+ ]);
528
+ return { overview, pages, elements, devices, topUsed, journeys, errors, monitored };
529
+ }
530
+ export async function getRecentErrors(limit = 10) {
531
+ const rows = await query(`SELECT path, component, meta, ts
532
+ FROM events WHERE type = 'js_error'
533
+ ORDER BY id DESC LIMIT ?`, [limit]);
534
+ return rows.map((r) => {
535
+ let message = "Unknown error";
536
+ const meta = parseMeta(r.meta);
537
+ if (meta && typeof meta.message === "string")
538
+ message = meta.message;
539
+ return {
540
+ path: r.path,
541
+ component: r.component ?? undefined,
542
+ message,
543
+ ts: num(r, "ts"),
544
+ };
545
+ });
546
+ }
547
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/server/queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { PolishdEvent, PolishdEventRow } from "../shared/types";
2
+ /**
3
+ * A backend-agnostic row map. Queries are written once, in a portable SQL
4
+ * subset, with `?` placeholders; each backend adapts them to its own dialect.
5
+ */
6
+ type Row = Record<string, unknown>;
7
+ /** True when events are actually being persisted (used by the dashboard). */
8
+ export declare function storeReady(): Promise<boolean>;
9
+ /** Persist a batch of events for one session. Silently drops if no store. */
10
+ export declare function insertEvents(sessionId: string, events: PolishdEvent[]): Promise<number>;
11
+ /**
12
+ * Run a portable read query, or return `[]` in no-op mode. Use `?` placeholders;
13
+ * each backend rewrites them to its own dialect. The aggregation layer (and the
14
+ * dashboard) goes through here so it never has to know which backend is live.
15
+ */
16
+ export declare function query(sql: string, params?: unknown[]): Promise<Row[]>;
17
+ /** Read one metadata value by key, or null when absent / no store. */
18
+ export declare function getMeta(key: string): Promise<string | null>;
19
+ /** Upsert one metadata value. Returns false when the store is unavailable. */
20
+ export declare function setMeta(key: string, value: string): Promise<boolean>;
21
+ /** Raw rows, newest first. For debugging and the queries layer. */
22
+ export declare function allRows(limit?: number): Promise<PolishdEventRow[]>;
23
+ /**
24
+ * Normalize the `meta` column across backends: SQLite stores it as a JSON
25
+ * string, Postgres' JSONB comes back already parsed. Accept either.
26
+ */
27
+ export declare function parseMeta(raw: unknown): PolishdEventRow["meta"];
28
+ export {};
29
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;GAGG;AACH,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA8RnC,6EAA6E;AAC7E,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAEnD;AAED,6EAA6E;AAC7E,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EAAE,GACrB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAY/E;AAQD,sEAAsE;AACtE,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIjE;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB1E;AAED,mEAAmE;AACnE,wBAAsB,OAAO,CAAC,KAAK,SAAO,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAGtE;AAkBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAW/D"}