@polishd/next 0.1.0 → 0.2.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 +161 -46
- package/CHANGELOG.md +128 -0
- package/README.md +238 -48
- package/bin/doctor.mjs +374 -0
- package/bin/init.mjs +215 -188
- package/bin/polishd.mjs +85 -0
- package/bin/shared.mjs +114 -0
- package/dist/ai/actions.d.ts +7 -2
- package/dist/ai/actions.d.ts.map +1 -1
- package/dist/ai/actions.js +9 -1
- package/dist/ai/actions.js.map +1 -1
- package/dist/ai/guard.d.ts +44 -3
- package/dist/ai/guard.d.ts.map +1 -1
- package/dist/ai/guard.js +13 -2
- package/dist/ai/guard.js.map +1 -1
- package/dist/ai/providers.d.ts +2 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +70 -0
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/settings.d.ts +15 -1
- package/dist/ai/settings.d.ts.map +1 -1
- package/dist/ai/settings.js +38 -1
- package/dist/ai/settings.js.map +1 -1
- package/dist/ai/types.d.ts +10 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/client/init.d.ts.map +1 -1
- package/dist/client/init.js +51 -2
- package/dist/client/init.js.map +1 -1
- package/dist/config.d.ts +57 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +62 -3
- package/dist/config.js.map +1 -1
- package/dist/dashboard/index.d.ts +34 -5
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +145 -17
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/summary.d.ts.map +1 -1
- package/dist/dashboard/summary.js +59 -5
- package/dist/dashboard/summary.js.map +1 -1
- package/dist/dashboard/token-auth.d.ts +56 -0
- package/dist/dashboard/token-auth.d.ts.map +1 -0
- package/dist/dashboard/token-auth.js +158 -0
- package/dist/dashboard/token-auth.js.map +1 -0
- package/dist/dashboard/unlock.d.ts +2 -0
- package/dist/dashboard/unlock.d.ts.map +1 -0
- package/dist/dashboard/unlock.js +54 -0
- package/dist/dashboard/unlock.js.map +1 -0
- package/dist/dashboard.css +2601 -0
- package/dist/proxy.d.ts +53 -5
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +73 -3
- package/dist/proxy.js.map +1 -1
- package/dist/route.d.ts.map +1 -1
- package/dist/route.js +11 -3
- package/dist/route.js.map +1 -1
- package/dist/server/ingest.d.ts +33 -2
- package/dist/server/ingest.d.ts.map +1 -1
- package/dist/server/ingest.js +61 -7
- package/dist/server/ingest.js.map +1 -1
- package/dist/server/queries.d.ts +16 -0
- package/dist/server/queries.d.ts.map +1 -1
- package/dist/server/queries.js +80 -16
- package/dist/server/queries.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +4 -2
- package/dist/session.js.map +1 -1
- package/docs/SETUP.md +308 -51
- package/package.json +14 -7
package/README.md
CHANGED
|
@@ -13,6 +13,32 @@ one httpOnly UUID cookie per session, no fingerprinting, no PII.
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## Installing with a coding agent
|
|
17
|
+
|
|
18
|
+
Polishd ships a machine-readable install procedure. If you use Claude Code,
|
|
19
|
+
Cursor, Copilot, or any other coding agent, **paste this prompt** and let it do
|
|
20
|
+
the whole install:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Install @polishd/next into this Next.js app.
|
|
24
|
+
|
|
25
|
+
Follow the procedure in AGENTS.md from the package exactly — after installing,
|
|
26
|
+
read node_modules/@polishd/next/AGENTS.md. If the package isn't installed yet,
|
|
27
|
+
read it at https://github.com/muskanvaswan/polishd/blob/main/AGENTS.md
|
|
28
|
+
|
|
29
|
+
Work through its phases in order and do not skip Phase 7 (verification).
|
|
30
|
+
Several of its constraints fail silently at runtime rather than at build time,
|
|
31
|
+
so a passing build does not mean the install worked.
|
|
32
|
+
|
|
33
|
+
Finish by running `npx @polishd/next doctor` and fixing everything it reports.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`npx @polishd/next doctor` is the ground truth: it checks the wiring that fails
|
|
37
|
+
quietly — the proxy named for the wrong Next major, route segment config
|
|
38
|
+
re-exported instead of declared, a matcher that isn't an inline literal, the
|
|
39
|
+
dashboard stylesheet not imported. Add `--url http://localhost:3000` to also
|
|
40
|
+
verify live that the session cookie is minted and events actually store.
|
|
41
|
+
|
|
16
42
|
## Documentation
|
|
17
43
|
|
|
18
44
|
| | |
|
|
@@ -20,27 +46,36 @@ one httpOnly UUID cookie per session, no fingerprinting, no PII.
|
|
|
20
46
|
| **[docs/SETUP.md](./docs/SETUP.md)** | Full walkthrough — every variant, verification, troubleshooting |
|
|
21
47
|
| **[AGENTS.md](./AGENTS.md)** | Install procedure written for AI coding agents |
|
|
22
48
|
| **[DATABASE.md](./DATABASE.md)** | Production database setup, schema, retention |
|
|
49
|
+
| **[CHANGELOG.md](./CHANGELOG.md)** | Release notes — **start here if you're upgrading from 0.1.x** |
|
|
23
50
|
|
|
24
51
|
## Requirements
|
|
25
52
|
|
|
26
53
|
- Next.js **15+**, App Router (no Pages Router support)
|
|
27
54
|
- Node **22.5+** (the dev store uses the built-in `node:sqlite`)
|
|
28
|
-
- Tailwind CSS v3 or v4, if you want the dashboard styled — see [Styling](#styling-required)
|
|
29
55
|
- Postgres in production; nothing extra for local development
|
|
30
56
|
|
|
31
57
|
## Quickstart
|
|
32
58
|
|
|
33
59
|
```bash
|
|
34
60
|
npm install @polishd/next
|
|
35
|
-
npx polishd init
|
|
36
|
-
npm run dev
|
|
61
|
+
npx @polishd/next init
|
|
62
|
+
npm run dev # then visit /polishd
|
|
63
|
+
npx @polishd/next doctor # confirm the wiring
|
|
37
64
|
```
|
|
38
65
|
|
|
66
|
+
(`npx polishd init` is the shorter form once the package is installed;
|
|
67
|
+
`npx @polishd/next init` also works before it is.)
|
|
68
|
+
|
|
39
69
|
That's it locally — events write to `.polishd/analytics.db` with no
|
|
40
|
-
configuration. `init` detects your layout (`src/` or root),
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
70
|
+
configuration. `init` detects your layout (`src/` or root), detects your
|
|
71
|
+
installed Next major to name the proxy file correctly, writes the four files
|
|
72
|
+
below, and adds `.polishd/` to `.gitignore`.
|
|
73
|
+
|
|
74
|
+
Files it already wrote are skipped unless you pass `--force`, which backs them
|
|
75
|
+
up to `.bak` first. **Files it did not write are never overwritten**, with or
|
|
76
|
+
without `--force` — if you already have a proxy, it prints how to merge instead
|
|
77
|
+
of clobbering your code. Other flags: `--dry-run` to preview, `--js` for
|
|
78
|
+
JavaScript, `--config` to also emit a `polishd.config.ts`.
|
|
44
79
|
|
|
45
80
|
`pg` installs automatically as an optional dependency and is loaded only when
|
|
46
81
|
you point Polishd at a Postgres URL.
|
|
@@ -70,11 +105,12 @@ export const runtime = "nodejs"; // must be declared here, not re-exporte
|
|
|
70
105
|
export const dynamic = "force-dynamic";
|
|
71
106
|
export const POST = createPolishdRoute();
|
|
72
107
|
|
|
73
|
-
// src/app/polishd/page.tsx — the dashboard
|
|
108
|
+
// src/app/polishd/page.tsx — the dashboard
|
|
109
|
+
import "@polishd/next/dashboard.css"; // self-contained; no Tailwind needed
|
|
74
110
|
import { createPolishdPage } from "@polishd/next/dashboard";
|
|
75
111
|
export const runtime = "nodejs"; // Next requires these inline in the page
|
|
76
112
|
export const dynamic = "force-dynamic";
|
|
77
|
-
export default createPolishdPage();
|
|
113
|
+
export default createPolishdPage(); // see Protecting the dashboard
|
|
78
114
|
```
|
|
79
115
|
|
|
80
116
|
> **Route segment config can't be re-exported.** Next statically parses
|
|
@@ -88,35 +124,127 @@ export default createPolishdPage();
|
|
|
88
124
|
> under Next 16 + Turbopack, a proxy matcher that touches any `/api/*` route
|
|
89
125
|
> breaks resolution for the entire `/api` segment.
|
|
90
126
|
|
|
91
|
-
##
|
|
127
|
+
## Composing with an existing proxy
|
|
92
128
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
129
|
+
Next allows exactly **one** proxy module per app (`proxy.ts` on Next 16+,
|
|
130
|
+
`middleware.ts` on Next 15), so if you already have one, Polishd has to go
|
|
131
|
+
inside it. `init` detects this and prints instructions rather than overwriting.
|
|
96
132
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
133
|
+
The subtle part is the matcher. One file means one `config.matcher`, so adding
|
|
134
|
+
Polishd means widening it to the union — and Polishd's matcher is near-total.
|
|
135
|
+
Your handler, which you deliberately scoped to a couple of routes, would
|
|
136
|
+
silently start running on every request in the app. Nothing errors; your
|
|
137
|
+
redirects and rewrites just begin firing where they never did before.
|
|
138
|
+
|
|
139
|
+
Declare your original scope and `composePolishd` re-applies it as an internal
|
|
140
|
+
guard:
|
|
102
141
|
|
|
103
142
|
```ts
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
143
|
+
// proxy.ts (middleware.ts on Next 15, with the function named `middleware`)
|
|
144
|
+
import { composePolishd } from "@polishd/next/proxy";
|
|
145
|
+
import type { NextRequest } from "next/server";
|
|
146
|
+
|
|
147
|
+
function myProxy(request: NextRequest) {
|
|
148
|
+
if (!hasSession(request)) return NextResponse.redirect(new URL("/login", request.url));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Your handler still only runs on /admin/*, even though the matcher below
|
|
152
|
+
// now feeds Polishd every page request.
|
|
153
|
+
export const proxy = composePolishd(myProxy, { matcher: "/admin/:path*" });
|
|
154
|
+
|
|
155
|
+
// Written by hand: Next statically parses this and it must be an inline
|
|
156
|
+
// literal, so no helper can generate it. The union of your paths and Polishd's.
|
|
157
|
+
export const config = {
|
|
158
|
+
matcher: ["/((?!api|_next/static|_next/image|favicon|assets).*)"],
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Your handler runs first and its response is threaded through, so a redirect or
|
|
163
|
+
rewrite you return **carries the analytics cookie** rather than losing it.
|
|
164
|
+
Returning nothing is fine. `matcher` accepts Next's matcher strings
|
|
165
|
+
(`"/admin/:path*"`, `"/((?!api).*)"`) or a `RegExp` when you want to be exact;
|
|
166
|
+
omit it only if your handler genuinely did run on everything.
|
|
167
|
+
|
|
168
|
+
Widening the matcher is safe from Polishd's side: `withPolishdSession` returns
|
|
169
|
+
early once the cookie is present, so the extra paths cost a cookie lookup and
|
|
170
|
+
nothing else.
|
|
171
|
+
|
|
172
|
+
## Styling
|
|
173
|
+
|
|
174
|
+
The dashboard ships its own stylesheet. One import, no configuration:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
// app/polishd/page.tsx — `init` writes this for you
|
|
178
|
+
import "@polishd/next/dashboard.css";
|
|
109
179
|
```
|
|
110
180
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
181
|
+
**Tailwind is not required in your app.** The stylesheet is compiled at publish
|
|
182
|
+
time, so it doesn't depend on your build setup, your Tailwind version, or
|
|
183
|
+
whether you use Tailwind at all.
|
|
184
|
+
|
|
185
|
+
It is built not to interfere with the app it lands in:
|
|
186
|
+
|
|
187
|
+
- **No global preflight.** Tailwind's reset is what would restyle every element
|
|
188
|
+
on your page; it's left out. A scoped equivalent applies to the dashboard's
|
|
189
|
+
own subtree instead, so your headings, lists and buttons are untouched.
|
|
190
|
+
- **Every rule is scoped to `.polishd-root`**, verified at build time — nothing
|
|
191
|
+
in the file can match your markup.
|
|
192
|
+
- **The scope is doubled (`.polishd-root.polishd-root`)** so the dashboard wins
|
|
193
|
+
specificity ties against a host that happens to have its own `.flex`,
|
|
194
|
+
`.hidden` or `.container`, rather than the winner depending on stylesheet
|
|
195
|
+
order.
|
|
196
|
+
- **No cascade layers**, because unlayered host CSS beats layered CSS outright —
|
|
197
|
+
a plain `h1 { color: … }` in your app would otherwise override the dashboard's
|
|
198
|
+
own typography.
|
|
199
|
+
|
|
200
|
+
The one thing it can't win is a host `!important` rule aimed at the same
|
|
201
|
+
property. Nothing short of `!important` on our side would, and a package
|
|
202
|
+
shouting over its host is worse than a rare collision.
|
|
203
|
+
|
|
204
|
+
> **Upgrading from 0.1.x?** Nothing to do — your existing `@source
|
|
205
|
+
> "…/@polishd/next/dist"` keeps compiling the dashboard's utilities exactly as
|
|
206
|
+
> before, so the dashboard stays styled whether or not you add the import.
|
|
207
|
+
>
|
|
208
|
+
> To move over at your own pace, **add the import first, then** delete the
|
|
209
|
+
> `@source` line — in that order, so the dashboard is never unstyled in between.
|
|
210
|
+
> `polishd doctor` reports which setup it finds and won't call a working 0.1.x
|
|
211
|
+
> install broken.
|
|
114
212
|
|
|
115
213
|
## Protecting the dashboard
|
|
116
214
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
215
|
+
The dashboard is not a read-only report. Its settings change the configured
|
|
216
|
+
model and **API base URL**, so anyone who can reach it can point your requests
|
|
217
|
+
at their own endpoint and spend your key. Treat it as a credential-adjacent
|
|
218
|
+
surface.
|
|
219
|
+
|
|
220
|
+
In **development** it is open and says so once in the console. In
|
|
221
|
+
**production** Polishd refuses to serve it until you have made a decision.
|
|
222
|
+
There are three ways to make one.
|
|
223
|
+
|
|
224
|
+
### 1. A token — no code, works with no auth of your own
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
POLISHD_DASHBOARD_TOKEN=$(openssl rand -hex 32)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
That is the whole integration. The locked screen shows a password field; the
|
|
231
|
+
token is exchanged for an httpOnly, path-scoped cookie by a server action, so
|
|
232
|
+
it never appears in a URL, in browser history, in server logs, or in a
|
|
233
|
+
`Referer` header. The cookie holds an HMAC of the token rather than the token
|
|
234
|
+
itself, comparison is constant-time, expiry is enforced server-side, and the
|
|
235
|
+
unlock form is rate-limited per IP.
|
|
236
|
+
|
|
237
|
+
Tune it if you moved the dashboard off `/polishd`:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { createPolishdPage, polishdTokenAuth } from "@polishd/next/dashboard";
|
|
241
|
+
|
|
242
|
+
export default createPolishdPage({
|
|
243
|
+
authenticate: polishdTokenAuth({ basePath: "/admin/polishd" }),
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 2. Your app's own auth
|
|
120
248
|
|
|
121
249
|
```tsx
|
|
122
250
|
import { createPolishdPage } from "@polishd/next/dashboard";
|
|
@@ -127,21 +255,37 @@ export const runtime = "nodejs";
|
|
|
127
255
|
export const dynamic = "force-dynamic";
|
|
128
256
|
|
|
129
257
|
export default createPolishdPage({
|
|
130
|
-
authenticate: isAuthenticated,
|
|
131
|
-
unauthorized: <MyLogin />,
|
|
258
|
+
authenticate: isAuthenticated, // (ctx) => boolean | Promise<boolean>
|
|
259
|
+
unauthorized: <MyLogin />, // optional; a minimal screen otherwise
|
|
132
260
|
});
|
|
133
261
|
```
|
|
134
262
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
263
|
+
`authenticate` receives the request's `cookies` and `headers`, so it is an
|
|
264
|
+
ordinary function of its inputs and can be unit tested without a live request:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const authenticate = (ctx) => ctx.cookies.get("session")?.value === expected;
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
A zero-argument callback still works unchanged.
|
|
271
|
+
|
|
272
|
+
### 3. Public on purpose
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
POLISHD_DASHBOARD_PUBLIC=true
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
An explicit, grep-able opt-in. You will still get one console warning per boot,
|
|
279
|
+
in production, because that is the only place it is worth acting on.
|
|
141
280
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
281
|
+
> **The gate covers the server actions, not just the page.** The AI actions
|
|
282
|
+
> (generate summary, save settings, scan codebase, file GitHub issues) are
|
|
283
|
+
> independently addressable POST endpoints whose ids ship in the public client
|
|
284
|
+
> bundle, so gating only the page would leave them callable by anyone. They
|
|
285
|
+
> re-run your check against the caller's cookies on every call, and deny when
|
|
286
|
+
> no dashboard page has registered a policy — including when you delete
|
|
287
|
+
> `app/polishd/page.tsx` but keep the package, in which case the actions stay
|
|
288
|
+
> routable and always deny.
|
|
145
289
|
|
|
146
290
|
## AI summary
|
|
147
291
|
|
|
@@ -236,8 +380,7 @@ import { PolishdMonitor } from "@polishd/next/client";
|
|
|
236
380
|
|
|
237
381
|
## Configuration
|
|
238
382
|
|
|
239
|
-
Defaults suit a low-traffic site. Override via `polishd.config.ts
|
|
240
|
-
`initPolishd`:
|
|
383
|
+
Defaults suit a low-traffic site. Override via `polishd.config.ts`:
|
|
241
384
|
|
|
242
385
|
```ts
|
|
243
386
|
// polishd.config.ts
|
|
@@ -248,11 +391,32 @@ export default definePolishdConfig({
|
|
|
248
391
|
});
|
|
249
392
|
```
|
|
250
393
|
|
|
394
|
+
The dashboard never measures itself: nothing on `/polishd` (or below it) is
|
|
395
|
+
captured, so reading your analytics can't show up as your site's most-used
|
|
396
|
+
feature. Moved the dashboard elsewhere? Set `dashboardRoute`.
|
|
397
|
+
|
|
398
|
+
> **Nothing imports this file for you.** It cannot be auto-loaded: the three
|
|
399
|
+
> places that read config run in three different runtimes — the browser, the
|
|
400
|
+
> Edge proxy, and the Node route handler — and the Edge one has no filesystem.
|
|
401
|
+
> Pass it to each explicitly:
|
|
402
|
+
|
|
251
403
|
```ts
|
|
252
|
-
// src/instrumentation-client.ts
|
|
253
|
-
import { initPolishd } from "@polishd/next/client";
|
|
254
404
|
import config from "../polishd.config";
|
|
255
|
-
|
|
405
|
+
|
|
406
|
+
initPolishd(config); // instrumentation-client.ts
|
|
407
|
+
export const POST = createPolishdRoute(config); // app/api/polishd/route.ts
|
|
408
|
+
const { proxy } = createPolishdProxy(config); // proxy.ts
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Thread it into one but not another and the layers disagree silently — a proxy
|
|
412
|
+
minting cookie `a` while ingest looks for `b` produces exactly the empty
|
|
413
|
+
dashboard described below.
|
|
414
|
+
|
|
415
|
+
For **`sessionCookie` specifically, prefer the environment variable**. It is the
|
|
416
|
+
one channel every runtime can read, so it cannot go half-applied:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
POLISHD_SESSION_COOKIE=my_session
|
|
256
420
|
```
|
|
257
421
|
|
|
258
422
|
## Environment variables
|
|
@@ -261,6 +425,9 @@ initPolishd(config);
|
|
|
261
425
|
|---|---|---|
|
|
262
426
|
| `POLISHD_DATABASE_URL` | production | Pooled Postgres connection string — enables capture |
|
|
263
427
|
| `POLISHD_DB_PATH` | dev (optional) | Custom SQLite path (default `.polishd/analytics.db`) |
|
|
428
|
+
| `POLISHD_DASHBOARD_TOKEN` | production | Protects `/polishd` with the built-in gate. Generate with `openssl rand -hex 32` |
|
|
429
|
+
| `POLISHD_DASHBOARD_PUBLIC` | production (optional) | `true` — serve the dashboard unguarded on purpose |
|
|
430
|
+
| `POLISHD_SESSION_COOKIE` | optional | Rename the anonymous session cookie. Read by both the proxy and ingest, so they cannot disagree |
|
|
264
431
|
| `POLISHD_AI_PROVIDER` | AI (optional) | `anthropic` \| `openai` \| `openai-compatible` \| `google` |
|
|
265
432
|
| `POLISHD_AI_MODEL` | AI (optional) | Model id (defaults per provider, e.g. `claude-opus-4-8`) |
|
|
266
433
|
| `POLISHD_AI_API_KEY` | AI (optional) | Model API key — preset instead of using the dashboard |
|
|
@@ -280,16 +447,39 @@ Local dev needs nothing. If no writable store is available the package degrades
|
|
|
280
447
|
to a safe no-op (a console warning, dashboard notice) — your app never breaks.
|
|
281
448
|
See [DATABASE.md](./DATABASE.md) for production setup.
|
|
282
449
|
|
|
450
|
+
## Troubleshooting
|
|
451
|
+
|
|
452
|
+
Run `npx @polishd/next doctor` first — it catches most of these by name, and
|
|
453
|
+
`--url http://localhost:3000` checks the live path as well.
|
|
454
|
+
|
|
455
|
+
| Symptom | Cause | Fix |
|
|
456
|
+
|---|---|---|
|
|
457
|
+
| Dashboard empty, beacons all return 200 | The proxy isn't running, so no session cookie is minted and ingest drops every batch | Check the file is named for your Next major — `proxy.ts` on 16+, `middleware.ts` on 15 — and exports a function of the same name. The dashboard shows a banner when it detects this |
|
|
458
|
+
| Every `/api/*` route 404s | The proxy matcher touches `/api` | Restore the `(?!api…)` exclusion. Under Next 16 + Turbopack this breaks the whole `/api` segment |
|
|
459
|
+
| Build fails: *"can't recognize the exported `runtime` field"* | Route segment config re-exported | Declare `runtime`/`dynamic` inline in the module |
|
|
460
|
+
| Dashboard renders unstyled | `@polishd/next/dashboard.css` isn't imported | See [Styling](#styling) |
|
|
461
|
+
| Dashboard content cut off below the fold | A host `body` that doesn't scroll | Fixed by default; if an ancestor sets `transform`/`filter`/`contain`, pass `shell: false` and see [docs/SETUP.md](./docs/SETUP.md) |
|
|
462
|
+
| `ExperimentalWarning: SQLite is an experimental feature` | Node's built-in `node:sqlite` | **Expected and harmless.** Node prints this for its own SQLite module on 22.x. Not a sign the package is unstable, and it disappears in production where Postgres is used |
|
|
463
|
+
| `ERR_UNKNOWN_BUILTIN_MODULE: node:sqlite` | Node < 22.5 | Upgrade Node, or configure Postgres |
|
|
464
|
+
| Server actions fail with an opaque digest | No dashboard page registered a policy | Expected if you deleted `app/polishd/page.tsx` — the actions fail closed by design |
|
|
465
|
+
|
|
283
466
|
## Entry points
|
|
284
467
|
|
|
285
468
|
| Import | Contents |
|
|
286
469
|
|---|---|
|
|
287
|
-
| `@polishd/next` | config, `definePolishdConfig`, event types (isomorphic) |
|
|
470
|
+
| `@polishd/next` | config, `definePolishdConfig`, `resolveSessionCookie`, event types (isomorphic) |
|
|
288
471
|
| `@polishd/next/client` | `initPolishd`, `PolishdMonitor` |
|
|
289
472
|
| `@polishd/next/server` | store, ingest, queries, `withPolishdSession` (Node only) |
|
|
290
473
|
| `@polishd/next/route` | `POST`, `createPolishdRoute` |
|
|
291
|
-
| `@polishd/next/proxy` | `proxy`, `config`, `withPolishdSession`, `polishdMatcher` |
|
|
292
|
-
| `@polishd/next/dashboard` | `createPolishdPage`, `PolishdDashboard`, `loadPolishdDashboardData` |
|
|
474
|
+
| `@polishd/next/proxy` | `proxy`, `config`, `withPolishdSession`, `polishdMatcher`, `composePolishd` |
|
|
475
|
+
| `@polishd/next/dashboard` | `createPolishdPage`, `polishdTokenAuth`, `PolishdDashboard`, `loadPolishdDashboardData` |
|
|
476
|
+
|
|
477
|
+
## CLI
|
|
478
|
+
|
|
479
|
+
| Command | Purpose |
|
|
480
|
+
|---|---|
|
|
481
|
+
| `npx @polishd/next init` | Scaffold the glue files. `--dry-run`, `--force`, `--js`, `--config` |
|
|
482
|
+
| `npx @polishd/next doctor` | Check an install. `--url <origin>` adds live checks |
|
|
293
483
|
|
|
294
484
|
## Development
|
|
295
485
|
|