@patchstack/connect 0.5.4 → 0.5.6
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/AGENT-INSTALL.md +5 -5
- package/README.md +6 -5
- package/dist/cli.js +239 -140
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +55 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +54 -20
- package/dist/index.js.map +1 -1
- package/dist/protect.cjs +88 -57
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.js +1 -1
- package/dist/{refresh-manifest-K3RT4XNH.js → refresh-manifest-2ALIJGZ3.js} +57 -26
- package/dist/refresh-manifest-2ALIJGZ3.js.map +1 -0
- package/package.json +1 -1
- package/dist/refresh-manifest-K3RT4XNH.js.map +0 -1
package/AGENT-INSTALL.md
CHANGED
|
@@ -17,7 +17,7 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
17
17
|
| `guide` | Print this project's live setup status (done/missing, with tailored commands), then the full guide. `--full` prints it even when setup is complete. | No | Nothing | Nothing |
|
|
18
18
|
| `status` | Re-print the site UUID + dashboard URL and check whether the site still exists (active / removed / could not verify). | No | Nothing | Site-existence check |
|
|
19
19
|
| `init <site-uuid>` | Optional: pre-seed `.patchstackrc.json` with an existing UUID. | No | `.patchstackrc.json` only | Nothing |
|
|
20
|
-
| `mark-build` |
|
|
20
|
+
| `mark-build` | Ensure the widget tag in built pages, and — **on a production build only** — stamp the live-site flag + build fingerprint. A local or preview build is stamped with neither, and has a stale marker removed; `--production` forces it for a build published by hand. Run as a `postbuild` step. | No | Build output only (`dist/ build/ out/ .output/public/ _site/`) — never source | Nothing |
|
|
21
21
|
| `claim` | Attach the site to a Patchstack account from the terminal: print a link the user opens to sign in (or sign up) and poll (10 min). Whoever approves becomes the owner. Does **not** rotate the credential. Same result as opening the dashboard link `scan` prints. Not usable in CI. | No | Nothing, unless the server issues a credential for a checkout that had none — then `.patchstackrc.local.json` | Device-code request + approval poll |
|
|
22
22
|
| `login` | Recover a lost credential for an existing site: print an owner-approval link and poll (10 min). Approving **rotates** the credential. Not usable in CI. | No | New credential into `.patchstackrc.local.json` on approval | Device-code request + approval poll |
|
|
23
23
|
| `uninstall` | Signal Patchstack that the package is being removed: an unclaimed record is deleted, a claimed one is flagged. Does **not** touch local files. | No | Nothing local | Removal signal |
|
|
@@ -88,7 +88,7 @@ This is a request, not a mechanism: nothing in the install depends on it. Do it
|
|
|
88
88
|
|
|
89
89
|
**If the request you were given includes a claim token**, pass it exactly as given: `npx @patchstack/connect setup --claim-token <token>`. The site is then created in the person's own Patchstack account and `setup` prints their dashboard link for it — there is no sign-in step to relay. The token comes only from the person's Patchstack dashboard; never invent one, never write it to a file, never print it back. If `setup` reports that the token had expired or was not recognised, the site is not connected: hand over the dashboard link it prints instead, and tell the person they can copy a fresh prompt from the dashboard.
|
|
90
90
|
|
|
91
|
-
In a hosted builder, run setup with `PATCHSTACK_ENVIRONMENT=sandbox` scoped to the workspace process/command, ensure the CLI's on-disk edits are adopted into the platform's persisted project state, then restart any already-running preview/server process so it loads the guard. Do not persist `"environment": "sandbox"` in `.patchstackrc.json`: deployed builds use the same committed files and report `production` only when the platform's own production signal says so (Vercel, Netlify, Render, Railway); a preview there reports `sandbox`. A scan on a developer's machine, in a generic CI runner, or on a platform with no such signal reports `local` on its own, and the dashboard shows that app as configured, not deployed, until its build is seen live. A client-only SPA or a static site generator has no server request path to guard: `setup` says runtime protection does not apply and installs nothing for it; never call such a project protected.
|
|
91
|
+
In a hosted builder, run setup with `PATCHSTACK_ENVIRONMENT=sandbox` scoped to the workspace process/command, ensure the CLI's on-disk edits are adopted into the platform's persisted project state, then restart any already-running preview/server process so it loads the guard. Do not persist `"environment": "sandbox"` in `.patchstackrc.json`: deployed builds use the same committed files and report `production` only when the platform's own production signal says so (Vercel, Netlify, Render, Railway); a preview there reports `sandbox`. A build in a project the builder generated and builds for itself (Lovable, Replit) reports `production` without an override, because the edit preview is a dev server and a build is the publish step — which is exactly why the sandbox label belongs in the workspace process and not in a file. A scan on a developer's machine, in a generic CI runner, or on a platform with no such signal reports `local` on its own, and the dashboard shows that app as configured, not deployed, until its build is seen live. A client-only SPA or a static site generator has no server request path to guard: `setup` says runtime protection does not apply and installs nothing for it; never call such a project protected.
|
|
92
92
|
|
|
93
93
|
**Finish by telling the user to refresh their preview.** The widget's "Report a vulnerability" button loads with the page, so a preview that was already open still shows the HTML from before setup — the button is missing there until it reloads. Nothing in the CLI can reach the user's browser, so relaying this is your job. Phrase it as a check rather than a required step: a builder that hot reloads, or a preview server you restarted, may have refreshed it already.
|
|
94
94
|
|
|
@@ -120,7 +120,7 @@ This is a request, not a mechanism: nothing in the install depends on it. Do it
|
|
|
120
120
|
|
|
121
121
|
**Bun-managed projects:** `bun run` does not execute npm-style `pre`/`post` scripts, so wire the build script directly instead: `"build": "patchstack-connect scan && <existing build command> && patchstack-connect mark-build"`.
|
|
122
122
|
|
|
123
|
-
3. **Verify the disclosure widget** — a floating "Report a vulnerability" button. `scan` installs it automatically into a plain HTML shell, and `mark-build` carries it into built HTML. Only when `scan` reported that it found no editable shell
|
|
123
|
+
3. **Verify the disclosure widget** — a floating "Report a vulnerability" button. `scan` installs it automatically into a plain HTML shell **or a JSX root** (Next, Remix, React Router, TanStack Start, Gatsby), and `mark-build` carries it into built HTML. Only when `scan` reported that it found no editable shell at all — a root whose head mechanism is not a plain script tag, e.g. Nuxt's `useHead` or an Astro layout — add the one-liner it printed to the root layout yourself, just before `</body>` (never a JS entry point), reading `siteUuid` from `.patchstackrc.json`. On those same roots the widget also needs the production marker above the tag — `scan` adds it automatically to a JSX root, and prints it to paste when it finds no anchor. A server-rendered site without the marker serves the build-mode claim flow to its visitors:
|
|
124
124
|
|
|
125
125
|
```html
|
|
126
126
|
<script src="https://cdn.patchstack.com/patchstack-widget.js" data-site-uuid="<SITE_UUID>" defer></script>
|
|
@@ -194,7 +194,7 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
194
194
|
- Never invent or guess a UUID — the scan provisions it, the widget silently no-ops on a fake one.
|
|
195
195
|
- Never invent or guess a claim token either. One is only ever handed to you by the person, from their own Patchstack dashboard; pass it with `--claim-token` (or `PATCHSTACK_CLAIM_TOKEN`) and nowhere else — not into `.patchstackrc.json`, not into a committed file, not into your reply.
|
|
196
196
|
- The CLI never opens the dashboard link and never asks for Patchstack credentials.
|
|
197
|
-
- Label hosted workspace scans with `PATCHSTACK_ENVIRONMENT=sandbox` in that process only. Leave production builds unset (a platform's own production signal makes the build report `production`; a developer machine or a generic CI runner reports `local`) and never commit a sandbox label into files shared with production.
|
|
197
|
+
- Label hosted workspace scans with `PATCHSTACK_ENVIRONMENT=sandbox` in that process only. Leave production builds unset (a platform's own production signal, or the hosted builder the project belongs to, makes the build report `production`; a developer machine or a generic CI runner reports `local`) and never commit a sandbox label into files shared with production.
|
|
198
198
|
- If a step fails, stop and report it. Don't proceed with placeholders.
|
|
199
199
|
- CI never has the credential in a file: `.patchstackrc.local.json` is git-ignored by design, so set `PATCHSTACK_API_KEY` as an env var there (and `PATCHSTACK_SITE_UUID` too where `.patchstackrc.json` is also absent). Precedence for the site UUID and settings: CLI flag → env var → `.patchstackrc.json`. For the API key: env var → `.patchstackrc.local.json` → `.patchstackrc.json` (where installs made before the split still hold it). `login` is interactive and refuses to run in CI, so CI always takes its credential from the environment.
|
|
200
200
|
|
|
@@ -605,7 +605,7 @@ Remove only the pieces that are actually present — check for each first. If no
|
|
|
605
605
|
5. **Signal Patchstack that the package is being removed**: run `npx @patchstack/connect uninstall` (while the package is still installed and `.patchstackrc.json` still exists). If the site was never claimed, this deletes its anonymous record on Patchstack; if the site is claimed, it is only flagged — the record stays until its owner removes it in the dashboard. A failed signal must not stop the uninstall; continue with the remaining steps.
|
|
606
606
|
6. **Uninstall the package** with the manager matching the lockfile: `npm uninstall` / `pnpm remove` / `yarn remove` / `bun remove` `@patchstack/connect`. Don't hand-edit `node_modules` or the lockfile.
|
|
607
607
|
7. **Delete `.patchstackrc.json` and `.patchstackrc.local.json`** (the second holds the API key and is git-ignored, so it is present locally even when the repo shows nothing), remove the `.gitignore` entry setup added for it, and remove `PATCHSTACK_SITE_UUID`, `PATCHSTACK_API_KEY` (and public-prefixed variants like `NEXT_PUBLIC_PATCHSTACK_SITE_UUID`) from env files and CI variables.
|
|
608
|
-
8. **Commit** the changes. Reporting stops immediately. On HTML shells the `window.__PATCHSTACK_PROD__` flag that `mark-build` stamped lives only in build output — the next build simply won't contain it (rebuild if build output is committed). On JSX roots `scan` wrote the same marker into source; remove that managed `#region patchstack` block (or the hand-pasted equivalent) with the widget tag in step 2.
|
|
608
|
+
8. **Commit** the changes. Reporting stops immediately. On HTML shells the `window.__PATCHSTACK_PROD__` flag that `mark-build` stamped on production builds lives only in build output — the next build simply won't contain it (rebuild if build output is committed). On JSX roots `scan` wrote the same marker into source; remove that managed `#region patchstack` block (or the hand-pasted equivalent) with the widget tag in step 2.
|
|
609
609
|
|
|
610
610
|
The `uninstall` signal is the only account-side effect local removal can have: it deletes an *unclaimed* (anonymous) record and merely flags a *claimed* one. A claimed site keeps using a site slot until its owner removes it in the dashboard at https://app.patchstack.com — end your report by telling the user this, alongside the site UUID from step 1. Never attempt to authenticate or remove a claimed site on the user's behalf.
|
|
611
611
|
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ patchstack-connect setup [options] Run scan, manage the widget,
|
|
|
63
63
|
patchstack-connect init <site-uuid> Optional: pre-seed .patchstackrc.json with
|
|
64
64
|
an existing site UUID
|
|
65
65
|
patchstack-connect status [options] Show current configuration
|
|
66
|
-
patchstack-connect mark-build [
|
|
66
|
+
patchstack-connect mark-build [--production] Stamp built HTML with a production flag +
|
|
67
67
|
build fingerprint and ensure the widget tag
|
|
68
68
|
in built pages (run as a postbuild step)
|
|
69
69
|
patchstack-connect guide Show this project's setup status (what's done,
|
|
@@ -208,7 +208,7 @@ Environment variables:
|
|
|
208
208
|
- `PATCHSTACK_SITE_UUID` — the site UUID from your Patchstack dashboard
|
|
209
209
|
- `PATCHSTACK_ENDPOINT` — override the API endpoint (default `https://api.patchstack.com/monitor/pulse/manifest`)
|
|
210
210
|
- `PATCHSTACK_TIMEOUT_MS` — request timeout in milliseconds (default `30000`)
|
|
211
|
-
- `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the hosting platform's own production/preview signal (Vercel, Netlify, Render, Railway); a preview reports `sandbox
|
|
211
|
+
- `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the hosting platform's own production/preview signal (Vercel, Netlify, Render, Railway); a preview reports `sandbox`. Failing that, a project a hosted builder generated and builds for itself (Lovable, Replit) reports `production`, because on those platforms the edit preview is a dev server and a build only happens when the owner publishes. Anything else — a developer machine, a generic CI runner, a platform this does not know — reports `local`
|
|
212
212
|
- `PATCHSTACK_CLAIM_TOKEN` — connect the site straight to your account (see *Connecting straight to your account*)
|
|
213
213
|
|
|
214
214
|
Two files, because one value is public and the other is not.
|
|
@@ -257,7 +257,7 @@ The token names your account, not the project: it is never written to `.patchsta
|
|
|
257
257
|
|
|
258
258
|
### Sandbox and production manifests
|
|
259
259
|
|
|
260
|
-
Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the hosting platform's own answer to "is this the production deployment?": Vercel's `VERCEL_ENV`, Netlify's `CONTEXT`, Render's pull-request flag, Railway's environment name. Production reports `production`; a preview those platforms name as such reports `sandbox`. Everything else reports `local` — a developer's machine, a generic CI runner (`CI=true` proves automation, not deployment), and a platform whose build environment carries no such signal, Cloudflare Pages among them. A local manifest is inventory — it tells Patchstack what the app is built from — and never counts as contact with a live site, so an app that has only been set up on a laptop shows in the dashboard as **Configured locally**, not as connected or deployed; once its build is seen on the live site it reads as deployed regardless of the label. Set `PATCHSTACK_ENVIRONMENT=production` where builds run on a platform this list does not know. Sandboxed builders should set `PATCHSTACK_ENVIRONMENT=sandbox` in the sandbox process only. Patchstack stores and deduplicates manifests per environment, so an iterative workspace scan does not replace the last production manifest.
|
|
260
|
+
Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the hosting platform's own answer to "is this the production deployment?": Vercel's `VERCEL_ENV`, Netlify's `CONTEXT`, Render's pull-request flag, Railway's environment name. Production reports `production`; a preview those platforms name as such reports `sandbox`. Where no platform answers, the project itself gets the last word: one a hosted builder generated and builds for itself — Lovable, Replit — reports `production`, because those platforms set no variable to read and their edit preview is a dev server, so a build running at all is the owner publishing. Everything else reports `local` — a developer's machine, a generic CI runner (`CI=true` proves automation, not deployment), and a platform whose build environment carries no such signal, Cloudflare Pages among them. A local manifest is inventory — it tells Patchstack what the app is built from — and never counts as contact with a live site, so an app that has only been set up on a laptop shows in the dashboard as **Configured locally**, not as connected or deployed; once its build is seen on the live site it reads as deployed regardless of the label. Set `PATCHSTACK_ENVIRONMENT=production` where builds run on a platform this list does not know. Sandboxed builders should set `PATCHSTACK_ENVIRONMENT=sandbox` in the sandbox process only. Patchstack stores and deduplicates manifests per environment, so an iterative workspace scan does not replace the last production manifest.
|
|
261
261
|
|
|
262
262
|
Do not commit `"environment": "sandbox"` to `.patchstackrc.json` when the same files are deployed to production. Scope the variable to the sandbox command/process instead:
|
|
263
263
|
|
|
@@ -265,7 +265,7 @@ Do not commit `"environment": "sandbox"` to `.patchstackrc.json` when the same f
|
|
|
265
265
|
PATCHSTACK_ENVIRONMENT=sandbox npx @patchstack/connect setup
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
-
The generated `prebuild` scan deliberately carries no hard-coded environment. A production build with no override reports `production` only because the platform's own production signal says it is one; a preview on those platforms reports `sandbox` by itself, and a hosted builder's workspace must receive `PATCHSTACK_ENVIRONMENT=sandbox` from its host. Runtime protection itself is not environment-specific: `PATCHSTACK_ENVIRONMENT` labels manifests only. Use `PATCHSTACK_MODE=dry-run` when protection should observe rather than block.
|
|
268
|
+
The generated `prebuild` scan deliberately carries no hard-coded environment. A production build with no override reports `production` only because the platform's own production signal says it is one; a preview on those platforms reports `sandbox` by itself, and a hosted builder's *workspace* must receive `PATCHSTACK_ENVIRONMENT=sandbox` from its host — its published build needs no override, because a build in such a project is the publish step. Runtime protection itself is not environment-specific: `PATCHSTACK_ENVIRONMENT` labels manifests only. Use `PATCHSTACK_MODE=dry-run` when protection should observe rather than block.
|
|
269
269
|
|
|
270
270
|
During a build, the `prebuild` scan removes any previous map stamp. A later `map --upload` in the same pre-bundle lifecycle hashes the map's policy content (excluding analyser timing and memory observations) and records that identity in the guard's existing rules file. The guard presents it on the rules request it already makes; only an explicit Patchstack confirmation naming the same map lets a rule scoped to one of your app's parameter names block. A build with no confirmed identity still enforces every ordinary rule — only scoped rules drop to detect-only, with the reason reported. Outside a pre-bundle hook, `map --upload` changes no file and sends no identity. See "Which build a rule belongs to" in `AGENT-INSTALL.md`.
|
|
271
271
|
|
|
@@ -306,11 +306,12 @@ The widget is a floating "Report a vulnerability" button — a disclosure channe
|
|
|
306
306
|
<script src="https://cdn.patchstack.com/patchstack-widget.js" data-site-uuid="<SITE_UUID>" defer data-patchstack-connect-widget="true"></script>
|
|
307
307
|
```
|
|
308
308
|
|
|
309
|
+
- **`scan`** installs the widget tag into a plain HTML shell, and — where there is none — into a JSX root (`src/routes/__root.tsx`, `app/layout.tsx`, …), just before `</body>`. The same tag serves both: JSX reads `defer` as a boolean attribute and passes `data-*` through. A server-rendered app has no HTML shell at all, so without this its published site carries no widget and Patchstack never hears from the live page.
|
|
309
310
|
- **`scan`** also adds the production marker when the root shell is JSX rather than HTML (`src/routes/__root.tsx`, `app/layout.tsx`, …), above the widget tag and guarded by the framework's production expression. A server-rendered app emits no built HTML for `mark-build` to stamp, so without it the widget reads the published site as build mode and shows the claim flow to visitors instead of the report form.
|
|
310
311
|
|
|
311
312
|
Re-runs update the tag in place (the `data-patchstack-connect-widget` attribute marks it as connector-managed); a pre-existing manual widget tag is left untouched. `--dry-run` never edits anything; a failed post still skips the widget tag (it needs the site UUID) but the production marker may already have been written, since it runs before the post. Projects whose root layout is code rather than HTML (Next.js, Nuxt, Astro, …) get the exact snippet and target file printed instead — `guide` shows framework-specific placement.
|
|
312
313
|
|
|
313
|
-
- **`mark-build`** ensures the same tag in built HTML output, covering builds whose source shell the connector couldn't edit, and stamps `window.__PATCHSTACK_PROD__` so the widget hides the claim/login UI on the published site (owners reach it by appending `#patchstack` to the live URL).
|
|
314
|
+
- **`mark-build`** ensures the same tag in built HTML output, covering builds whose source shell the connector couldn't edit, and stamps `window.__PATCHSTACK_PROD__` so the widget hides the claim/login UI on the published site (owners reach it by appending `#patchstack` to the live URL). The marker says the page is the live site, so **only a production build carries it**: the environment is read the same way `scan` reads it (the hosting platform's own production discriminator, then the hosted builder the project belongs to), and a local or preview build gets the widget tag, no marker, and any marker an earlier build left behind removed. Publishing a static build by hand from your machine is the case that needs `--production` (or `PATCHSTACK_ENVIRONMENT=production`), because nothing in that environment can say the build is a deployment.
|
|
314
315
|
|
|
315
316
|
- **Opting out:** persist `"widget": false` in `.patchstackrc.json` to disable both the widget tag and the production marker (dependency scanning only). Without it, the next successful scan re-adds the managed tag, and the next scan re-adds the marker on a JSX root.
|
|
316
317
|
|