The editor reported nothing when a session died. Evidence lived only in-tab —
an 800-line React array behind a "Show console" button — so diagnosis meant
asking a user to paste a screenshot.
Better Stack's Error Tracking ingests the Sentry wire protocol, so this runs
the stock @sentry/browser against a Better Stack DSN. Sentry.init installs its
own window error/unhandledrejection handlers, so uncaught main-thread errors
and the wasm traps that escape emscripten's DOM event handlers are captured
with no instrumentation at the throw sites. Not their JS tag: it has no
beforeSend or fingerprint hooks, its runtime spawns workers from cross-origin
CDN hosts (this page is COEP: require-corp), and it ships session replay on by
default — which on a CAD canvas records customers' board geometry.
@sentry/browser is imported in exactly one file so the vendor stays swappable,
mirroring how lib/analytics.ts isolates Plausible.
Also replaces the terminal-signature regex with a shared, unit-tested predicate
(wasm/terminal-error.ts) used by BOTH the fatal overlay and the reporter, so
they cannot disagree. The regex was a type check written as a string match and
had three live holes: `RuntimeError` was listed but never appears IN
`.message`; Chrome's bare "unreachable" and "null function" matched nothing
(the v0.1.20 prod log is exactly those); and narrowing "table index is out of
bounds" to `\bindex out of bounds` for Firefox in 197f317 silently stopped
matching Chrome's spelling. Checking the TYPE — every trap in this family is a
WebAssembly.RuntimeError — covers all engines and ends the spelling chase; the
message patterns remain as a fallback for paths that lose the Error object,
such as a worker ErrorEvent crossing the realm boundary with error: null.
197f317's pthread-worker tap, promote() and Firefox findings are kept as-is.
Notes:
- Off unless VITE_ERRORS_DSN is set AND VITE_ALLOW_USER_OVERRIDE !== "1" (dev
servers and every Playwright harness set the latter, and production builds
never do), so a production DSN in a local .env still cannot report. With no
DSN the whole SDK is const-folded out: 1,193,080 vs 1,282,463 bytes of JS.
- browserApiErrors integration removed. It wraps setTimeout/rAF/addEventListener
in try/catch, which is exactly how KiCad-on-Emscripten drives its main loop.
- Console breadcrumbs off (collab/debug.ts's clog fires per Yjs update and would
evict the ring before any crash); dom/fetch/navigation breadcrumbs kept.
- beforeSend redacts token/apiKey/Bearer — collab/provider.ts puts the collab
token in the y-partyserver URL, so a connection-failure string carries a live
credential — and guards the cascade: one wedge produced 8 errors in prod, and
after the first terminal event the rest are dropped into cascade_count.
Verified end to end against the real EU host from a cross-origin-isolated page:
POST /api/<id>/envelope/ -> 200, and 4 terminal throws produce 1 event
(control: 1 throw, same count).
Privacy policy 9, cookie policy 6 and the licenses page are updated: Better
Stack is disclosed as an EU processor, and the licenses page now describes the
browser app's own JS dependencies, which it never did.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
85 lines
4.7 KiB
Shell
85 lines
4.7 KiB
Shell
# Standalone editor (Vite). Copy to standalone/.env. All vars must be VITE_-prefixed.
|
|
|
|
# Backend implementing the @pcbjam/shared contract (the example backend, or any
|
|
# conforming backend). Leave it pointing anywhere unreachable to use only the
|
|
# local-folder loader on the home page.
|
|
VITE_API_BASE_URL=http://localhost:3060
|
|
|
|
# Where PROJECTS come from: "remote" (default — the @pcbjam/shared backend above)
|
|
# or "static" (a read-only example gallery served from a CDN, NO backend — the
|
|
# demo.pcbjam.com mode; editor saves download to local). For "static" you must
|
|
# also set VITE_PROJECT_MANIFEST_URL.
|
|
# VITE_PROJECT_SOURCE=static
|
|
# VITE_PROJECT_MANIFEST_URL=https://cdn.pcbjam.com/content/2.7.7/manifest.json
|
|
|
|
# WASM asset root (no trailing slash). On `dev` the artifacts are symlinked into
|
|
# public/wasm and served by Vite at /wasm (a FLAT layout — files live directly
|
|
# under the root). For a prod CDN set an absolute URL, e.g.:
|
|
# VITE_WASM_ROOT=https://cdn.pcbjam.com/wasm
|
|
# A cross-origin CDN works: boot.ts loads the pthread worker via a same-origin
|
|
# blob shim, and the CDN must send CORP: cross-origin + ACAO (+ COEP/COOP on the
|
|
# app). VITE_WASM_ASSET_BASE_URL is the legacy name and is still honored.
|
|
VITE_WASM_ROOT=/wasm
|
|
|
|
# Per-release WASM manifest file under VITE_WASM_ROOT (e.g. "manifest-2.7.7.json").
|
|
# When set, the editor resolves each tool's versioned, content-addressed folder
|
|
# (VITE_WASM_ROOT/<tool>/<ver>/) from it at runtime (see docs/features/demo-deploy).
|
|
# Leave UNSET for the flat dev layout above.
|
|
# VITE_WASM_MANIFEST=manifest-2.7.7.json
|
|
|
|
# Override the artifact source dir the dev symlink points at (default:
|
|
# <repo>/tests/apps/kicad). Useful when serving prebuilt artifacts from elsewhere.
|
|
# WASM_SRC_DIR=
|
|
|
|
# Marketing / landing page the version badge links to (default https://pcbjam.com).
|
|
# VITE_LANDING_URL=https://pcbjam.com
|
|
|
|
# Companion management app origin. When set (the backed editor.pcbjam.com
|
|
# deploy), non-editor routes — home, project overview, mgmt-only paths —
|
|
# redirect there, preserving path + search (standalone-hardening 0006).
|
|
# Leave UNSET for dev and the demo: every route renders locally.
|
|
# VITE_APP_URL=https://app.pcbjam.com
|
|
|
|
# Honor `?user=` / `?libowner=` identity overrides ("1"). Dev servers and e2e
|
|
# harnesses set this so specs can mint isolated per-run identities; NEVER set
|
|
# it on a production build — the session user from /api/me (or the VITE_USER
|
|
# fallback) is the identity there (collab-presence 0009).
|
|
# VITE_ALLOW_USER_OVERRIDE=1
|
|
|
|
# Where the in-editor waitlist form POSTs (default https://www.pcbjam.com/api/waitlist).
|
|
# The demo is static with no backend, so it cross-posts to the landing site's
|
|
# serverless endpoint, which must send CORS for this origin (see
|
|
# site/src/pages/api/waitlist.ts). Same JSON contract {email, source, company_url}.
|
|
# Use the canonical www host: the apex 308-redirects to www and a CORS preflight
|
|
# can't follow redirects.
|
|
# VITE_WAITLIST_URL=https://www.pcbjam.com/api/waitlist
|
|
|
|
# Plausible analytics. Unset ⇒ no tracking (dev/checkout default). Set the shared
|
|
# site's pa-*.js script URL to enable (the pa- id keys the dashboard, no
|
|
# data-domain). Point it at a self-hosted/proxied copy on cdn.pcbjam.com if the
|
|
# plausible.io script won't load under COEP require-corp.
|
|
# VITE_PLAUSIBLE_SRC=https://plausible.io/js/pa-KjNS9YmidydULZTstsjRg.js
|
|
|
|
# Error tracking (Better Stack). Unset ⇒ nothing is reported (dev/checkout
|
|
# default). Better Stack ingests the Sentry wire protocol, so this is a Sentry-
|
|
# format DSN and the app runs the stock @sentry/browser SDK against their host:
|
|
# https://<application-token>@<ingesting-host>/<application-id>
|
|
# The token is public once it ships in the bundle — that is inherent to browser
|
|
# error reporting, and it is write-only, so the exposure is quota abuse rather
|
|
# than data. Deploys pass it via scripts/deploy/build-{editor,demo}.mjs
|
|
# --errors-dsn; VITE_ERRORS_ENV tags which deploy an error came from.
|
|
# NOTE: reporting also stays off whenever VITE_ALLOW_USER_OVERRIDE=1 (dev servers
|
|
# and e2e harnesses set it), so a production DSN in a local .env still can't
|
|
# pollute the dashboard.
|
|
# VITE_ERRORS_DSN=
|
|
# VITE_ERRORS_ENV=production
|
|
|
|
# Where a backend project's DOCUMENT content lives ("api" default | "ydoc").
|
|
# Same /p/<project> URLs either way. "api": file bytes come from the REST
|
|
# backend and a user save (File->Save in the editor) is uploaded back to it.
|
|
# "ydoc": the collab room (VITE_YJS_PROVIDER) is the source of truth - when the
|
|
# room holds the document it is materialized client-side instead of fetched,
|
|
# and saves stay in the browser (the provider persists the doc); the REST
|
|
# backend still serves project metadata + sibling files, and the file fetch is
|
|
# the first-open fallback that seeds the room.
|
|
# VITE_DOC_SOURCE=ydoc
|