@polishd/next 0.2.1 → 0.2.2-dev.1
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/CHANGELOG.md +57 -0
- package/DATABASE.md +8 -1
- package/README.md +52 -0
- package/dist/ai/actions.d.ts +10 -1
- package/dist/ai/actions.d.ts.map +1 -1
- package/dist/ai/actions.js +21 -0
- package/dist/ai/actions.js.map +1 -1
- package/dist/ai/ignored.d.ts +26 -0
- package/dist/ai/ignored.d.ts.map +1 -0
- package/dist/ai/ignored.js +134 -0
- package/dist/ai/ignored.js.map +1 -0
- package/dist/ai/summary.d.ts +8 -1
- package/dist/ai/summary.d.ts.map +1 -1
- package/dist/ai/summary.js +59 -22
- package/dist/ai/summary.js.map +1 -1
- package/dist/ai/types.d.ts +31 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/client/dom.d.ts +23 -0
- package/dist/client/dom.d.ts.map +1 -0
- package/dist/client/dom.js +71 -0
- package/dist/client/dom.js.map +1 -0
- package/dist/client/init.d.ts.map +1 -1
- package/dist/client/init.js +3 -62
- package/dist/client/init.js.map +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +16 -10
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/summary.d.ts.map +1 -1
- package/dist/dashboard/summary.js +64 -4
- package/dist/dashboard/summary.js.map +1 -1
- package/dist/dashboard/telemetry-consent.d.ts +3 -0
- package/dist/dashboard/telemetry-consent.d.ts.map +1 -0
- package/dist/dashboard/telemetry-consent.js +20 -0
- package/dist/dashboard/telemetry-consent.js.map +1 -0
- package/dist/dashboard/telemetry.d.ts +21 -0
- package/dist/dashboard/telemetry.d.ts.map +1 -0
- package/dist/dashboard/telemetry.js +212 -0
- package/dist/dashboard/telemetry.js.map +1 -0
- package/dist/dashboard.css +52 -0
- package/dist/server/ingest.d.ts +20 -0
- package/dist/server/ingest.d.ts.map +1 -1
- package/dist/server/ingest.js +49 -0
- package/dist/server/ingest.js.map +1 -1
- package/dist/server/store.d.ts +6 -2
- package/dist/server/store.d.ts.map +1 -1
- package/dist/server/store.js +34 -13
- package/dist/server/store.js.map +1 -1
- package/dist/server/telemetry.d.ts +28 -0
- package/dist/server/telemetry.d.ts.map +1 -0
- package/dist/server/telemetry.js +83 -0
- package/dist/server/telemetry.js.map +1 -0
- package/dist/shared/types.d.ts +16 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/telemetry.d.ts +43 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +81 -0
- package/dist/telemetry.js.map +1 -0
- package/package.json +5 -1
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
14
14
|
* the server-side cadence refresh.
|
|
15
15
|
*/
|
|
16
16
|
import { useState, useTransition } from "react";
|
|
17
|
-
import { createIssueFromLossAction, generateSummaryAction } from "../ai/actions";
|
|
17
|
+
import { createIssueFromLossAction, generateSummaryAction, ignoreLossAction, unignoreLossAction, } from "../ai/actions";
|
|
18
18
|
import { TabLink } from "./chrome";
|
|
19
19
|
import { GearIcon, RefreshIcon, border, card, iconBtn, labelCls, primaryBtn, relTime } from "./ui";
|
|
20
20
|
export default function SummaryCard({ initialSummary, initialSettings, initialStale, initialProfile, initialGaps, sourceAvailable, }) {
|
|
@@ -38,7 +38,7 @@ export default function SummaryCard({ initialSummary, initialSettings, initialSt
|
|
|
38
38
|
};
|
|
39
39
|
const hasSummary = summary !== null;
|
|
40
40
|
const cadence = settings.refreshCadence ?? "manual";
|
|
41
|
-
return (_jsxs("section", { className: `mb-8 ${card} overflow-hidden`, children: [_jsxs("div", { className: `flex items-center justify-between gap-3 border-b ${border} px-4 py-3 sm:px-5`, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: labelCls, children: "All you need to know" }), settings.hasApiKey && (_jsxs("span", { className: "hidden rounded-full bg-[#1a1a1a] px-2 py-0.5 text-[10px] font-medium text-[#888] sm:inline", children: [settings.provider, " \u00B7 ", settings.model] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => generate(hasSummary), disabled: pending || !settings.hasApiKey, title: settings.hasApiKey ? "Regenerate the summary now" : "Connect a model first", "aria-label": "Refresh summary", className: iconBtn, children: _jsx(RefreshIcon, { spinning: pending }) }), _jsx(TabLink, { tab: "settings", title: "Model, site context and GitHub settings", "aria-label": "Open settings", className: iconBtn, children: _jsx(GearIcon, {}) })] })] }), _jsxs("div", { className: "px-4 py-4 sm:px-5 sm:py-5", children: [error && (_jsx("div", { className: "mb-3 rounded-md border border-red-900/50 bg-red-950/30 px-3 py-2 text-[12px] text-red-400", children: error })), hasSummary ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-[14px] leading-relaxed text-[#e4e4e4]", children: summary.text }), _jsx(WinsLosses, { wins: summary.wins, losses: summary.losses, githubConnected: settings.hasGithubToken && !!settings.githubRepo }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-[#555]", children: [_jsxs("span", { children: ["Generated ", relTime(summary.generatedAt), " \u00B7 ", summary.provider, "/", summary.model, cadence !== "manual" && ` · refreshes ${cadence}`] }), stale && (_jsx("span", { className: "rounded-full bg-amber-950/50 px-2 py-0.5 text-amber-400", children: "
|
|
41
|
+
return (_jsxs("section", { className: `mb-8 ${card} overflow-hidden`, children: [_jsxs("div", { className: `flex items-center justify-between gap-3 border-b ${border} px-4 py-3 sm:px-5`, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: labelCls, children: "All you need to know" }), settings.hasApiKey && (_jsxs("span", { className: "hidden rounded-full bg-[#1a1a1a] px-2 py-0.5 text-[10px] font-medium text-[#888] sm:inline", children: [settings.provider, " \u00B7 ", settings.model] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => generate(hasSummary), disabled: pending || !settings.hasApiKey, title: settings.hasApiKey ? "Regenerate the summary now" : "Connect a model first", "aria-label": "Refresh summary", className: iconBtn, children: _jsx(RefreshIcon, { spinning: pending }) }), _jsx(TabLink, { tab: "settings", title: "Model, site context and GitHub settings", "aria-label": "Open settings", className: iconBtn, children: _jsx(GearIcon, {}) })] })] }), _jsxs("div", { className: "px-4 py-4 sm:px-5 sm:py-5", children: [error && (_jsx("div", { className: "mb-3 rounded-md border border-red-900/50 bg-red-950/30 px-3 py-2 text-[12px] text-red-400", children: error })), hasSummary ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-[14px] leading-relaxed text-[#e4e4e4]", children: summary.text }), _jsx(WinsLosses, { wins: summary.wins, losses: summary.losses, githubConnected: settings.hasGithubToken && !!settings.githubRepo }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-[#555]", children: [_jsxs("span", { children: ["Generated ", relTime(summary.generatedAt), " \u00B7 ", summary.provider, "/", summary.model, cadence !== "manual" && ` · refreshes ${cadence}`] }), stale && (_jsx("span", { className: "rounded-full bg-amber-950/50 px-2 py-0.5 text-amber-400", children: "Something's changed since this summary \u2014 refresh to update" }))] })] })) : (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("p", { className: "text-[13px] leading-relaxed text-[#888]", children: settings.hasApiKey
|
|
42
42
|
? "No summary yet — generate the story of how visitors are using this site from the signals below."
|
|
43
43
|
: "No model connected yet. Add a key in Settings and Polishd will read these numbers back to you in plain English." }), settings.hasApiKey ? (_jsx("button", { type: "button", onClick: () => generate(false), disabled: pending, className: primaryBtn, children: pending ? "Thinking…" : "Generate summary" })) : (_jsx(TabLink, { tab: "settings", className: primaryBtn, children: "Connect a model" }))] }))] }), _jsx(ProfileNudge, { profile: initialProfile, gaps: initialGaps, sourceAvailable: sourceAvailable })] }));
|
|
44
44
|
}
|
|
@@ -61,6 +61,9 @@ function ProfileNudge({ profile, gaps, sourceAvailable, }) {
|
|
|
61
61
|
return (_jsxs("div", { className: `flex flex-wrap items-center justify-between gap-2 border-t ${border} bg-[#080808] px-4 py-2.5 sm:px-5`, children: [_jsx("span", { className: "text-[11px] text-[#666]", children: message }), _jsxs(TabLink, { tab: "settings", className: "text-[11px] text-[#888] underline decoration-[#444] underline-offset-2 hover:text-white", children: [profile ? "Re-scan in Settings" : "Scan in Settings", " \u2192"] })] }));
|
|
62
62
|
}
|
|
63
63
|
// ── Wins & losses ────────────────────────────────────────────────────────────
|
|
64
|
+
/** The two verdicts on a loss sit side by side, and share their button style. */
|
|
65
|
+
const microBtn = "rounded border border-[#2e2e2e] px-1.5 py-0.5 text-[10px] text-[#888] transition-colors hover:border-[#555] hover:text-white disabled:cursor-not-allowed disabled:opacity-40";
|
|
66
|
+
const microLink = "text-[10px] text-[#666] underline decoration-[#333] underline-offset-2 transition-colors hover:text-[#ccc] disabled:cursor-not-allowed disabled:opacity-40";
|
|
64
67
|
/**
|
|
65
68
|
* One-click "loss → GitHub issue". The server action verifies the report
|
|
66
69
|
* against the repository's source first: confirmed reports become issues with
|
|
@@ -91,13 +94,70 @@ function FileBugButton({ loss }) {
|
|
|
91
94
|
else
|
|
92
95
|
setFailed({ notABug: res.error === "not-a-bug", message: res.message });
|
|
93
96
|
});
|
|
94
|
-
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: file, disabled: pending, title: "Verify this problem against the source code, then create a GitHub issue with the technical details", className:
|
|
97
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: file, disabled: pending, title: "Verify this problem against the source code, then create a GitHub issue with the technical details", className: microBtn, children: pending ? "Verifying…" : "File bug" }), failed && _jsx("span", { className: "text-[10px] text-red-400", children: failed.message })] }));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* One loss, and the two things you can do with it: file it as a bug, or ignore
|
|
101
|
+
* it. Ignoring takes one click and no explanation — a reason is offered
|
|
102
|
+
* afterwards, because the fast path is "this isn't a problem, stop showing me
|
|
103
|
+
* it" and typing shouldn't be the toll for that. A reason is worth giving
|
|
104
|
+
* though: it's replayed to the model on every later summary, so "the second CTA
|
|
105
|
+
* is deliberate" stops the same finding coming back in different words.
|
|
106
|
+
*
|
|
107
|
+
* The dismissal is stored server-side immediately; the row stays put, struck
|
|
108
|
+
* through with an undo, and only disappears on the next render. Ignored losses
|
|
109
|
+
* never reappear — not from the cached summary, and not from a regenerate.
|
|
110
|
+
*/
|
|
111
|
+
function LossRow({ loss, githubConnected }) {
|
|
112
|
+
const [ignored, setIgnored] = useState(null);
|
|
113
|
+
const [editing, setEditing] = useState(false);
|
|
114
|
+
const [draft, setDraft] = useState("");
|
|
115
|
+
const [error, setError] = useState(null);
|
|
116
|
+
const [pending, startIgnore] = useTransition();
|
|
117
|
+
const ignore = (reason) => startIgnore(async () => {
|
|
118
|
+
setError(null);
|
|
119
|
+
const res = await ignoreLossAction({ issue: loss.issue, evidence: loss.evidence }, reason);
|
|
120
|
+
if (res.ok) {
|
|
121
|
+
setIgnored(res.ignored);
|
|
122
|
+
setEditing(false);
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
setError(res.message);
|
|
126
|
+
});
|
|
127
|
+
const undo = () => startIgnore(async () => {
|
|
128
|
+
setError(null);
|
|
129
|
+
const res = await unignoreLossAction(loss.evidence);
|
|
130
|
+
if (res.ok) {
|
|
131
|
+
setIgnored(null);
|
|
132
|
+
setEditing(false);
|
|
133
|
+
setDraft("");
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
setError(res.message);
|
|
137
|
+
});
|
|
138
|
+
if (ignored) {
|
|
139
|
+
return (_jsxs("li", { className: "flex gap-2 text-[13px] leading-snug text-[#666]", children: [_jsx("span", { className: "mt-px shrink-0 text-[#444]", children: "\u2715" }), _jsxs("span", { children: [_jsx("span", { className: "line-through decoration-[#3a3a3a]", children: loss.issue }), _jsxs("span", { className: "mt-0.5 flex flex-wrap items-center gap-1.5", children: [_jsxs("span", { className: "rounded bg-[#1a1a1a] px-1.5 py-0.5 text-[10px] text-[#777]", children: ["Ignored", ignored.reason ? ` — ${ignored.reason}` : ""] }), editing ? (_jsx(ReasonForm, { draft: draft, setDraft: setDraft, pending: pending, onSubmit: () => ignore(draft.trim() || undefined), onCancel: () => setEditing(false) })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => {
|
|
140
|
+
setDraft(ignored.reason ?? "");
|
|
141
|
+
setEditing(true);
|
|
142
|
+
}, disabled: pending, title: "Tell the model why this isn't a problem \u2014 it reads this on every later summary", className: microLink, children: ignored.reason ? "Edit reason" : "Add a reason" }), _jsx("button", { type: "button", onClick: undo, disabled: pending, title: "Put this back \u2014 the model can report it again", className: microLink, children: pending ? "Undoing…" : "Undo" })] })), error && _jsx("span", { className: "text-[10px] text-red-400", children: error })] })] })] }));
|
|
143
|
+
}
|
|
144
|
+
return (_jsxs("li", { className: "flex gap-2 text-[13px] leading-snug text-[#bbb]", children: [_jsx("span", { className: "mt-px shrink-0 text-red-400", children: "\u2715" }), _jsxs("span", { children: [loss.issue, _jsxs("span", { className: "mt-0.5 flex flex-wrap items-center gap-1.5", children: [_jsx("code", { className: "rounded bg-[#1a1a1a] px-1.5 py-0.5 font-mono text-[10px] text-[#888]", children: loss.evidence }), loss.location ? (_jsx("code", { className: "rounded bg-[#101c14] px-1.5 py-0.5 font-mono text-[10px] text-emerald-500", children: loss.location })) : (_jsx("span", { className: "rounded bg-[#1a1a1a] px-1.5 py-0.5 text-[10px] text-[#666]", title: "The citation is real analytics data, but no matching file was found on disk (dynamic content, or source not available here).", children: "not matched to source" })), (githubConnected || !!loss.issueUrl) && _jsx(FileBugButton, { loss: loss }), _jsx("button", { type: "button", onClick: () => ignore(), disabled: pending, title: "Not a problem \u2014 drop it from this summary and stop the model reporting it again", className: microBtn, children: pending ? "Ignoring…" : "Ignore" }), error && _jsx("span", { className: "text-[10px] text-red-400", children: error })] })] })] }));
|
|
145
|
+
}
|
|
146
|
+
/** The optional half of ignoring: why it isn't a problem, in the owner's words. */
|
|
147
|
+
function ReasonForm({ draft, setDraft, pending, onSubmit, onCancel, }) {
|
|
148
|
+
return (_jsxs("form", { onSubmit: (e) => {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
onSubmit();
|
|
151
|
+
}, className: "flex flex-wrap items-center gap-1.5", children: [_jsx("input", { autoFocus: true, value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: (e) => {
|
|
152
|
+
if (e.key === "Escape")
|
|
153
|
+
onCancel();
|
|
154
|
+
}, maxLength: 240, placeholder: "Why isn't this a problem?", "aria-label": "Reason for ignoring this loss", className: "w-56 rounded border border-[#2e2e2e] bg-[#0d0d0d] px-1.5 py-0.5 text-[10px] text-[#ddd] placeholder:text-[#555] focus:border-[#555] focus:outline-none" }), _jsx("button", { type: "submit", disabled: pending, className: microBtn, children: pending ? "Saving…" : "Save" }), _jsx("button", { type: "button", onClick: onCancel, disabled: pending, className: microLink, children: "Cancel" })] }));
|
|
95
155
|
}
|
|
96
156
|
function WinsLosses({ wins, losses, githubConnected, }) {
|
|
97
157
|
const hasWins = !!wins?.length;
|
|
98
158
|
const hasLosses = !!losses?.length;
|
|
99
159
|
if (!hasWins && !hasLosses)
|
|
100
160
|
return null;
|
|
101
|
-
return (_jsxs("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: [hasWins && (_jsxs("div", { children: [_jsx("div", { className: `${labelCls} mb-2 text-emerald-500`, children: "Wins" }), _jsx("ul", { className: "space-y-1.5", children: wins.map((w, i) => (_jsxs("li", { className: "flex gap-2 text-[13px] leading-snug text-[#bbb]", children: [_jsx("span", { className: "mt-px shrink-0 text-emerald-500", children: "\u2713" }), _jsx("span", { children: w })] }, i))) })] })), hasLosses && (_jsxs("div", { children: [_jsx("div", { className: `${labelCls} mb-2 text-red-400`, children: "Losses" }), _jsx("ul", { className: "space-y-2.5", children: losses.map((l, i) => (
|
|
161
|
+
return (_jsxs("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: [hasWins && (_jsxs("div", { children: [_jsx("div", { className: `${labelCls} mb-2 text-emerald-500`, children: "Wins" }), _jsx("ul", { className: "space-y-1.5", children: wins.map((w, i) => (_jsxs("li", { className: "flex gap-2 text-[13px] leading-snug text-[#bbb]", children: [_jsx("span", { className: "mt-px shrink-0 text-emerald-500", children: "\u2713" }), _jsx("span", { children: w })] }, i))) })] })), hasLosses && (_jsxs("div", { children: [_jsx("div", { className: `${labelCls} mb-2 text-red-400`, children: "Losses" }), _jsx("ul", { className: "space-y-2.5", children: losses.map((l, i) => (_jsx(LossRow, { loss: l, githubConnected: githubConnected }, l.evidence || i))) })] }))] }));
|
|
102
162
|
}
|
|
103
163
|
//# sourceMappingURL=summary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/dashboard/summary.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/dashboard/summary.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-consent.d.ts","sourceRoot":"","sources":["../../src/dashboard/telemetry-consent.ts"],"names":[],"mappings":"AAYA,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
/**
|
|
3
|
+
* Polishd — telemetry consent actions.
|
|
4
|
+
*
|
|
5
|
+
* The consent prompt's two buttons land here. Guarded like every other action
|
|
6
|
+
* in the package: server actions are independently addressable POST endpoints,
|
|
7
|
+
* so without the auth check anyone who found the action id could flip a
|
|
8
|
+
* dashboard's telemetry on. See ai/guard.ts.
|
|
9
|
+
*/
|
|
10
|
+
import { requirePolishdAuth } from "../ai/guard";
|
|
11
|
+
import { saveTelemetryConsent } from "../server/telemetry";
|
|
12
|
+
export async function grantPolishdTelemetry() {
|
|
13
|
+
await requirePolishdAuth();
|
|
14
|
+
await saveTelemetryConsent(true);
|
|
15
|
+
}
|
|
16
|
+
export async function denyPolishdTelemetry() {
|
|
17
|
+
await requirePolishdAuth();
|
|
18
|
+
await saveTelemetryConsent(false);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=telemetry-consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-consent.js","sourceRoot":"","sources":["../../src/dashboard/telemetry-consent.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AACb;;;;;;;GAOG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
__polishdTelemetryStarted?: boolean;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Mounted by the dashboard page only when consent is granted. Renders nothing;
|
|
8
|
+
* starts the capture singleton once per page load (the listeners are global
|
|
9
|
+
* and deliberately never torn down — capture suspends off-dashboard instead).
|
|
10
|
+
*/
|
|
11
|
+
export declare function PolishdTelemetryEmitter({ endpoint, installId, }: {
|
|
12
|
+
endpoint: string;
|
|
13
|
+
installId: string;
|
|
14
|
+
}): null;
|
|
15
|
+
/**
|
|
16
|
+
* The one-time opt-in prompt, shown while consent is "unset". Both buttons
|
|
17
|
+
* persist the decision through a guarded server action; "Share" refreshes the
|
|
18
|
+
* server render so the emitter mounts immediately rather than on next visit.
|
|
19
|
+
*/
|
|
20
|
+
export declare function PolishdTelemetryConsent(): import("react").JSX.Element | null;
|
|
21
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/dashboard/telemetry.tsx"],"names":[],"mappings":"AAsDA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,yBAAyB,CAAC,EAAE,OAAO,CAAC;KACrC;CACF;AAuID;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,QAQA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,uCAuDtC"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Polishd — the dashboard's own telemetry: emitter + consent prompt.
|
|
5
|
+
*
|
|
6
|
+
* The dashboard is polishd's product surface, and it is exactly the traffic
|
|
7
|
+
* the package refuses to record for its hosts (the dashboard-path drop in
|
|
8
|
+
* ingest). This emitter is the separate, opt-in channel that records it —
|
|
9
|
+
* dashboard interactions only — and reports it to the polishd project's own
|
|
10
|
+
* `@polishd/next/telemetry` endpoint, so the dashboard is improved by the same
|
|
11
|
+
* signals it preaches.
|
|
12
|
+
*
|
|
13
|
+
* Everything about the transport differs from the host-site capture layer
|
|
14
|
+
* because the POST crosses origins:
|
|
15
|
+
*
|
|
16
|
+
* • Identity travels in the body — an anonymous session id minted here (the
|
|
17
|
+
* httpOnly session cookie never leaves its origin) plus the stable
|
|
18
|
+
* anonymous install id the server resolved from `polishd_meta`.
|
|
19
|
+
* • Batches are sent as `text/plain`, which keeps the request inside CORS's
|
|
20
|
+
* "simple" class — no preflight — and is the only shape `sendBeacon` can
|
|
21
|
+
* deliver cross-origin anyway. The receiving route parses the body as
|
|
22
|
+
* JSON regardless of the header.
|
|
23
|
+
* • Paths are namespaced under `/~polishd/<tab>` so the receiving database
|
|
24
|
+
* can never confuse dashboard telemetry with the receiving site's own
|
|
25
|
+
* first-party pages.
|
|
26
|
+
*
|
|
27
|
+
* What is captured: page views (tab switches), clicks (with the same
|
|
28
|
+
* rage/dead classification as the host capture layer, via the shared DOM
|
|
29
|
+
* helpers), one viewport sample per session, and session end. Nothing else —
|
|
30
|
+
* no scroll depth, no errors, no design scans, and never anything from the
|
|
31
|
+
* host site's own pages: capture suspends the moment the URL leaves the
|
|
32
|
+
* dashboard's mount path.
|
|
33
|
+
*/
|
|
34
|
+
import { useRouter } from "next/navigation";
|
|
35
|
+
import { useEffect, useState, useTransition } from "react";
|
|
36
|
+
import { componentOf, deviceCategory, hasTextSelection, isInteractive, labelOf, selectorOf, } from "../client/dom";
|
|
37
|
+
import { denyPolishdTelemetry, grantPolishdTelemetry } from "./telemetry-consent";
|
|
38
|
+
const PATH_PREFIX = "/~polishd";
|
|
39
|
+
const SESSION_KEY = "polishd_telemetry_session";
|
|
40
|
+
const FLUSH_INTERVAL_MS = 15_000;
|
|
41
|
+
const MAX_BATCH = 50;
|
|
42
|
+
const RAGE_COUNT = 3;
|
|
43
|
+
const RAGE_WINDOW_MS = 500;
|
|
44
|
+
/** The telemetry path for the tab currently in the URL. */
|
|
45
|
+
function telemetryPath() {
|
|
46
|
+
const tab = new URLSearchParams(location.search).get("tab");
|
|
47
|
+
return `${PATH_PREFIX}/${tab || "analytics"}`;
|
|
48
|
+
}
|
|
49
|
+
/** A session id that survives soft navigation but not the browser session. */
|
|
50
|
+
function sessionId() {
|
|
51
|
+
try {
|
|
52
|
+
const existing = sessionStorage.getItem(SESSION_KEY);
|
|
53
|
+
if (existing)
|
|
54
|
+
return existing;
|
|
55
|
+
const minted = crypto.randomUUID();
|
|
56
|
+
sessionStorage.setItem(SESSION_KEY, minted);
|
|
57
|
+
return minted;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Storage blocked — a per-page-load id is the honest fallback.
|
|
61
|
+
return crypto.randomUUID();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function start(endpoint, installId) {
|
|
65
|
+
const sid = sessionId();
|
|
66
|
+
// The route the dashboard is mounted at, learned from where we woke up.
|
|
67
|
+
// Capture is suspended whenever the URL wanders off it — the listeners
|
|
68
|
+
// below outlive the dashboard page, and host-site interactions must never
|
|
69
|
+
// leak into telemetry.
|
|
70
|
+
const mountPath = location.pathname;
|
|
71
|
+
const onDashboard = () => location.pathname === mountPath;
|
|
72
|
+
const queue = [];
|
|
73
|
+
let currentPath = telemetryPath();
|
|
74
|
+
const flush = (useBeacon = false) => {
|
|
75
|
+
if (queue.length === 0)
|
|
76
|
+
return;
|
|
77
|
+
const batch = queue.splice(0, queue.length);
|
|
78
|
+
const body = JSON.stringify({ installId, sessionId: sid, events: batch });
|
|
79
|
+
if (useBeacon && navigator.sendBeacon) {
|
|
80
|
+
const ok = navigator.sendBeacon(endpoint, new Blob([body], { type: "text/plain" }));
|
|
81
|
+
if (!ok)
|
|
82
|
+
queue.unshift(...batch);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
fetch(endpoint, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "text/plain" },
|
|
88
|
+
body,
|
|
89
|
+
keepalive: true,
|
|
90
|
+
}).catch(() => {
|
|
91
|
+
queue.unshift(...batch);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const push = (e) => {
|
|
95
|
+
if (!onDashboard())
|
|
96
|
+
return;
|
|
97
|
+
queue.push({ ts: Date.now(), path: currentPath, ...e });
|
|
98
|
+
if (queue.length >= MAX_BATCH)
|
|
99
|
+
flush();
|
|
100
|
+
};
|
|
101
|
+
// ---- clicks (same classification as the host capture layer) --------------
|
|
102
|
+
let lastClick = { selector: "", time: 0, count: 0 };
|
|
103
|
+
document.addEventListener("click", (ev) => {
|
|
104
|
+
const target = ev.target;
|
|
105
|
+
if (!target || !onDashboard())
|
|
106
|
+
return;
|
|
107
|
+
const selector = selectorOf(target);
|
|
108
|
+
const component = componentOf(target);
|
|
109
|
+
const text = labelOf(target);
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
if (selector === lastClick.selector && now - lastClick.time < RAGE_WINDOW_MS) {
|
|
112
|
+
lastClick.count++;
|
|
113
|
+
lastClick.time = now;
|
|
114
|
+
if (lastClick.count === RAGE_COUNT) {
|
|
115
|
+
push({ type: "rage_click", selector, component, text });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
lastClick = { selector: selector ?? "", time: now, count: 1 };
|
|
120
|
+
}
|
|
121
|
+
if (!isInteractive(target)) {
|
|
122
|
+
if (hasTextSelection())
|
|
123
|
+
return;
|
|
124
|
+
push({ type: "dead_click", selector, component, text });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
push({ type: "click", selector, component, text });
|
|
128
|
+
}
|
|
129
|
+
}, { capture: true, passive: true });
|
|
130
|
+
// ---- page views (tab switches ride on the History API) -------------------
|
|
131
|
+
const onNavigate = () => {
|
|
132
|
+
if (!onDashboard())
|
|
133
|
+
return;
|
|
134
|
+
const next = telemetryPath();
|
|
135
|
+
if (next === currentPath)
|
|
136
|
+
return;
|
|
137
|
+
currentPath = next;
|
|
138
|
+
push({ type: "page_view", path: currentPath });
|
|
139
|
+
};
|
|
140
|
+
const wrapHistory = (method) => {
|
|
141
|
+
const original = history[method];
|
|
142
|
+
history[method] = function (...args) {
|
|
143
|
+
const ret = original.apply(this, args);
|
|
144
|
+
onNavigate();
|
|
145
|
+
return ret;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
wrapHistory("pushState");
|
|
149
|
+
wrapHistory("replaceState");
|
|
150
|
+
addEventListener("popstate", onNavigate);
|
|
151
|
+
// ---- lifecycle -----------------------------------------------------------
|
|
152
|
+
addEventListener("pagehide", () => {
|
|
153
|
+
push({ type: "session_end" });
|
|
154
|
+
flush(true);
|
|
155
|
+
});
|
|
156
|
+
addEventListener("visibilitychange", () => {
|
|
157
|
+
if (document.visibilityState === "hidden")
|
|
158
|
+
flush(true);
|
|
159
|
+
});
|
|
160
|
+
setInterval(() => flush(false), FLUSH_INTERVAL_MS);
|
|
161
|
+
const w = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
162
|
+
if (w > 0) {
|
|
163
|
+
push({ type: "viewport", value: w, text: deviceCategory(w) });
|
|
164
|
+
}
|
|
165
|
+
push({ type: "page_view", path: currentPath });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Mounted by the dashboard page only when consent is granted. Renders nothing;
|
|
169
|
+
* starts the capture singleton once per page load (the listeners are global
|
|
170
|
+
* and deliberately never torn down — capture suspends off-dashboard instead).
|
|
171
|
+
*/
|
|
172
|
+
export function PolishdTelemetryEmitter({ endpoint, installId, }) {
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (window.__polishdTelemetryStarted)
|
|
175
|
+
return;
|
|
176
|
+
if (navigator.doNotTrack === "1")
|
|
177
|
+
return;
|
|
178
|
+
window.__polishdTelemetryStarted = true;
|
|
179
|
+
start(endpoint, installId);
|
|
180
|
+
}, [endpoint, installId]);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The one-time opt-in prompt, shown while consent is "unset". Both buttons
|
|
185
|
+
* persist the decision through a guarded server action; "Share" refreshes the
|
|
186
|
+
* server render so the emitter mounts immediately rather than on next visit.
|
|
187
|
+
*/
|
|
188
|
+
export function PolishdTelemetryConsent() {
|
|
189
|
+
const router = useRouter();
|
|
190
|
+
const [dismissed, setDismissed] = useState(false);
|
|
191
|
+
const [pending, startTransition] = useTransition();
|
|
192
|
+
if (dismissed)
|
|
193
|
+
return null;
|
|
194
|
+
const decide = (granted) => startTransition(async () => {
|
|
195
|
+
try {
|
|
196
|
+
if (granted) {
|
|
197
|
+
await grantPolishdTelemetry();
|
|
198
|
+
router.refresh();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
await denyPolishdTelemetry();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Denied or store unavailable — the banner will be back next render;
|
|
206
|
+
// hiding it now keeps this visit usable either way.
|
|
207
|
+
}
|
|
208
|
+
setDismissed(true);
|
|
209
|
+
});
|
|
210
|
+
return (_jsxs("div", { role: "dialog", "aria-label": "Polishd telemetry consent", className: "fixed bottom-4 right-4 z-50 w-[340px] max-w-[calc(100vw-2rem)] rounded-xl border border-[#2e2e2e] bg-[#111] p-4 shadow-2xl", children: [_jsx("p", { className: "text-[13px] font-medium text-white", children: "Help improve polishd?" }), _jsxs("p", { className: "mt-1.5 text-[12px] leading-relaxed text-[#888]", children: ["Share anonymous usage of ", _jsx("span", { className: "text-[#aaa]", children: "this dashboard" }), " \u2014 clicks and tab views inside it, tied to a random install id. Never your site's analytics, never your visitors' data. Opt out any time with", " ", _jsx("code", { className: "font-mono text-[11px] text-[#aaa]", children: "POLISHD_TELEMETRY=off" }), "."] }), _jsxs("div", { className: "mt-3 flex gap-2", children: [_jsx("button", { type: "button", disabled: pending, onClick: () => decide(true), className: "rounded-lg bg-white px-3 py-1.5 text-[12px] font-medium text-black transition-opacity hover:opacity-90 disabled:opacity-50", children: "Share" }), _jsx("button", { type: "button", disabled: pending, onClick: () => decide(false), className: "rounded-lg border border-[#2e2e2e] px-3 py-1.5 text-[12px] text-[#888] transition-colors hover:text-white disabled:opacity-50", children: "No thanks" })] })] }));
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/dashboard/telemetry.tsx"],"names":[],"mappings":"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"}
|
package/dist/dashboard.css
CHANGED
|
@@ -298,10 +298,18 @@
|
|
|
298
298
|
right: 0;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
.polishd-root.polishd-root .right-4 {
|
|
302
|
+
right: calc(var(--spacing) * 4);
|
|
303
|
+
}
|
|
304
|
+
|
|
301
305
|
.polishd-root.polishd-root .bottom-0 {
|
|
302
306
|
bottom: 0;
|
|
303
307
|
}
|
|
304
308
|
|
|
309
|
+
.polishd-root.polishd-root .bottom-4 {
|
|
310
|
+
bottom: calc(var(--spacing) * 4);
|
|
311
|
+
}
|
|
312
|
+
|
|
305
313
|
.polishd-root.polishd-root .bottom-full {
|
|
306
314
|
bottom: 100%;
|
|
307
315
|
}
|
|
@@ -712,6 +720,10 @@
|
|
|
712
720
|
width: calc(var(--spacing) * 40);
|
|
713
721
|
}
|
|
714
722
|
|
|
723
|
+
.polishd-root.polishd-root .w-56 {
|
|
724
|
+
width: calc(var(--spacing) * 56);
|
|
725
|
+
}
|
|
726
|
+
|
|
715
727
|
.polishd-root.polishd-root .w-64 {
|
|
716
728
|
width: calc(var(--spacing) * 64);
|
|
717
729
|
}
|
|
@@ -744,6 +756,10 @@
|
|
|
744
756
|
width: 92%;
|
|
745
757
|
}
|
|
746
758
|
|
|
759
|
+
.polishd-root.polishd-root .w-\[340px\] {
|
|
760
|
+
width: 340px;
|
|
761
|
+
}
|
|
762
|
+
|
|
747
763
|
.polishd-root.polishd-root .w-full {
|
|
748
764
|
width: 100%;
|
|
749
765
|
}
|
|
@@ -860,6 +876,10 @@
|
|
|
860
876
|
flex: none;
|
|
861
877
|
}
|
|
862
878
|
|
|
879
|
+
.polishd-root.polishd-root .shrink {
|
|
880
|
+
flex-shrink: 1;
|
|
881
|
+
}
|
|
882
|
+
|
|
863
883
|
.polishd-root.polishd-root .shrink-0 {
|
|
864
884
|
flex-shrink: 0;
|
|
865
885
|
}
|
|
@@ -1149,6 +1169,10 @@
|
|
|
1149
1169
|
border-radius: var(--radius-sm);
|
|
1150
1170
|
}
|
|
1151
1171
|
|
|
1172
|
+
.polishd-root.polishd-root .rounded-xl {
|
|
1173
|
+
border-radius: var(--radius-xl);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1152
1176
|
.polishd-root.polishd-root .rounded-t-xl {
|
|
1153
1177
|
border-top-left-radius: var(--radius-xl);
|
|
1154
1178
|
border-top-right-radius: var(--radius-xl);
|
|
@@ -1255,6 +1279,10 @@
|
|
|
1255
1279
|
background-color: oklab(21.479% -.0106614 -.037564 / .6);
|
|
1256
1280
|
}
|
|
1257
1281
|
|
|
1282
|
+
.polishd-root.polishd-root .bg-\[\#0d0d0d\] {
|
|
1283
|
+
background-color: #0d0d0d;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1258
1286
|
.polishd-root.polishd-root .bg-\[\#1a1a1a\] {
|
|
1259
1287
|
background-color: #1a1a1a;
|
|
1260
1288
|
}
|
|
@@ -2019,6 +2047,10 @@
|
|
|
2019
2047
|
color: #ccc;
|
|
2020
2048
|
}
|
|
2021
2049
|
|
|
2050
|
+
.polishd-root.polishd-root .text-\[\#ddd\] {
|
|
2051
|
+
color: #ddd;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2022
2054
|
.polishd-root.polishd-root .text-\[\#e4e4e4\] {
|
|
2023
2055
|
color: #e4e4e4;
|
|
2024
2056
|
}
|
|
@@ -2120,10 +2152,22 @@
|
|
|
2120
2152
|
font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
|
|
2121
2153
|
}
|
|
2122
2154
|
|
|
2155
|
+
.polishd-root.polishd-root .line-through {
|
|
2156
|
+
text-decoration-line: line-through;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2123
2159
|
.polishd-root.polishd-root .underline {
|
|
2124
2160
|
text-decoration-line: underline;
|
|
2125
2161
|
}
|
|
2126
2162
|
|
|
2163
|
+
.polishd-root.polishd-root .decoration-\[\#3a3a3a\] {
|
|
2164
|
+
text-decoration-color: #3a3a3a;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
.polishd-root.polishd-root .decoration-\[\#333\] {
|
|
2168
|
+
text-decoration-color: #333;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2127
2171
|
.polishd-root.polishd-root .decoration-\[\#444\] {
|
|
2128
2172
|
text-decoration-color: #444;
|
|
2129
2173
|
}
|
|
@@ -2275,6 +2319,10 @@
|
|
|
2275
2319
|
color: #888;
|
|
2276
2320
|
}
|
|
2277
2321
|
|
|
2322
|
+
.polishd-root.polishd-root .hover\:text-\[\#ccc\]:hover {
|
|
2323
|
+
color: #ccc;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2278
2326
|
.polishd-root.polishd-root .hover\:text-emerald-400:hover {
|
|
2279
2327
|
color: var(--color-emerald-400);
|
|
2280
2328
|
}
|
|
@@ -2309,6 +2357,10 @@
|
|
|
2309
2357
|
opacity: .4;
|
|
2310
2358
|
}
|
|
2311
2359
|
|
|
2360
|
+
.polishd-root.polishd-root .disabled\:opacity-50:disabled {
|
|
2361
|
+
opacity: .5;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2312
2364
|
@media not all and (min-width: 40rem) {
|
|
2313
2365
|
.polishd-root.polishd-root .max-sm\:hidden {
|
|
2314
2366
|
display: none;
|
package/dist/server/ingest.d.ts
CHANGED
|
@@ -42,4 +42,24 @@ export declare function sessionIdFromCookie(cookieValue: string | undefined): st
|
|
|
42
42
|
* they do in the browser. It falls back to the defaults when omitted.
|
|
43
43
|
*/
|
|
44
44
|
export declare function ingest(body: unknown, cookieValue: string | undefined, config?: Partial<PolishdConfig>): Promise<IngestResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Ingest a telemetry batch from a *remote* polishd installation.
|
|
47
|
+
*
|
|
48
|
+
* This differs from first-party {@link ingest} in exactly three ways, each a
|
|
49
|
+
* consequence of the request crossing origins:
|
|
50
|
+
*
|
|
51
|
+
* 1. Identity travels in the body. The session cookie is httpOnly and
|
|
52
|
+
* first-party, so it never arrives here; the emitter mints an anonymous
|
|
53
|
+
* session id itself and sends it alongside a stable `installId`. Both are
|
|
54
|
+
* validated by shape only — there is nothing to verify them against, and
|
|
55
|
+
* the worst a forger achieves is polluting telemetry, same as any public
|
|
56
|
+
* ingest endpoint.
|
|
57
|
+
* 2. Every stored row is tagged with the `installId`, so one shared database
|
|
58
|
+
* can tell installations apart.
|
|
59
|
+
* 3. Dashboard paths are NOT dropped. Recording how the dashboard itself is
|
|
60
|
+
* used is the entire point of this channel; the emitter is expected to
|
|
61
|
+
* namespace its paths so they can't collide with the receiving site's own
|
|
62
|
+
* first-party events.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ingestTelemetry(body: unknown, config?: Partial<PolishdConfig>): Promise<IngestResult>;
|
|
45
65
|
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/server/ingest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AAenB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AA+BD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAElF;AAkCD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC1B,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/server/ingest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AAenB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AA+BD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAElF;AAkCD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC1B,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,YAAY,CAAC,CAyBvB"}
|