@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.
- package/AGENTS.md +262 -0
- package/DATABASE.md +147 -0
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/bin/init.mjs +224 -0
- package/dist/ai/actions.d.ts +23 -0
- package/dist/ai/actions.d.ts.map +1 -0
- package/dist/ai/actions.js +58 -0
- package/dist/ai/actions.js.map +1 -0
- package/dist/ai/digest.d.ts +9 -0
- package/dist/ai/digest.d.ts.map +1 -0
- package/dist/ai/digest.js +108 -0
- package/dist/ai/digest.js.map +1 -0
- package/dist/ai/github.d.ts +51 -0
- package/dist/ai/github.d.ts.map +1 -0
- package/dist/ai/github.js +175 -0
- package/dist/ai/github.js.map +1 -0
- package/dist/ai/guard.d.ts +43 -0
- package/dist/ai/guard.d.ts.map +1 -0
- package/dist/ai/guard.js +57 -0
- package/dist/ai/guard.js.map +1 -0
- package/dist/ai/issues.d.ts +21 -0
- package/dist/ai/issues.d.ts.map +1 -0
- package/dist/ai/issues.js +291 -0
- package/dist/ai/issues.js.map +1 -0
- package/dist/ai/profile.d.ts +26 -0
- package/dist/ai/profile.d.ts.map +1 -0
- package/dist/ai/profile.js +160 -0
- package/dist/ai/profile.js.map +1 -0
- package/dist/ai/providers.d.ts +40 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +142 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/scan.d.ts +38 -0
- package/dist/ai/scan.d.ts.map +1 -0
- package/dist/ai/scan.js +274 -0
- package/dist/ai/scan.js.map +1 -0
- package/dist/ai/settings.d.ts +31 -0
- package/dist/ai/settings.d.ts.map +1 -0
- package/dist/ai/settings.js +182 -0
- package/dist/ai/settings.js.map +1 -0
- package/dist/ai/summary.d.ts +26 -0
- package/dist/ai/summary.d.ts.map +1 -0
- package/dist/ai/summary.js +260 -0
- package/dist/ai/summary.js.map +1 -0
- package/dist/ai/types.d.ts +188 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +13 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/client/init.d.ts +14 -0
- package/dist/client/init.d.ts.map +1 -0
- package/dist/client/init.js +274 -0
- package/dist/client/init.js.map +1 -0
- package/dist/client/monitor.d.ts +23 -0
- package/dist/client/monitor.d.ts.map +1 -0
- package/dist/client/monitor.js +176 -0
- package/dist/client/monitor.js.map +1 -0
- package/dist/client.d.ts +11 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +10 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/elements.d.ts +6 -0
- package/dist/dashboard/elements.d.ts.map +1 -0
- package/dist/dashboard/elements.js +35 -0
- package/dist/dashboard/elements.js.map +1 -0
- package/dist/dashboard/features.d.ts +6 -0
- package/dist/dashboard/features.d.ts.map +1 -0
- package/dist/dashboard/features.js +28 -0
- package/dist/dashboard/features.js.map +1 -0
- package/dist/dashboard/index.d.ts +54 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +140 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/journeys.d.ts +5 -0
- package/dist/dashboard/journeys.d.ts.map +1 -0
- package/dist/dashboard/journeys.js +158 -0
- package/dist/dashboard/journeys.js.map +1 -0
- package/dist/dashboard/pages.d.ts +5 -0
- package/dist/dashboard/pages.d.ts.map +1 -0
- package/dist/dashboard/pages.js +104 -0
- package/dist/dashboard/pages.js.map +1 -0
- package/dist/dashboard/summary.d.ts +13 -0
- package/dist/dashboard/summary.d.ts.map +1 -0
- package/dist/dashboard/summary.js +333 -0
- package/dist/dashboard/summary.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +40 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +29 -0
- package/dist/proxy.js.map +1 -0
- package/dist/route.d.ts +24 -0
- package/dist/route.d.ts.map +1 -0
- package/dist/route.js +37 -0
- package/dist/route.js.map +1 -0
- package/dist/server/ingest.d.ts +14 -0
- package/dist/server/ingest.d.ts.map +1 -0
- package/dist/server/ingest.js +73 -0
- package/dist/server/ingest.js.map +1 -0
- package/dist/server/queries.d.ts +245 -0
- package/dist/server/queries.d.ts.map +1 -0
- package/dist/server/queries.js +547 -0
- package/dist/server/queries.js.map +1 -0
- package/dist/server/store.d.ts +29 -0
- package/dist/server/store.d.ts.map +1 -0
- package/dist/server/store.js +359 -0
- package/dist/server/store.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +16 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +29 -0
- package/dist/session.js.map +1 -0
- package/dist/shared/types.d.ts +56 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +27 -0
- package/dist/shared/types.js.map +1 -0
- package/docs/SETUP.md +315 -0
- 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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"}
|
package/dist/server.d.ts
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
|
+
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"}
|
package/dist/session.js
ADDED
|
@@ -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"}
|