@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,359 @@
1
+ /**
2
+ * Polishd — analytics store (server only).
3
+ *
4
+ * Two interchangeable backends sit behind one async interface:
5
+ *
6
+ * • SQLite via Node's built-in `node:sqlite` (Node 22+) — zero npm dependency,
7
+ * no native build, used for local development by default.
8
+ * • Postgres via `pg` — used in production, selected by `POLISHD_DATABASE_URL`.
9
+ * Most modern hosts (Vercel, Netlify) run on a read-only, ephemeral
10
+ * filesystem where a local SQLite file can't persist, so real traffic must
11
+ * point at a networked database. See `DATABASE.md`.
12
+ *
13
+ * The store opens lazily and is deliberately fault-tolerant: if neither backend
14
+ * can be opened (read-only FS, unreachable database, bad URL) it latches to a
15
+ * safe no-op — capture silently drops and every read returns empty — so the
16
+ * host app is never taken down by analytics.
17
+ *
18
+ * The interface is async because Postgres is; the SQLite backend simply resolves
19
+ * its synchronous results. Callers never know which backend is live.
20
+ */
21
+ import { defaultPolishdConfig } from "../config";
22
+ /** The columns every backend inserts, in a fixed order, for one event. */
23
+ const INSERT_COLS = "session_id, type, ts, path, selector, component, text, value, meta, received_at";
24
+ /** Turn one event into its positional values, matching `INSERT_COLS`. */
25
+ function eventValues(sessionId, e, now) {
26
+ return [
27
+ sessionId,
28
+ e.type,
29
+ e.ts,
30
+ e.path,
31
+ e.selector ?? null,
32
+ e.component ?? null,
33
+ e.text ?? null,
34
+ e.value ?? null,
35
+ e.meta ? JSON.stringify(e.meta) : null,
36
+ now,
37
+ ];
38
+ }
39
+ // ── Legacy metadata migration ────────────────────────────────────────────────
40
+ // The metadata table was called `buffd_meta` before the Polishd rename. Copy any
41
+ // rows it holds into `polishd_meta` once, at backend open, so an existing
42
+ // deployment keeps its AI settings, cached summary, and project profile. The
43
+ // legacy table is absent on every fresh install, and the statement raises when
44
+ // it is — that is the expected path, not an error worth surfacing. Existing
45
+ // rows always win, so the copy is idempotent and safe to re-run.
46
+ function migrateLegacyMeta(exec, sql) {
47
+ try {
48
+ exec(sql);
49
+ }
50
+ catch {
51
+ // No pre-rename table — nothing to carry over.
52
+ }
53
+ }
54
+ async function migrateLegacyMetaAsync(exec, sql) {
55
+ try {
56
+ await exec(sql);
57
+ }
58
+ catch {
59
+ // No pre-rename table — nothing to carry over.
60
+ }
61
+ }
62
+ // ── SQLite backend ───────────────────────────────────────────────────────────
63
+ import { dirname, isAbsolute, join } from "node:path";
64
+ import { mkdirSync } from "node:fs";
65
+ function resolveDbPath() {
66
+ // BUFFD_DB_PATH and POLISH_DB_PATH are read as fallbacks for apps set up
67
+ // before each rename, so existing deployments keep working untouched.
68
+ const configured = process.env.POLISHD_DB_PATH ||
69
+ process.env.BUFFD_DB_PATH ||
70
+ process.env.POLISH_DB_PATH ||
71
+ defaultPolishdConfig.databasePath;
72
+ return isAbsolute(configured) ? configured : join(process.cwd(), configured);
73
+ }
74
+ async function openSqlite() {
75
+ // Imported dynamically, not at module scope: `node:sqlite` only exists on
76
+ // Node 22.5+, and a static import would throw while this module is being
77
+ // evaluated — before the caller's try/catch can degrade to the no-op backend.
78
+ // A host on an older Node would take down every route importing the package
79
+ // rather than simply losing analytics.
80
+ const { DatabaseSync } = await import("node:sqlite");
81
+ const path = resolveDbPath();
82
+ mkdirSync(dirname(path), { recursive: true });
83
+ const db = new DatabaseSync(path);
84
+ db.exec(`
85
+ CREATE TABLE IF NOT EXISTS events (
86
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ session_id TEXT NOT NULL,
88
+ type TEXT NOT NULL,
89
+ ts INTEGER NOT NULL,
90
+ path TEXT NOT NULL,
91
+ selector TEXT,
92
+ component TEXT,
93
+ text TEXT,
94
+ value REAL,
95
+ meta TEXT,
96
+ received_at INTEGER NOT NULL
97
+ );
98
+ CREATE INDEX IF NOT EXISTS idx_events_path ON events(path);
99
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
100
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
101
+
102
+ CREATE TABLE IF NOT EXISTS polishd_meta (
103
+ key TEXT PRIMARY KEY,
104
+ value TEXT NOT NULL,
105
+ updated_at INTEGER NOT NULL
106
+ );
107
+ `);
108
+ migrateLegacyMeta((sql) => db.exec(sql), `INSERT OR IGNORE INTO polishd_meta (key, value, updated_at)
109
+ SELECT key, value, updated_at FROM buffd_meta`);
110
+ return {
111
+ async insert(sessionId, events) {
112
+ if (events.length === 0)
113
+ return 0;
114
+ const now = Date.now();
115
+ const stmt = db.prepare(`INSERT INTO events (${INSERT_COLS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
116
+ let written = 0;
117
+ // node:sqlite has no .transaction() helper; wrap manually for batch speed.
118
+ db.exec("BEGIN");
119
+ try {
120
+ for (const e of events) {
121
+ stmt.run(...eventValues(sessionId, e, now));
122
+ written++;
123
+ }
124
+ db.exec("COMMIT");
125
+ }
126
+ catch (err) {
127
+ db.exec("ROLLBACK");
128
+ throw err;
129
+ }
130
+ return written;
131
+ },
132
+ async query(sql, params = []) {
133
+ return db.prepare(sql).all(...params);
134
+ },
135
+ async exec(sql, params = []) {
136
+ db.prepare(sql).run(...params);
137
+ },
138
+ };
139
+ }
140
+ // ── Postgres backend ─────────────────────────────────────────────────────────
141
+ function openPostgres(url) {
142
+ // Lazy require so the SQLite-only dev path never loads `pg`. Importing here
143
+ // (not at module top) also keeps the Edge bundler from tracing it needlessly.
144
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
145
+ const { Pool, types } = require("pg");
146
+ // pg returns BIGINT (int8, OID 20) and NUMERIC (OID 1700) as strings to avoid
147
+ // precision loss. Our ids/counts/averages are all well within Number range,
148
+ // and the dashboard expects numbers, so parse them eagerly for this pool.
149
+ types.setTypeParser(20, (v) => (v === null ? null : parseInt(v, 10)));
150
+ types.setTypeParser(1700, (v) => (v === null ? null : parseFloat(v)));
151
+ const pool = new Pool({
152
+ connectionString: url,
153
+ // Serverless invocations are short-lived and each opens its own pool, so
154
+ // keep it tiny and let idle connections drop quickly. Use the *pooled*
155
+ // (pgbouncer) connection string in production — see DATABASE.md.
156
+ max: 3,
157
+ idleTimeoutMillis: 10_000,
158
+ // Hosted Postgres (Neon, Supabase, RDS) requires TLS; most managed
159
+ // certificates aren't in the default CA bundle, so don't reject them.
160
+ ssl: { rejectUnauthorized: false },
161
+ });
162
+ // Convert our portable `?` placeholders to Postgres' positional `$1, $2, …`.
163
+ const toPg = (sql) => {
164
+ let i = 0;
165
+ return sql.replace(/\?/g, () => `$${++i}`);
166
+ };
167
+ const ready = pool.query(`
168
+ CREATE TABLE IF NOT EXISTS events (
169
+ id BIGSERIAL PRIMARY KEY,
170
+ session_id TEXT NOT NULL,
171
+ type TEXT NOT NULL,
172
+ ts BIGINT NOT NULL,
173
+ path TEXT NOT NULL,
174
+ selector TEXT,
175
+ component TEXT,
176
+ text TEXT,
177
+ value DOUBLE PRECISION,
178
+ meta JSONB,
179
+ received_at BIGINT NOT NULL
180
+ );
181
+ CREATE INDEX IF NOT EXISTS idx_events_path ON events(path);
182
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
183
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
184
+
185
+ CREATE TABLE IF NOT EXISTS polishd_meta (
186
+ key TEXT PRIMARY KEY,
187
+ value TEXT NOT NULL,
188
+ updated_at BIGINT NOT NULL
189
+ );
190
+ `).then(async () => {
191
+ await migrateLegacyMetaAsync((sql) => pool.query(sql).then(() => undefined), `INSERT INTO polishd_meta (key, value, updated_at)
192
+ SELECT key, value, updated_at FROM buffd_meta
193
+ ON CONFLICT (key) DO NOTHING`);
194
+ });
195
+ return {
196
+ async insert(sessionId, events) {
197
+ if (events.length === 0)
198
+ return 0;
199
+ await ready;
200
+ const now = Date.now();
201
+ const cols = 10; // must match INSERT_COLS / eventValues
202
+ const params = [];
203
+ const tuples = [];
204
+ events.forEach((e, row) => {
205
+ const base = row * cols;
206
+ // meta is the 9th column (index 8) — cast its text param to jsonb.
207
+ const ph = Array.from({ length: cols }, (_, c) => c === 8 ? `$${base + c + 1}::jsonb` : `$${base + c + 1}`);
208
+ tuples.push(`(${ph.join(", ")})`);
209
+ params.push(...eventValues(sessionId, e, now));
210
+ });
211
+ const res = await pool.query(`INSERT INTO events (${INSERT_COLS}) VALUES ${tuples.join(", ")}`, params);
212
+ return res.rowCount ?? 0;
213
+ },
214
+ async query(sql, params = []) {
215
+ await ready;
216
+ const res = await pool.query(toPg(sql), params);
217
+ return res.rows;
218
+ },
219
+ async exec(sql, params = []) {
220
+ await ready;
221
+ await pool.query(toPg(sql), params);
222
+ },
223
+ };
224
+ }
225
+ // ── Backend selection (lazy, cached, fault-tolerant) ─────────────────────────
226
+ /** undefined = not yet attempted; null = failed, stay no-op; Backend = ready. */
227
+ let backend;
228
+ /** Shared init promise so concurrent callers don't race to open the backend. */
229
+ let initPromise = null;
230
+ async function getBackend() {
231
+ if (backend !== undefined)
232
+ return backend;
233
+ if (initPromise)
234
+ return initPromise;
235
+ initPromise = (async () => {
236
+ try {
237
+ // BUFFD_/POLISH_ fallbacks keep pre-rename deployments working.
238
+ const url = process.env.POLISHD_DATABASE_URL ||
239
+ process.env.BUFFD_DATABASE_URL ||
240
+ process.env.POLISH_DATABASE_URL;
241
+ if (url) {
242
+ const pg = openPostgres(url);
243
+ // Force the lazy connection now so a bad URL latches to no-op loudly,
244
+ // instead of silently failing on the first capture in production.
245
+ await pg.query("SELECT 1");
246
+ backend = pg;
247
+ }
248
+ else {
249
+ backend = await openSqlite();
250
+ }
251
+ }
252
+ catch (err) {
253
+ console.warn("[polishd] analytics store unavailable, capture disabled:", err instanceof Error ? err.message : err);
254
+ backend = null;
255
+ }
256
+ return backend;
257
+ })();
258
+ return initPromise;
259
+ }
260
+ /** True when events are actually being persisted (used by the dashboard). */
261
+ export async function storeReady() {
262
+ return (await getBackend()) !== null;
263
+ }
264
+ /** Persist a batch of events for one session. Silently drops if no store. */
265
+ export async function insertEvents(sessionId, events) {
266
+ const b = await getBackend();
267
+ if (!b || events.length === 0)
268
+ return 0;
269
+ try {
270
+ return await b.insert(sessionId, events);
271
+ }
272
+ catch (err) {
273
+ console.warn("[polishd] insert failed, dropping batch:", err instanceof Error ? err.message : err);
274
+ return 0;
275
+ }
276
+ }
277
+ /**
278
+ * Run a portable read query, or return `[]` in no-op mode. Use `?` placeholders;
279
+ * each backend rewrites them to its own dialect. The aggregation layer (and the
280
+ * dashboard) goes through here so it never has to know which backend is live.
281
+ */
282
+ export async function query(sql, params = []) {
283
+ const b = await getBackend();
284
+ if (!b)
285
+ return [];
286
+ try {
287
+ return await b.query(sql, params);
288
+ }
289
+ catch (err) {
290
+ console.warn("[polishd] query failed:", err instanceof Error ? err.message : err);
291
+ return [];
292
+ }
293
+ }
294
+ // ── Key/value metadata (polishd_meta) ──────────────────────────────────────────
295
+ // A tiny single-row-per-key store for things that aren't events: the AI
296
+ // settings the dashboard owner configures, and the last generated AI summary
297
+ // (cached so the dashboard renders it without spending tokens). Returns
298
+ // null / no-ops when the store is unavailable, like the rest of this module.
299
+ /** Read one metadata value by key, or null when absent / no store. */
300
+ export async function getMeta(key) {
301
+ const rows = await query(`SELECT value FROM polishd_meta WHERE key = ?`, [key]);
302
+ const v = rows[0]?.value;
303
+ return typeof v === "string" ? v : null;
304
+ }
305
+ /** Upsert one metadata value. Returns false when the store is unavailable. */
306
+ export async function setMeta(key, value) {
307
+ const b = await getBackend();
308
+ if (!b)
309
+ return false;
310
+ try {
311
+ await b.exec(`INSERT INTO polishd_meta (key, value, updated_at) VALUES (?, ?, ?)
312
+ ON CONFLICT (key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`, [key, value, Date.now()]);
313
+ return true;
314
+ }
315
+ catch (err) {
316
+ console.warn("[polishd] setMeta failed:", err instanceof Error ? err.message : err);
317
+ return false;
318
+ }
319
+ }
320
+ /** Raw rows, newest first. For debugging and the queries layer. */
321
+ export async function allRows(limit = 1000) {
322
+ const rows = await query(`SELECT * FROM events ORDER BY id DESC LIMIT ?`, [limit]);
323
+ return rows.map(deserialize);
324
+ }
325
+ function deserialize(row) {
326
+ return {
327
+ id: Number(row.id),
328
+ session_id: row.session_id,
329
+ type: row.type,
330
+ ts: Number(row.ts),
331
+ path: row.path,
332
+ selector: row.selector ?? undefined,
333
+ component: row.component ?? undefined,
334
+ text: row.text ?? undefined,
335
+ value: row.value == null ? undefined : Number(row.value),
336
+ meta: parseMeta(row.meta),
337
+ received_at: Number(row.received_at),
338
+ };
339
+ }
340
+ /**
341
+ * Normalize the `meta` column across backends: SQLite stores it as a JSON
342
+ * string, Postgres' JSONB comes back already parsed. Accept either.
343
+ */
344
+ export function parseMeta(raw) {
345
+ if (raw == null)
346
+ return undefined;
347
+ if (typeof raw === "object")
348
+ return raw;
349
+ if (typeof raw === "string") {
350
+ try {
351
+ return JSON.parse(raw);
352
+ }
353
+ catch {
354
+ return undefined;
355
+ }
356
+ }
357
+ return undefined;
358
+ }
359
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @polishd/next/server — storage, ingest, and read-side queries.
3
+ *
4
+ * Node runtime only (uses `node:sqlite` in dev, `pg` in production). Never
5
+ * import this from a client component — it will pull database drivers into the
6
+ * browser bundle.
7
+ */
8
+ export * from "./server/store";
9
+ export * from "./server/ingest";
10
+ export * from "./server/queries";
11
+ export { withPolishdSession } from "./session";
12
+ export { generateSummary, loadSummary, loadSummaryState, getAISettingsPublic, saveAISettings, } from "./ai/summary";
13
+ export { generateProjectProfile, loadProjectProfile, loadProfileState, } from "./ai/profile";
14
+ export type { PolishdAIProvider, PolishdAISettings, PolishdAISettingsPublic, PolishdProjectProfile, PolishdSummary, GenerateProfileResult, GenerateSummaryResult, } from "./ai/types";
15
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAG/C,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @polishd/next/server — storage, ingest, and read-side queries.
3
+ *
4
+ * Node runtime only (uses `node:sqlite` in dev, `pg` in production). Never
5
+ * import this from a client component — it will pull database drivers into the
6
+ * browser bundle.
7
+ */
8
+ export * from "./server/store";
9
+ export * from "./server/ingest";
10
+ export * from "./server/queries";
11
+ export { withPolishdSession } from "./session";
12
+ // AI summary layer (server-only — calls model providers).
13
+ export { generateSummary, loadSummary, loadSummaryState, getAISettingsPublic, saveAISettings, } from "./ai/summary";
14
+ export { generateProjectProfile, loadProjectProfile, loadProfileState, } from "./ai/profile";
15
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,0DAA0D;AAC1D,OAAO,EACL,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Polishd — anonymous session assignment (Edge-safe helper).
3
+ *
4
+ * Runs at the Edge before render (from the host app's `proxy.ts`). Its only job
5
+ * is to guarantee every visitor carries an anonymous `polishd_session` cookie.
6
+ * The id is a random UUID — no fingerprinting, no PII — so it is GDPR-safe by
7
+ * construction. The ingest layer trusts this cookie as the sole source of
8
+ * session identity.
9
+ *
10
+ * Kept framework-light: takes and returns a NextResponse so the host app can
11
+ * compose it with any other proxy logic it runs.
12
+ */
13
+ import { NextResponse, type NextRequest } from "next/server";
14
+ import { type PolishdConfig } from "./config";
15
+ export declare function withPolishdSession(request: NextRequest, response?: NextResponse, config?: Partial<PolishdConfig>): NextResponse;
16
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAIpE,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EACpB,QAAQ,GAAE,YAAkC,EAC5C,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,YAAY,CAYd"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Polishd — anonymous session assignment (Edge-safe helper).
3
+ *
4
+ * Runs at the Edge before render (from the host app's `proxy.ts`). Its only job
5
+ * is to guarantee every visitor carries an anonymous `polishd_session` cookie.
6
+ * The id is a random UUID — no fingerprinting, no PII — so it is GDPR-safe by
7
+ * construction. The ingest layer trusts this cookie as the sole source of
8
+ * session identity.
9
+ *
10
+ * Kept framework-light: takes and returns a NextResponse so the host app can
11
+ * compose it with any other proxy logic it runs.
12
+ */
13
+ import { NextResponse } from "next/server";
14
+ import { defaultPolishdConfig } from "./config";
15
+ const ONE_YEAR = 60 * 60 * 24 * 365;
16
+ export function withPolishdSession(request, response = NextResponse.next(), config = {}) {
17
+ const cookieName = config.sessionCookie ?? defaultPolishdConfig.sessionCookie;
18
+ if (request.cookies.get(cookieName))
19
+ return response;
20
+ response.cookies.set(cookieName, crypto.randomUUID(), {
21
+ httpOnly: true, // never exposed to JS — capture sends no session id itself
22
+ sameSite: "lax",
23
+ secure: process.env.NODE_ENV === "production",
24
+ path: "/",
25
+ maxAge: ONE_YEAR,
26
+ });
27
+ return response;
28
+ }
29
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,YAAY,EAAoB,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAsB,MAAM,UAAU,CAAC;AAEpE,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAEpC,MAAM,UAAU,kBAAkB,CAChC,OAAoB,EACpB,WAAyB,YAAY,CAAC,IAAI,EAAE,EAC5C,SAAiC,EAAE;IAEnC,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,IAAI,oBAAoB,CAAC,aAAa,CAAC;IAC9E,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,QAAQ,CAAC;IAErD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE;QACpD,QAAQ,EAAE,IAAI,EAAE,2DAA2D;QAC3E,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC7C,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Polishd — shared event schema.
3
+ *
4
+ * This module is the single source of truth for the shape of every behavioral
5
+ * signal Polishd captures. It is imported by the client capture layer, the
6
+ * ingest route, and the dashboard queries, so it must stay dependency-free and
7
+ * runtime-agnostic (no Node, no DOM types beyond primitives).
8
+ *
9
+ * Designed for clean extraction into `@polishd/next` — nothing here knows about
10
+ * the host notes app.
11
+ */
12
+ /** Every kind of signal we capture. Ordered roughly by synthesis value. */
13
+ export type PolishdEventType = "page_view" | "click" | "rage_click" | "dead_click" | "scroll_depth" | "viewport" | "js_error" | "web_vital" | "hover" | "component_view" | "mount" | "session_end";
14
+ /**
15
+ * A single captured event. The client sends these without a session id; the
16
+ * ingest layer attaches `session_id` server-side from the signed cookie, so a
17
+ * client can never spoof another session.
18
+ */
19
+ export interface PolishdEvent {
20
+ type: PolishdEventType;
21
+ /** Client clock, ms since epoch. Used for ordering and rage-click windows. */
22
+ ts: number;
23
+ /** Pathname the event occurred on, e.g. "/posts/my-note". No query string. */
24
+ path: string;
25
+ /** Best-effort CSS selector path to the target element. */
26
+ selector?: string;
27
+ /** `data-component` hint, walked up from the target. Key to synthesis. */
28
+ component?: string;
29
+ /** Short, truncated element text — labels only, never user-typed content. */
30
+ text?: string;
31
+ /**
32
+ * Numeric payload whose meaning depends on `type`:
33
+ * - scroll_depth: percent 0–100
34
+ * - web_vital: the metric value (ms, or unitless for CLS)
35
+ * - viewport: viewport width in CSS pixels
36
+ */
37
+ value?: number;
38
+ /** Small, typed extras keyed by event type (e.g. vital name, error message). */
39
+ meta?: Record<string, string | number | boolean | null>;
40
+ }
41
+ /** What the client POSTs to the ingest route. */
42
+ export interface PolishdIngestBody {
43
+ events: PolishdEvent[];
44
+ /** Page that produced this batch, for sanity/debugging. */
45
+ page?: string;
46
+ }
47
+ /** A stored event row, as it comes back from the database. */
48
+ export interface PolishdEventRow extends PolishdEvent {
49
+ id: number;
50
+ session_id: string;
51
+ /** Server receive time, ms since epoch — authoritative for retention. */
52
+ received_at: number;
53
+ }
54
+ /** The set of event types the client is allowed to send. */
55
+ export declare const CLIENT_EVENT_TYPES: ReadonlySet<PolishdEventType>;
56
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,OAAO,GACP,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,UAAU,GACV,UAAU,GACV,WAAW,GACX,OAAO,GACP,gBAAgB,GAChB,OAAO,GACP,aAAa,CAAC;AAElB;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,8EAA8E;IAC9E,EAAE,EAAE,MAAM,CAAC;IACX,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;CACzD;AAED,iDAAiD;AACjD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,gBAAgB,CAa3D,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Polishd — shared event schema.
3
+ *
4
+ * This module is the single source of truth for the shape of every behavioral
5
+ * signal Polishd captures. It is imported by the client capture layer, the
6
+ * ingest route, and the dashboard queries, so it must stay dependency-free and
7
+ * runtime-agnostic (no Node, no DOM types beyond primitives).
8
+ *
9
+ * Designed for clean extraction into `@polishd/next` — nothing here knows about
10
+ * the host notes app.
11
+ */
12
+ /** The set of event types the client is allowed to send. */
13
+ export const CLIENT_EVENT_TYPES = new Set([
14
+ "page_view",
15
+ "click",
16
+ "rage_click",
17
+ "dead_click",
18
+ "scroll_depth",
19
+ "viewport",
20
+ "js_error",
21
+ "web_vital",
22
+ "hover",
23
+ "component_view",
24
+ "mount",
25
+ "session_end",
26
+ ]);
27
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4DH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAkC,IAAI,GAAG,CAAmB;IACzF,WAAW;IACX,OAAO;IACP,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,UAAU;IACV,UAAU;IACV,WAAW;IACX,OAAO;IACP,gBAAgB;IAChB,OAAO;IACP,aAAa;CACd,CAAC,CAAC"}