@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,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polishd — AI summary types (shared between server and the dashboard UI).
|
|
3
|
+
*
|
|
4
|
+
* The AI layer reads the captured analytics, folds in an owner-supplied
|
|
5
|
+
* description of the site, and asks a model to narrate "how are people actually
|
|
6
|
+
* using this — what's working and what isn't" as one short paragraph.
|
|
7
|
+
*
|
|
8
|
+
* Bring-your-own-key: the owner picks a provider + model and supplies an API
|
|
9
|
+
* key. Keys live server-side only (in the polishd_meta store / env) and are never
|
|
10
|
+
* sent back to the browser — the client only ever sees whether one is set.
|
|
11
|
+
*/
|
|
12
|
+
/** Supported model vendors. `openai-compatible` covers OpenRouter, Groq, etc. */
|
|
13
|
+
export type PolishdAIProvider = "anthropic" | "openai" | "openai-compatible" | "google";
|
|
14
|
+
/**
|
|
15
|
+
* How often the summary refreshes itself. Evaluated when the dashboard loads
|
|
16
|
+
* (serverless-friendly — no external cron): if the cadence has elapsed AND the
|
|
17
|
+
* data actually changed, a regenerate runs in the background after the page is
|
|
18
|
+
* served. Unchanged data never costs tokens regardless of cadence.
|
|
19
|
+
*/
|
|
20
|
+
export type PolishdRefreshCadence = "manual" | "daily" | "weekly";
|
|
21
|
+
/** The full, server-side settings — includes the secret API key. */
|
|
22
|
+
export interface PolishdAISettings {
|
|
23
|
+
provider: PolishdAIProvider;
|
|
24
|
+
/** Model id, e.g. "claude-opus-4-8", "gpt-4o-mini", "gemini-1.5-flash". */
|
|
25
|
+
model: string;
|
|
26
|
+
/** Secret. Server-only — never serialized to the client. */
|
|
27
|
+
apiKey: string;
|
|
28
|
+
/** Base URL override (required for `openai-compatible`; ignored otherwise). */
|
|
29
|
+
baseUrl?: string;
|
|
30
|
+
/** Extra steering appended to the system prompt (tone, focus, length…). */
|
|
31
|
+
instructions?: string;
|
|
32
|
+
/** Owner's description of the site — what it is, who it's for, key flows. */
|
|
33
|
+
context?: string;
|
|
34
|
+
/** Who the product is for — fed into the project profile. */
|
|
35
|
+
audience?: string;
|
|
36
|
+
/** Product ideology / values / what success looks like — fed into the profile. */
|
|
37
|
+
ideology?: string;
|
|
38
|
+
/** Comma-separated source folders to scan (relative to project root). */
|
|
39
|
+
sourceDirs?: string;
|
|
40
|
+
/** Auto-refresh cadence for the summary. Defaults to "manual". */
|
|
41
|
+
refreshCadence?: PolishdRefreshCadence;
|
|
42
|
+
/** GitHub repository this site lives in, as "owner/repo". */
|
|
43
|
+
githubRepo?: string;
|
|
44
|
+
/** GitHub access token. Secret. Server-only — never serialized to the client. */
|
|
45
|
+
githubToken?: string;
|
|
46
|
+
/** File an issue automatically for each new problem a summary finds. */
|
|
47
|
+
githubAutoIssues?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Client-safe view of the settings: same shape, key replaced by a boolean. */
|
|
50
|
+
export interface PolishdAISettingsPublic {
|
|
51
|
+
provider: PolishdAIProvider;
|
|
52
|
+
model: string;
|
|
53
|
+
/** True when an API key is configured (via the dashboard or env). */
|
|
54
|
+
hasApiKey: boolean;
|
|
55
|
+
baseUrl?: string;
|
|
56
|
+
instructions?: string;
|
|
57
|
+
context?: string;
|
|
58
|
+
audience?: string;
|
|
59
|
+
ideology?: string;
|
|
60
|
+
sourceDirs?: string;
|
|
61
|
+
refreshCadence?: PolishdRefreshCadence;
|
|
62
|
+
/** GitHub repository ("owner/repo"), when connected. */
|
|
63
|
+
githubRepo?: string;
|
|
64
|
+
/** True when a GitHub token is configured (via the dashboard or env). */
|
|
65
|
+
hasGithubToken: boolean;
|
|
66
|
+
/** File an issue automatically for each new problem a summary finds. */
|
|
67
|
+
githubAutoIssues?: boolean;
|
|
68
|
+
/** True when provider settings came from env vars (read-only defaults). */
|
|
69
|
+
fromEnv: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A one-time, model-written understanding of the codebase: its purpose, the
|
|
73
|
+
* key components and interactive elements, and (optionally) the owner's stated
|
|
74
|
+
* audience and ideology. Cached and reused as authoritative context for every
|
|
75
|
+
* summary, so the per-summary call never needs to read source again — only an
|
|
76
|
+
* explicit re-scan does.
|
|
77
|
+
*/
|
|
78
|
+
export interface PolishdProjectProfile {
|
|
79
|
+
/** The profile prose. */
|
|
80
|
+
text: string;
|
|
81
|
+
provider: PolishdAIProvider;
|
|
82
|
+
model: string;
|
|
83
|
+
generatedAt: number;
|
|
84
|
+
/** Hash of the scanned source (paths + sizes) — detects source drift. */
|
|
85
|
+
fingerprint: string;
|
|
86
|
+
/** Component/element identifiers the profile explicitly covers. */
|
|
87
|
+
coveredIdentifiers: string[];
|
|
88
|
+
/** How many source files went into the scan, and whether it was capped. */
|
|
89
|
+
sourceFiles: number;
|
|
90
|
+
truncated: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** Result of a profile scan attempt. */
|
|
93
|
+
export type GenerateProfileResult = {
|
|
94
|
+
ok: true;
|
|
95
|
+
profile: PolishdProjectProfile;
|
|
96
|
+
} | {
|
|
97
|
+
ok: false;
|
|
98
|
+
error: GenerateProfileError;
|
|
99
|
+
message: string;
|
|
100
|
+
};
|
|
101
|
+
export type GenerateProfileError = "no-key" | "no-source" | "provider-error";
|
|
102
|
+
/**
|
|
103
|
+
* A specific problem the model found in the data. Losses are held to a higher
|
|
104
|
+
* bar than wins: each must cite evidence (a selector, component name, or page
|
|
105
|
+
* path) that appears in the digest we sent — uncited losses are dropped as
|
|
106
|
+
* hallucinations — and the server then tries to locate the cited element in
|
|
107
|
+
* the codebase (`location`/`verified`).
|
|
108
|
+
*/
|
|
109
|
+
export interface PolishdLossItem {
|
|
110
|
+
/** The specific problem, in plain English. */
|
|
111
|
+
issue: string;
|
|
112
|
+
/** The identifier from the data that shows it (selector / component / path). */
|
|
113
|
+
evidence: string;
|
|
114
|
+
/** Source file containing the cited element, when found on disk. */
|
|
115
|
+
location?: string;
|
|
116
|
+
/** True when the citation was matched to a file in the codebase. */
|
|
117
|
+
verified: boolean;
|
|
118
|
+
/** GitHub issue filed for this problem (manually or automatically). */
|
|
119
|
+
issueUrl?: string;
|
|
120
|
+
issueNumber?: number;
|
|
121
|
+
}
|
|
122
|
+
/** A generated narrative summary, cached in the store. */
|
|
123
|
+
export interface PolishdSummary {
|
|
124
|
+
/** The paragraph(s) of narrative. */
|
|
125
|
+
text: string;
|
|
126
|
+
/** What's working — may be general. Absent on summaries from older versions. */
|
|
127
|
+
wins?: string[];
|
|
128
|
+
/** Specific, evidence-cited problems. Absent on summaries from older versions. */
|
|
129
|
+
losses?: PolishdLossItem[];
|
|
130
|
+
/** Provider + model that produced it. */
|
|
131
|
+
provider: PolishdAIProvider;
|
|
132
|
+
model: string;
|
|
133
|
+
/** When it was generated, ms since epoch. */
|
|
134
|
+
generatedAt: number;
|
|
135
|
+
/** Hash of the digest it was generated from — drives the "stale" check. */
|
|
136
|
+
fingerprint: string;
|
|
137
|
+
/** Token usage, when the provider reported it. */
|
|
138
|
+
usage?: {
|
|
139
|
+
inputTokens?: number;
|
|
140
|
+
outputTokens?: number;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/** Result of a generate attempt, returned to the dashboard. */
|
|
144
|
+
export type GenerateSummaryResult = {
|
|
145
|
+
ok: true;
|
|
146
|
+
summary: PolishdSummary;
|
|
147
|
+
regenerated: boolean;
|
|
148
|
+
} | {
|
|
149
|
+
ok: false;
|
|
150
|
+
error: GenerateSummaryError;
|
|
151
|
+
message: string;
|
|
152
|
+
};
|
|
153
|
+
export type GenerateSummaryError = "no-key" | "no-data" | "provider-error";
|
|
154
|
+
/**
|
|
155
|
+
* What the configured token can actually do against the configured repo,
|
|
156
|
+
* checked live during onboarding. `contents` (read) powers source reading;
|
|
157
|
+
* `issues` and `pullRequests` (write) power filing bugs and opening PRs.
|
|
158
|
+
*/
|
|
159
|
+
export interface PolishdGithubStatus {
|
|
160
|
+
/** The repo as GitHub reports it, e.g. "muskanvaswan/create". */
|
|
161
|
+
repo: string;
|
|
162
|
+
defaultBranch: string;
|
|
163
|
+
/** True when the token can push (create branches / PRs). */
|
|
164
|
+
canPush: boolean;
|
|
165
|
+
/** True when the repo has issues enabled. */
|
|
166
|
+
issuesEnabled: boolean;
|
|
167
|
+
}
|
|
168
|
+
/** Result of verifying the GitHub connection. */
|
|
169
|
+
export type VerifyGithubResult = {
|
|
170
|
+
ok: true;
|
|
171
|
+
status: PolishdGithubStatus;
|
|
172
|
+
} | {
|
|
173
|
+
ok: false;
|
|
174
|
+
error: GithubError;
|
|
175
|
+
message: string;
|
|
176
|
+
};
|
|
177
|
+
/** Result of creating an issue from a loss item. */
|
|
178
|
+
export type CreateIssueResult = {
|
|
179
|
+
ok: true;
|
|
180
|
+
url: string;
|
|
181
|
+
number: number;
|
|
182
|
+
} | {
|
|
183
|
+
ok: false;
|
|
184
|
+
error: GithubError;
|
|
185
|
+
message: string;
|
|
186
|
+
};
|
|
187
|
+
export type GithubError = "not-connected" | "not-a-bug" | "github-error";
|
|
188
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ai/types.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polishd — AI summary types (shared between server and the dashboard UI).
|
|
3
|
+
*
|
|
4
|
+
* The AI layer reads the captured analytics, folds in an owner-supplied
|
|
5
|
+
* description of the site, and asks a model to narrate "how are people actually
|
|
6
|
+
* using this — what's working and what isn't" as one short paragraph.
|
|
7
|
+
*
|
|
8
|
+
* Bring-your-own-key: the owner picks a provider + model and supplies an API
|
|
9
|
+
* key. Keys live server-side only (in the polishd_meta store / env) and are never
|
|
10
|
+
* sent back to the browser — the client only ever sees whether one is set.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/ai/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polishd — client capture layer.
|
|
3
|
+
*
|
|
4
|
+
* Vanilla DOM, no framework imports, so it drops cleanly into Next's
|
|
5
|
+
* `instrumentation-client.ts` (runs before hydration) and is trivially
|
|
6
|
+
* extractable into `@polishd/next`. It attaches global listeners, derives
|
|
7
|
+
* behavioral signals (rage/dead clicks, scroll depth, web vitals), batches
|
|
8
|
+
* events, and flushes on an interval, on soft navigation, and on pagehide.
|
|
9
|
+
*/
|
|
10
|
+
import { type PolishdConfig } from "../config";
|
|
11
|
+
type InitOptions = Partial<PolishdConfig>;
|
|
12
|
+
export declare function initPolishd(options?: InitOptions): void;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/client/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAGrE,KAAK,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAI1C,wBAAgB,WAAW,CAAC,OAAO,GAAE,WAAgB,GAAG,IAAI,CAiR3D"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polishd — client capture layer.
|
|
3
|
+
*
|
|
4
|
+
* Vanilla DOM, no framework imports, so it drops cleanly into Next's
|
|
5
|
+
* `instrumentation-client.ts` (runs before hydration) and is trivially
|
|
6
|
+
* extractable into `@polishd/next`. It attaches global listeners, derives
|
|
7
|
+
* behavioral signals (rage/dead clicks, scroll depth, web vitals), batches
|
|
8
|
+
* events, and flushes on an interval, on soft navigation, and on pagehide.
|
|
9
|
+
*/
|
|
10
|
+
import { defaultPolishdConfig } from "../config";
|
|
11
|
+
let started = false;
|
|
12
|
+
export function initPolishd(options = {}) {
|
|
13
|
+
// Guard: client-only, run once, respect enable flag and Do Not Track.
|
|
14
|
+
if (typeof window === "undefined" || started)
|
|
15
|
+
return;
|
|
16
|
+
const cfg = { ...defaultPolishdConfig, ...options };
|
|
17
|
+
if (!cfg.enabled)
|
|
18
|
+
return;
|
|
19
|
+
if (navigator.doNotTrack === "1")
|
|
20
|
+
return;
|
|
21
|
+
// Per-session sampling decision, stable for the page's lifetime.
|
|
22
|
+
if (cfg.sampleRate < 1 && Math.random() > cfg.sampleRate)
|
|
23
|
+
return;
|
|
24
|
+
started = true;
|
|
25
|
+
const queue = [];
|
|
26
|
+
let currentPath = location.pathname;
|
|
27
|
+
let maxScrollPct = 0;
|
|
28
|
+
const push = (e) => {
|
|
29
|
+
queue.push({ ts: Date.now(), path: currentPath, ...e });
|
|
30
|
+
if (queue.length >= cfg.maxBatchSize)
|
|
31
|
+
flush();
|
|
32
|
+
};
|
|
33
|
+
// Expose a track function so TrackingWrapper components can emit explicit
|
|
34
|
+
// events (e.g. hover) without coupling to React internals.
|
|
35
|
+
window.__polishdTrack = push;
|
|
36
|
+
// ---- transport -----------------------------------------------------------
|
|
37
|
+
const flush = (useBeacon = false) => {
|
|
38
|
+
if (queue.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
const batch = queue.splice(0, queue.length);
|
|
41
|
+
const body = JSON.stringify({ events: batch, page: currentPath });
|
|
42
|
+
// sendBeacon survives unload; fetch is used for periodic flushes.
|
|
43
|
+
if (useBeacon && navigator.sendBeacon) {
|
|
44
|
+
const ok = navigator.sendBeacon(cfg.apiRoute, new Blob([body], { type: "application/json" }));
|
|
45
|
+
if (!ok)
|
|
46
|
+
queue.unshift(...batch); // re-queue on beacon rejection
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
fetch(cfg.apiRoute, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "Content-Type": "application/json" },
|
|
52
|
+
body,
|
|
53
|
+
keepalive: true,
|
|
54
|
+
}).catch(() => {
|
|
55
|
+
// Network hiccup: put events back so the next flush retries them.
|
|
56
|
+
queue.unshift(...batch);
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
// ---- DOM helpers ---------------------------------------------------------
|
|
60
|
+
const INTERACTIVE = new Set(["A", "BUTTON", "INPUT", "SELECT", "TEXTAREA", "LABEL", "SUMMARY"]);
|
|
61
|
+
const isInteractive = (el) => {
|
|
62
|
+
let node = el;
|
|
63
|
+
for (let depth = 0; node && depth < 4; depth++) {
|
|
64
|
+
if (INTERACTIVE.has(node.tagName))
|
|
65
|
+
return true;
|
|
66
|
+
const role = node.getAttribute("role");
|
|
67
|
+
if (role && /button|link|menuitem|tab|checkbox|radio|switch/.test(role))
|
|
68
|
+
return true;
|
|
69
|
+
if (node.hasAttribute("onclick") || node.isContentEditable)
|
|
70
|
+
return true;
|
|
71
|
+
node = node.parentElement;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
/** True when the click coincided with the user highlighting text. */
|
|
76
|
+
const hasTextSelection = () => {
|
|
77
|
+
const sel = typeof window.getSelection === "function" ? window.getSelection() : null;
|
|
78
|
+
return !!sel && !sel.isCollapsed && sel.toString().trim().length > 0;
|
|
79
|
+
};
|
|
80
|
+
/** Walk up for the nearest `data-component`, the key synthesis signal. */
|
|
81
|
+
const componentOf = (el) => {
|
|
82
|
+
let node = el;
|
|
83
|
+
for (let depth = 0; node && depth < 8; depth++) {
|
|
84
|
+
const c = node.getAttribute("data-component");
|
|
85
|
+
if (c)
|
|
86
|
+
return c;
|
|
87
|
+
node = node.parentElement;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
/** Compact, stable-ish selector path (tag + id + first class), capped. */
|
|
92
|
+
const selectorOf = (el) => {
|
|
93
|
+
if (!el)
|
|
94
|
+
return undefined;
|
|
95
|
+
const parts = [];
|
|
96
|
+
let node = el;
|
|
97
|
+
for (let depth = 0; node && depth < 4 && node.tagName !== "BODY"; depth++) {
|
|
98
|
+
let part = node.tagName.toLowerCase();
|
|
99
|
+
if (node.id) {
|
|
100
|
+
parts.unshift(`${part}#${node.id}`);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
const cls = (node.getAttribute("class") || "").trim().split(/\s+/)[0];
|
|
104
|
+
if (cls)
|
|
105
|
+
part += `.${cls}`;
|
|
106
|
+
parts.unshift(part);
|
|
107
|
+
node = node.parentElement;
|
|
108
|
+
}
|
|
109
|
+
return parts.join(">");
|
|
110
|
+
};
|
|
111
|
+
const labelOf = (el) => {
|
|
112
|
+
const t = el?.innerText || el?.textContent || "";
|
|
113
|
+
const trimmed = t.replace(/\s+/g, " ").trim();
|
|
114
|
+
return trimmed ? trimmed.slice(0, 80) : undefined;
|
|
115
|
+
};
|
|
116
|
+
// ---- click + rage + dead -------------------------------------------------
|
|
117
|
+
let lastClick = { selector: "", time: 0, count: 0 };
|
|
118
|
+
const onClick = (ev) => {
|
|
119
|
+
const target = ev.target;
|
|
120
|
+
if (!target)
|
|
121
|
+
return;
|
|
122
|
+
const selector = selectorOf(target);
|
|
123
|
+
const component = componentOf(target);
|
|
124
|
+
const text = labelOf(target);
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
// Rage: same selector clicked `count` times inside `windowMs`.
|
|
127
|
+
if (selector === lastClick.selector && now - lastClick.time < cfg.rageClick.windowMs) {
|
|
128
|
+
lastClick.count++;
|
|
129
|
+
lastClick.time = now;
|
|
130
|
+
if (lastClick.count === cfg.rageClick.count) {
|
|
131
|
+
push({ type: "rage_click", selector, component, text });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
lastClick = { selector: selector ?? "", time: now, count: 1 };
|
|
136
|
+
}
|
|
137
|
+
// Dead: click that hits nothing interactive (likely confusion).
|
|
138
|
+
if (!isInteractive(target)) {
|
|
139
|
+
// A drag-to-highlight fires a click on the (non-interactive) text node on
|
|
140
|
+
// mouse-up. That's intent, not confusion — don't log it as a dead click.
|
|
141
|
+
if (hasTextSelection())
|
|
142
|
+
return;
|
|
143
|
+
push({ type: "dead_click", selector, component, text });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
push({ type: "click", selector, component, text });
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
// ---- page views (incl. soft navigation) ----------------------------------
|
|
150
|
+
const emitPageView = () => {
|
|
151
|
+
// Flush the previous page's scroll depth before switching context.
|
|
152
|
+
if (maxScrollPct > 0)
|
|
153
|
+
push({ type: "scroll_depth", value: maxScrollPct });
|
|
154
|
+
currentPath = location.pathname;
|
|
155
|
+
maxScrollPct = 0;
|
|
156
|
+
push({ type: "page_view", path: currentPath });
|
|
157
|
+
};
|
|
158
|
+
// Patch History API so Next.js soft navigations emit page views.
|
|
159
|
+
const wrapHistory = (method) => {
|
|
160
|
+
const original = history[method];
|
|
161
|
+
history[method] = function (...args) {
|
|
162
|
+
const ret = original.apply(this, args);
|
|
163
|
+
if (location.pathname !== currentPath)
|
|
164
|
+
emitPageView();
|
|
165
|
+
return ret;
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
// ---- viewport / device size ----------------------------------------------
|
|
169
|
+
// Coarse device buckets keyed off CSS-pixel width. Aligns with common
|
|
170
|
+
// breakpoints (Tailwind sm/lg) so categories read intuitively on the
|
|
171
|
+
// dashboard. The category is stored in `text` (a plain, portable column) so
|
|
172
|
+
// it can be grouped without per-backend JSON querying.
|
|
173
|
+
const deviceCategory = (w) => w < 640 ? "mobile" : w < 1024 ? "tablet" : "desktop";
|
|
174
|
+
const emitViewport = () => {
|
|
175
|
+
const w = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
176
|
+
const h = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
177
|
+
if (w <= 0)
|
|
178
|
+
return;
|
|
179
|
+
push({
|
|
180
|
+
type: "viewport",
|
|
181
|
+
value: w,
|
|
182
|
+
text: deviceCategory(w),
|
|
183
|
+
meta: { w, h, dpr: Math.round((window.devicePixelRatio || 1) * 100) / 100 },
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
// ---- scroll depth --------------------------------------------------------
|
|
187
|
+
const onScroll = () => {
|
|
188
|
+
const doc = document.documentElement;
|
|
189
|
+
const scrollable = doc.scrollHeight - doc.clientHeight;
|
|
190
|
+
const pct = scrollable <= 0 ? 100 : Math.round((doc.scrollTop / scrollable) * 100);
|
|
191
|
+
if (pct > maxScrollPct)
|
|
192
|
+
maxScrollPct = Math.min(100, pct);
|
|
193
|
+
};
|
|
194
|
+
// ---- errors --------------------------------------------------------------
|
|
195
|
+
const onError = (ev) => {
|
|
196
|
+
push({
|
|
197
|
+
type: "js_error",
|
|
198
|
+
component: componentOf(ev.target),
|
|
199
|
+
meta: {
|
|
200
|
+
message: String(ev.message).slice(0, 300),
|
|
201
|
+
source: ev.filename || "",
|
|
202
|
+
line: ev.lineno || 0,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
const onRejection = (ev) => {
|
|
207
|
+
const reason = ev.reason;
|
|
208
|
+
push({
|
|
209
|
+
type: "js_error",
|
|
210
|
+
meta: { message: String(reason?.message ?? reason).slice(0, 300), kind: "unhandledrejection" },
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
// ---- web vitals (LCP, CLS) ----------------------------------------------
|
|
214
|
+
const observeVitals = () => {
|
|
215
|
+
if (typeof PerformanceObserver === "undefined")
|
|
216
|
+
return;
|
|
217
|
+
try {
|
|
218
|
+
// Largest Contentful Paint — last entry wins.
|
|
219
|
+
let lcp = 0;
|
|
220
|
+
new PerformanceObserver((list) => {
|
|
221
|
+
for (const entry of list.getEntries())
|
|
222
|
+
lcp = entry.startTime;
|
|
223
|
+
}).observe({ type: "largest-contentful-paint", buffered: true });
|
|
224
|
+
// Cumulative Layout Shift — sum of non-input shifts.
|
|
225
|
+
let cls = 0;
|
|
226
|
+
new PerformanceObserver((list) => {
|
|
227
|
+
for (const entry of list.getEntries()) {
|
|
228
|
+
const e = entry;
|
|
229
|
+
if (!e.hadRecentInput)
|
|
230
|
+
cls += e.value;
|
|
231
|
+
}
|
|
232
|
+
}).observe({ type: "layout-shift", buffered: true });
|
|
233
|
+
// Report the finalized values when the page is hidden.
|
|
234
|
+
addEventListener("visibilitychange", () => {
|
|
235
|
+
if (document.visibilityState !== "hidden")
|
|
236
|
+
return;
|
|
237
|
+
if (lcp > 0)
|
|
238
|
+
push({ type: "web_vital", value: Math.round(lcp), meta: { name: "LCP" } });
|
|
239
|
+
push({ type: "web_vital", value: Math.round(cls * 1000) / 1000, meta: { name: "CLS" } });
|
|
240
|
+
}, { once: true });
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
/* unsupported entry types — skip vitals */
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
// ---- lifecycle -----------------------------------------------------------
|
|
247
|
+
document.addEventListener("click", onClick, { capture: true, passive: true });
|
|
248
|
+
addEventListener("scroll", onScroll, { passive: true });
|
|
249
|
+
addEventListener("error", onError, true);
|
|
250
|
+
addEventListener("unhandledrejection", onRejection);
|
|
251
|
+
wrapHistory("pushState");
|
|
252
|
+
wrapHistory("replaceState");
|
|
253
|
+
addEventListener("popstate", () => {
|
|
254
|
+
if (location.pathname !== currentPath)
|
|
255
|
+
emitPageView();
|
|
256
|
+
});
|
|
257
|
+
// Final flush + session_end as the page goes away.
|
|
258
|
+
addEventListener("pagehide", () => {
|
|
259
|
+
if (maxScrollPct > 0)
|
|
260
|
+
push({ type: "scroll_depth", value: maxScrollPct });
|
|
261
|
+
push({ type: "session_end" });
|
|
262
|
+
flush(true);
|
|
263
|
+
});
|
|
264
|
+
addEventListener("visibilitychange", () => {
|
|
265
|
+
if (document.visibilityState === "hidden")
|
|
266
|
+
flush(true);
|
|
267
|
+
});
|
|
268
|
+
observeVitals();
|
|
269
|
+
setInterval(() => flush(false), cfg.flushIntervalMs);
|
|
270
|
+
// One device/viewport sample per session, plus the initial page view.
|
|
271
|
+
emitViewport();
|
|
272
|
+
push({ type: "page_view", path: currentPath });
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/client/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PolishdEvent } from "../shared/types";
|
|
2
|
+
type TrackFn = (e: Omit<PolishdEvent, "ts" | "path"> & Partial<Pick<PolishdEvent, "ts" | "path">>) => void;
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
__polishdTrack?: TrackFn;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
type Props = {
|
|
9
|
+
/** Name shown in the Polishd dashboard — keep it kebab-case and stable. */
|
|
10
|
+
name: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Treat this as a content region: emit a `mount` event and track viewport
|
|
15
|
+
* time, scroll depth, and rendered height (component_view). Leave off
|
|
16
|
+
* (default) for interactive controls like buttons, where only hover + click
|
|
17
|
+
* attribution is wanted.
|
|
18
|
+
*/
|
|
19
|
+
content?: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function PolishdMonitor({ name, children, className, content }: Props): import("react").JSX.Element;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/client/monitor.tsx"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,KAAK,OAAO,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC;AAE3G,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B;CACF;AAED,KAAK,KAAK,GAAG;IACX,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAaF,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAe,EAAE,EAAE,KAAK,+BA2InF"}
|