v0.15.0 post-correction: @amplitude/unified + session replay + overlay binding

Mid-Session-L correction to the v0.15.0 release that the dispatched
subagent (Session ξ) shipped. ξ was working from a pre-vendor brief
that specified @amplitude/analytics-browser and treated the API key
as a secret via `flotilla secret set`. Operator subsequently
provisioned the Amplitude project, surfaced the vendor's
recommended installation prompt, and confirmed the key value.
Three downstream changes:

- Package: swap @amplitude/analytics-browser → @amplitude/unified
  (analytics + session replay in one install; vendor-recommended).
- Init call: `amplitude.init(KEY, undefined, { defaultTracking: false })`
  becomes `amplitude.initAll(KEY, { analytics: { autocapture: true },
  sessionReplay: { sampleRate: 1 } })`. Vendor's exact installation-
  wizard shape; gates remain on the v0.13.0 consent banner.
- Binding: Amplitude browser keys are bundle-embedded by design
  (same nature as VITE_TURNSTILE_SITE_KEY from v0.12.0), so the key
  is public, not secret. CHANGELOG MUST step rewritten to bind via
  `flotilla overlay set <deployment> VITE_AMPLITUDE_API_KEY=<key>`
  rather than `flotilla secret set`. The roadmap row #13's
  "new secret: AMPLITUDE_API_KEY" wording predated vendor
  consultation; the roadmap will be updated when this ships.

§19.2 candidate captured in CHANGELOG: split the analytics consent
toggle into a separate session-replay category (recording has a
larger privacy footprint than event counters), follow-up release.

Wrapper structural shape (track/identify/anonymize, queue + drain,
consent-flip → setOptOut, lazy import) is unchanged from ξ's work.
Event taxonomy and Login.jsx / App.jsx / Admin.jsx / etc. instrument
sites are unchanged. Frontend build verified green
(VITE_APP_NAME=… npm run build).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-28 04:44:33 -07:00
parent 0fd8c52724
commit 6cfbf69e26
5 changed files with 524 additions and 85 deletions
+58 -33
View File
@@ -26,21 +26,24 @@ that.
## 0.15.0 — 2026-05-28 ## 0.15.0 — 2026-05-28
**Minor — no schema migration; one new build-time env var bound via **Minor — no schema migration; one new build-time env var bound via
`flotilla secret set`.** This release ships Amplitude analytics `flotilla overlay set`.** This release ships Amplitude Analytics +
instrumentation (roadmap item #13). The frontend gains a small Session Replay instrumentation (roadmap item #13). The frontend
wrapper around `@amplitude/analytics-browser` that gates SDK gains a small wrapper around `@amplitude/unified` that gates SDK
initialization on the v0.13.0 cookie/privacy consent — the SDK is initialization on the v0.13.0 cookie/privacy consent — the SDK is
never loaded for visitors who have not granted analytics consent, and never loaded for visitors who have not granted analytics consent,
a later consent flip to `denied` calls `setOptOut(true)` so events no session is recorded, no network request fires; a later consent
stop firing immediately. The wrapper exposes a stable taxonomy of flip to `denied` calls `setOptOut(true)` so events and session
replay stop immediately. The wrapper exposes a stable taxonomy of
nine events (Page Viewed, RFC Viewed, User Signed In / Signed Out, nine events (Page Viewed, RFC Viewed, User Signed In / Signed Out,
RFC Proposed, PR Opened, Comment Posted, Beta Access Requested, Admin RFC Proposed, PR Opened, Comment Posted, Beta Access Requested, Admin
Permission Decision) wired into the existing routes, the Login flow, Permission Decision) wired into the existing routes, the Login flow,
the propose / open-PR / discussion / PR-review surfaces, and the the propose / open-PR / discussion / PR-review surfaces, and the
admin grant/revoke action. Event bodies carry only ids and enums; no admin grant/revoke action. Event bodies carry only ids and enums; no
free-text fields (titles, comment bodies, names, emails) are ever free-text fields (titles, comment bodies, names, emails) are ever
sent. The Amplitude API key is read from `VITE_AMPLITUDE_API_KEY` at sent. **Session replay** records sessions at `sampleRate: 1` (100%)
build time; when unset, the wrapper logs one console warning and — vendor-recommended default; gated by the same v0.13.0 analytics
consent. The Amplitude API key is read from `VITE_AMPLITUDE_API_KEY`
at build time; when unset, the wrapper logs one console warning and
no-ops so dev environments without analytics keep working. No backend no-ops so dev environments without analytics keep working. No backend
events ship in this release — Amplitude SaaS holds the events, events ship in this release — Amplitude SaaS holds the events,
nothing lands in our DB, no migration. nothing lands in our DB, no migration.
@@ -51,12 +54,13 @@ nothing lands in our DB, no migration.
surface: `track(name, props)`, `identify({ user_id })`, surface: `track(name, props)`, `identify({ user_id })`,
`anonymize()`, the `EVENTS` taxonomy constant, and a `anonymize()`, the `EVENTS` taxonomy constant, and a
`__resetForTests` helper. Internally lazy-imports `__resetForTests` helper. Internally lazy-imports
`@amplitude/analytics-browser` and calls `amplitude.init(API_KEY, `@amplitude/unified` and calls `amplitude.initAll(API_KEY,
undefined, { defaultTracking: false })` only after consent is { analytics: { autocapture: true }, sessionReplay: { sampleRate: 1 } })`
granted; queues pre-init calls and drains them on init resolve; only after consent is granted; queues pre-init calls and drains
flips `setOptOut(true)` on a granted→denied consent change. The them on init resolve; flips `setOptOut(true)` on a granted→denied
wrapper subscribes to `onConsentChange()` so a freshly-banner-clicked consent change (stops both analytics events and session replay).
"analytics on" flips the SDK live without a page reload. The wrapper subscribes to `onConsentChange()` so a freshly-banner-
clicked "analytics on" flips the SDK live without a page reload.
- **Event taxonomy** wired into the app: - **Event taxonomy** wired into the app:
- `Page Viewed` — fires from `App.jsx` on every route change with - `Page Viewed` — fires from `App.jsx` on every route change with
`path` (`location.pathname`); the location hook owns the firing `path` (`location.pathname`); the location hook owns the firing
@@ -86,8 +90,9 @@ nothing lands in our DB, no migration.
called with `String(viewer.id)`. The sign-out gesture calls called with `String(viewer.id)`. The sign-out gesture calls
`anonymize()` before the nav. No email, display name, or other PII `anonymize()` before the nav. No email, display name, or other PII
is passed through the SDK. is passed through the SDK.
- **`@amplitude/analytics-browser`** dependency added to - **`@amplitude/unified`** dependency added to
`frontend/package.json`. Lockfile updated. `frontend/package.json` (analytics + session replay in one
install). Lockfile updated.
- **`VITE_AMPLITUDE_API_KEY`** documented in `frontend/.env.example` - **`VITE_AMPLITUDE_API_KEY`** documented in `frontend/.env.example`
with the secret-vs-overlay binding caveat (see below). with the secret-vs-overlay binding caveat (see below).
@@ -124,40 +129,60 @@ nothing lands in our DB, no migration.
this release and remains available for the next minor that needs a this release and remains available for the next minor that needs a
schema bump. schema bump.
### Caveat — secret-vs-overlay binding for `AMPLITUDE_API_KEY` ### Caveat — overlay binding for `VITE_AMPLITUDE_API_KEY`
Amplitude browser API keys are embedded in the frontend bundle at Amplitude browser API keys are embedded in the frontend bundle at
build time and visible to anyone with browser dev tools. They are build time and visible to anyone with browser dev tools. They are
conventionally treated as semi-sensitive (not truly secret) and would public by design — same nature as the v0.12.0
in principle fit `flotilla overlay set` rather than `flotilla secret `VITE_TURNSTILE_SITE_KEY` (also public, also bundle-embedded,
set`. The roadmap calls for `secret set` to keep explicitly contrasted with `CLOUDFLARE_TURNSTILE_SECRET` which is
all-keys-in-Secret-Manager regularity for the deployment — that's the the real secret-half of that pair). The Amplitude installation
choice this release follows. The matching CloudFlare Turnstile pair guidance from the vendor shows the key inline as a literal string
(public site key via overlay, secret key via Secret Manager) is the argument to `initAll(…)`, confirming the public framing. This
contrast; Amplitude only has one key so the "is it public?" question release accordingly binds the value via `flotilla overlay set`,
has no separating answer at provisioning time, and the operator runs not `flotilla secret set` — the env-var name is `VITE_AMPLITUDE_API_KEY`
the secret-set gesture rather than the overlay-set one. (Vite-prefix convention, so the build picks it up directly without
an alias step).
(Roadmap row #13 originally said "new secret: AMPLITUDE_API_KEY";
that wording predated vendor consultation. Mid-Session-L the
operator provisioned the Amplitude project, surfaced the vendor's
recommended init prompt, and the binding settled as overlay. The
roadmap row will be updated to match when #13 ships.)
### Caveat — session replay scope and consent
This release enables Amplitude Session Replay at `sampleRate: 1`
(100% of sessions recorded for full-DOM playback). The vendor's
installation wizard recommends this default for new deployments —
maximum learning during the early phase. The v0.13.0 single
"analytics" consent toggle gates session replay together with
events, so no recording happens without explicit opt-in. A future
release **MAY** split this into a separate consent category for
session replay specifically (recording has a meaningfully larger
privacy footprint than event counters); §19.2 candidate.
### Upgrade steps (from 0.14.0) ### Upgrade steps (from 0.14.0)
- You **MUST** install the new frontend dependency before building: - You **MUST** install the new frontend dependency before building:
`cd frontend && npm install` picks up `@amplitude/analytics-browser` `cd frontend && npm install` picks up `@amplitude/unified` from
from the updated `frontend/package.json` and the refreshed the updated `frontend/package.json` and the refreshed
`package-lock.json`. The lockfile change is committed. `package-lock.json`. The lockfile change is committed.
- You **MUST** rebuild the frontend after upgrading so the analytics - You **MUST** rebuild the frontend after upgrading so the analytics
wrapper and its consent gate ship to viewers. `frontend/package.json#version` wrapper and its consent gate ship to viewers. `frontend/package.json#version`
and `VERSION` both move to `0.15.0`. No schema migration; the and `VERSION` both move to `0.15.0`. No schema migration; the
backend is unchanged for this release. backend is unchanged for this release.
- **MUST**: before deploying, the operator runs `pbpaste | /Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla secret set ohm-rfc-app AMPLITUDE_API_KEY` (with the Amplitude project's API key in the clipboard) to bind the new `AMPLITUDE_API_KEY` secret. The deploy MUST NOT proceed before this binding exists. If the binding is absent, the frontend's analytics wrapper no-ops with a console warning and the rest of the app continues to function — but no events are sent. - **MUST**: before deploying, the operator runs `/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla overlay set ohm-rfc-app VITE_AMPLITUDE_API_KEY=<key>` to bind the Amplitude project's public API key. (Receiving the value in the conversation is fine — it's bundle-embedded by design, same as `VITE_TURNSTILE_SITE_KEY`.) The deploy **SHOULD NOT** proceed before this binding exists; if the binding is absent, the frontend's analytics wrapper no-ops with a console warning and the rest of the app continues to function — but no events or session replays are sent.
- You **MAY** leave `VITE_AMPLITUDE_API_KEY` unset in dev environments - You **MAY** leave `VITE_AMPLITUDE_API_KEY` unset in dev environments
— the wrapper detects the empty value and no-ops with a single — the wrapper detects the empty value and no-ops with a single
console warning. The app, the consent banner, and every other console warning. The app, the consent banner, and every other
surface keep working unchanged. surface keep working unchanged.
- You **SHOULD** verify after deploy that the Amplitude dashboard - You **SHOULD** verify after deploy that the Amplitude dashboard
receives events when a consenting browser exercises one of the receives events and a session replay when a consenting browser
taxonomy events (the easiest probe: open the deployed site in an exercises one of the taxonomy events (the easiest probe: open the
Incognito window, accept analytics on the consent banner, navigate deployed site in an Incognito window, accept analytics on the
to an RFC, and watch the project's live event stream). consent banner, navigate to an RFC, and watch the project's live
event stream + replay panel).
## 0.14.0 — 2026-05-28 ## 0.14.0 — 2026-05-28
+17 -12
View File
@@ -63,19 +63,24 @@ VITE_COOKIES_POLICY_URL=
# VITE_TURNSTILE_SITE_KEY=0x4AAAAAAA... # VITE_TURNSTILE_SITE_KEY=0x4AAAAAAA...
VITE_TURNSTILE_SITE_KEY= VITE_TURNSTILE_SITE_KEY=
# v0.15.0 / roadmap item #13: Amplitude project API key. Embedded in # v0.15.0 / roadmap item #13: Amplitude project API key (public).
# the frontend bundle at build time and used by the analytics wrapper # Embedded in the frontend bundle at build time and used by the
# (`frontend/src/lib/analytics.js`) when the user has granted analytics # analytics wrapper (`frontend/src/lib/analytics.js`) — which loads
# consent (v0.13.0 cookie banner). Provision an Amplitude project at # `@amplitude/unified` (Analytics + Session Replay) when the user
# app.amplitude.com → Projects → New, copy the API key. # has granted analytics consent (v0.13.0 cookie banner). Provision
# an Amplitude project at app.amplitude.com → Projects → New, copy
# the API key.
# #
# Caveat — secret-vs-overlay binding choice: Amplitude browser API # Public by design: Amplitude browser keys are bundle-embedded
# keys are visible to anyone with browser dev tools (they ride in the # (visible in dev tools), same nature as VITE_TURNSTILE_SITE_KEY
# shipped bundle). They are conventionally treated as semi-sensitive, # (also public; the truly-secret half of that Turnstile pair is
# not truly secret. The roadmap binds the value through flotilla's # CLOUDFLARE_TURNSTILE_SECRET on the backend). For deployments
# `secret set` verb anyway, to keep all-keys-in-Secret-Manager # behind flotilla, bind via `flotilla overlay set <deployment>
# regularity for the OHM deployment. Leave unset in dev; the wrapper # VITE_AMPLITUDE_API_KEY=<key>` — NOT `flotilla secret set`. The
# logs one console warning and no-ops (the app continues to work). # vendor's installation wizard shows the key inline as a literal
# string in the init call, confirming the public framing. Leave
# unset in dev; the wrapper logs one console warning and no-ops
# (the app continues to work).
# #
# Examples: # Examples:
# VITE_AMPLITUDE_API_KEY=01234567890abcdef01234567890abcd # VITE_AMPLITUDE_API_KEY=01234567890abcdef01234567890abcd
+395 -6
View File
@@ -8,7 +8,7 @@
"name": "rfc-app-frontend", "name": "rfc-app-frontend",
"version": "0.15.0", "version": "0.15.0",
"dependencies": { "dependencies": {
"@amplitude/analytics-browser": "^2.42.4", "@amplitude/unified": "^1.1.9",
"@codemirror/commands": "^6.10.3", "@codemirror/commands": "^6.10.3",
"@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.12.3", "@codemirror/language": "^6.12.3",
@@ -48,6 +48,18 @@
"tslib": "^2.4.1" "tslib": "^2.4.1"
} }
}, },
"node_modules/@amplitude/analytics-client-common": {
"version": "2.4.48",
"resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.4.48.tgz",
"integrity": "sha512-jdRvu8ux3aIf74FvTDZuSFR1mutzdrIg1ebXYqpKizs9upXz1AJnHClkldSw9i4yu924AJ2wudxq6dccHWlNiA==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-connector": "^1.4.8",
"@amplitude/analytics-core": "2.48.2",
"@amplitude/analytics-types": "2.11.1",
"tslib": "^2.4.1"
}
},
"node_modules/@amplitude/analytics-connector": { "node_modules/@amplitude/analytics-connector": {
"version": "1.6.4", "version": "1.6.4",
"resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.6.4.tgz", "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.6.4.tgz",
@@ -67,6 +79,43 @@
"zen-observable": "0.10.0" "zen-observable": "0.10.0"
} }
}, },
"node_modules/@amplitude/analytics-types": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.11.1.tgz",
"integrity": "sha512-wFEgb0t99ly2uJKm5oZ28Lti0Kh5RecR5XBkwfUpDzn84IoCIZ8GJTsMw/nThu8FZFc7xFDA4UAt76zhZKrs9A==",
"license": "MIT"
},
"node_modules/@amplitude/engagement-browser": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@amplitude/engagement-browser/-/engagement-browser-1.0.9.tgz",
"integrity": "sha512-zvPr0L5aLlOS3nG8scIkEEDMVK2y3MaMbgjYhMfYruhMpfsC/U0apov22nEc1RRrTwve2awEXruPRKf1TysqrQ==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-types": "^2.0.0"
}
},
"node_modules/@amplitude/experiment-core": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/@amplitude/experiment-core/-/experiment-core-0.13.1.tgz",
"integrity": "sha512-ZHvR0dxTltasp8MiMcQ6qKsY20mWnODoy3oebGad6qaRR1ywpUi8IuLf5AwLTM35ZwgzEUTn9TEIWKLHpDwHMw==",
"license": "MIT",
"dependencies": {
"js-base64": "^3.7.5"
}
},
"node_modules/@amplitude/experiment-js-client": {
"version": "1.21.1",
"resolved": "https://registry.npmjs.org/@amplitude/experiment-js-client/-/experiment-js-client-1.21.1.tgz",
"integrity": "sha512-chE/4qQG/5Cgl93Wqj1NEdgOL5LkqySLlfk1EN0f+7bJa52HpkGFALA2FeCNYf31Z5CglEeKX6dUMgL7y33SIw==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-connector": "^1.6.4",
"@amplitude/experiment-core": "^0.13.1",
"@amplitude/ua-parser-js": "^0.7.31",
"base64-js": "1.5.1",
"unfetch": "4.1.0"
}
},
"node_modules/@amplitude/plugin-autocapture-browser": { "node_modules/@amplitude/plugin-autocapture-browser": {
"version": "1.27.2", "version": "1.27.2",
"resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.27.2.tgz", "resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.27.2.tgz",
@@ -97,6 +146,16 @@
"tslib": "^2.4.1" "tslib": "^2.4.1"
} }
}, },
"node_modules/@amplitude/plugin-experiment-browser": {
"version": "1.0.0-beta.28",
"resolved": "https://registry.npmjs.org/@amplitude/plugin-experiment-browser/-/plugin-experiment-browser-1.0.0-beta.28.tgz",
"integrity": "sha512-NQz267zLi7vl2G2lx10yUrEoGOCe5K9iqcPSIjbTavGu/XGvsmqLDqBHhg+EkdEMAPwypoXnmtPEs3RMhX+1MA==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-core": "2.48.2",
"@amplitude/experiment-js-client": "^1.15.5"
}
},
"node_modules/@amplitude/plugin-network-capture-browser": { "node_modules/@amplitude/plugin-network-capture-browser": {
"version": "1.10.1", "version": "1.10.1",
"resolved": "https://registry.npmjs.org/@amplitude/plugin-network-capture-browser/-/plugin-network-capture-browser-1.10.1.tgz", "resolved": "https://registry.npmjs.org/@amplitude/plugin-network-capture-browser/-/plugin-network-capture-browser-1.10.1.tgz",
@@ -127,6 +186,22 @@
"tslib": "^2.4.1" "tslib": "^2.4.1"
} }
}, },
"node_modules/@amplitude/plugin-session-replay-browser": {
"version": "1.31.0",
"resolved": "https://registry.npmjs.org/@amplitude/plugin-session-replay-browser/-/plugin-session-replay-browser-1.31.0.tgz",
"integrity": "sha512-b7kyYVEdW3EMR6cPXCfld+h8nQsuAR5o6vum8Glu+ofhFDfG4wj/mTJ0ITEaNbsJCfXniKQ3kFgTe6hTtxSFGQ==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-client-common": "2.4.48",
"@amplitude/analytics-core": "2.48.2",
"@amplitude/analytics-types": "2.11.1",
"@amplitude/rrweb-plugin-console-record": "2.0.0-alpha.40",
"@amplitude/rrweb-record": "2.0.0-alpha.40",
"@amplitude/session-replay-browser": "1.44.0",
"idb-keyval": "^6.2.1",
"tslib": "^2.4.1"
}
},
"node_modules/@amplitude/plugin-web-vitals-browser": { "node_modules/@amplitude/plugin-web-vitals-browser": {
"version": "1.1.33", "version": "1.1.33",
"resolved": "https://registry.npmjs.org/@amplitude/plugin-web-vitals-browser/-/plugin-web-vitals-browser-1.1.33.tgz", "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-vitals-browser/-/plugin-web-vitals-browser-1.1.33.tgz",
@@ -138,6 +213,178 @@
"web-vitals": "5.1.0" "web-vitals": "5.1.0"
} }
}, },
"node_modules/@amplitude/rrdom": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@amplitude/rrdom/-/rrdom-2.1.0.tgz",
"integrity": "sha512-2dAtxXL02usBV2CSOnScLd3WoVqWaeiGpxN8LuXJ0r/NpLJkW1k876v2tRKAz5NrxPwSdjihsMmwCIXHpJhHfA==",
"license": "MIT",
"dependencies": {
"@amplitude/rrweb-snapshot": "^2.1.0"
}
},
"node_modules/@amplitude/rrweb": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb/-/rrweb-2.1.1.tgz",
"integrity": "sha512-6uA+5VE/VHumaXPXTTLGRogd/K9MDwd01jGteppeLzsX0PvqlDyY5aIi35yh9+q1iS6ciPBn/2NRg0lg4cFIlw==",
"license": "MIT",
"dependencies": {
"@amplitude/rrdom": "^2.1.0",
"@amplitude/rrweb-snapshot": "^2.1.0",
"@amplitude/rrweb-types": "^2.1.0",
"@amplitude/rrweb-utils": "^2.1.0",
"@types/css-font-loading-module": "0.0.7",
"@xstate/fsm": "^1.4.0",
"base64-arraybuffer": "^1.0.1",
"mitt": "^3.0.0"
}
},
"node_modules/@amplitude/rrweb-packer": {
"version": "2.0.0-alpha.40",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-packer/-/rrweb-packer-2.0.0-alpha.40.tgz",
"integrity": "sha512-Btb6b9pS1IvDMbvyYxpUdTk9NRJugSoJjRCl7R6jP/iSlPWXoveJIwHaNFAS9ZmWUEK7HhyBJ8bKGFN3giUsDg==",
"license": "MIT",
"dependencies": {
"@amplitude/rrweb-types": "^2.0.0-alpha.40",
"fflate": "^0.4.4"
}
},
"node_modules/@amplitude/rrweb-plugin-console-record": {
"version": "2.0.0-alpha.40",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-plugin-console-record/-/rrweb-plugin-console-record-2.0.0-alpha.40.tgz",
"integrity": "sha512-vtY7T/kGFl62nC1u7ZUXQvU7ulB70cZGVHPRN/SO9fzVfsY7y6rCmBfoc2jS5KmISdlgkVzMjY2r/EE2Gk9AQA==",
"license": "MIT",
"peerDependencies": {
"@amplitude/rrweb": "^2.0.0-alpha.40"
}
},
"node_modules/@amplitude/rrweb-record": {
"version": "2.0.0-alpha.40",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-record/-/rrweb-record-2.0.0-alpha.40.tgz",
"integrity": "sha512-5cJhQwzhymJWX5/XOtpWK0h2NLq9+t2YiO6ub0cdZ9F5AZizaRbsVH88int07DfX0YiXTKWbISezVuduCLqgSQ==",
"license": "MIT",
"dependencies": {
"@amplitude/rrweb": "^2.0.0-alpha.40",
"@amplitude/rrweb-types": "^2.0.0-alpha.40"
}
},
"node_modules/@amplitude/rrweb-snapshot": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-snapshot/-/rrweb-snapshot-2.1.0.tgz",
"integrity": "sha512-xYQvOW73ig+5M7caqilA8j0S6MHWUULLeJNK+2VVvUqv8mr4FMT2DUAQiVBGCImNlb9Gu2rLUfCScMnVxn+EDg==",
"license": "MIT",
"dependencies": {
"postcss": "^8.4.38"
}
},
"node_modules/@amplitude/rrweb-types": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-types/-/rrweb-types-2.1.0.tgz",
"integrity": "sha512-S73tBI/04A6HCHgnrUNeeVOvnDTEoQnNrmZGyrZncJwRlTIX+6BQSYtBFofMag8GnAy9gA+NtC0TL0CnluOWBw==",
"license": "MIT"
},
"node_modules/@amplitude/rrweb-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-utils/-/rrweb-utils-2.1.0.tgz",
"integrity": "sha512-dTCDnSiMMHZ10utYHJ8dSd/xkjFgdF67y74PkOzAPcCKW1rLxyJYcFOA3uPL2b7cIVVmoel/5NTp5eflaUaJfQ==",
"license": "MIT"
},
"node_modules/@amplitude/session-replay-browser": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/@amplitude/session-replay-browser/-/session-replay-browser-1.44.0.tgz",
"integrity": "sha512-8Ruep2TTDMcfVMKurSpBbVclBK/v8Lb3aSHFsYd/xOQ1E3CaKoAu39pplli28NWoUcW7unyVE7khkOa2zzn0Lw==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-client-common": "2.4.48",
"@amplitude/analytics-core": "2.48.2",
"@amplitude/analytics-types": "2.11.1",
"@amplitude/experiment-core": "0.7.2",
"@amplitude/rrweb-packer": "2.0.0-alpha.40",
"@amplitude/rrweb-plugin-console-record": "2.0.0-alpha.40",
"@amplitude/rrweb-record": "2.0.0-alpha.40",
"@amplitude/rrweb-types": "2.0.0-alpha.40",
"@amplitude/rrweb-utils": "2.0.0-alpha.40",
"@amplitude/targeting": "0.2.0",
"@rollup/plugin-replace": "^6.0.1",
"idb": "8.0.0",
"tslib": "^2.4.1"
}
},
"node_modules/@amplitude/session-replay-browser/node_modules/@amplitude/experiment-core": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@amplitude/experiment-core/-/experiment-core-0.7.2.tgz",
"integrity": "sha512-Wc2NWvgQ+bLJLeF0A9wBSPIaw0XuqqgkPKsoNFQrmS7r5Djd56um75In05tqmVntPJZRvGKU46pAp8o5tdf4mA==",
"license": "MIT",
"dependencies": {
"js-base64": "^3.7.5"
}
},
"node_modules/@amplitude/session-replay-browser/node_modules/@amplitude/rrweb-types": {
"version": "2.0.0-alpha.40",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-types/-/rrweb-types-2.0.0-alpha.40.tgz",
"integrity": "sha512-rP7CBDkzXupxOA7ukvC+zDYLuCtsz54TuJKC4+5O72Jsz4YdokLznKZRG34P6zXozfhGU0261qckk87lLY6mKQ==",
"license": "MIT"
},
"node_modules/@amplitude/session-replay-browser/node_modules/@amplitude/rrweb-utils": {
"version": "2.0.0-alpha.40",
"resolved": "https://registry.npmjs.org/@amplitude/rrweb-utils/-/rrweb-utils-2.0.0-alpha.40.tgz",
"integrity": "sha512-i1CCt6MCjlqoeNc+1Hse5bz+ZbASaWaIJ0WdJZvnQjUCHH29Xy/QFouyOuor73RZ+UWX4s2tYSrUIdmBepXk3w==",
"license": "MIT"
},
"node_modules/@amplitude/targeting": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@amplitude/targeting/-/targeting-0.2.0.tgz",
"integrity": "sha512-/50ywTrC4hfcfJVBbh5DFbqMPPfaIOivZeb5Gb+OGM03QrA+lsUqdvtnKLNuWtceD4H6QQ2KFzPJ5aAJLyzVDA==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-client-common": ">=1 <3",
"@amplitude/analytics-core": ">=1 <3",
"@amplitude/analytics-types": ">=1 <3",
"@amplitude/experiment-core": "0.7.2",
"idb": "^8.0.0",
"tslib": "^2.4.1"
}
},
"node_modules/@amplitude/targeting/node_modules/@amplitude/experiment-core": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@amplitude/experiment-core/-/experiment-core-0.7.2.tgz",
"integrity": "sha512-Wc2NWvgQ+bLJLeF0A9wBSPIaw0XuqqgkPKsoNFQrmS7r5Djd56um75In05tqmVntPJZRvGKU46pAp8o5tdf4mA==",
"license": "MIT",
"dependencies": {
"js-base64": "^3.7.5"
}
},
"node_modules/@amplitude/ua-parser-js": {
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/ua-parser-js"
},
{
"type": "paypal",
"url": "https://paypal.me/faisalman"
}
],
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/@amplitude/unified": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@amplitude/unified/-/unified-1.1.9.tgz",
"integrity": "sha512-YPgQbp/vDQ92GshHs2hfUxoeRnR3rRBWCoQ6wXgFjXQ1uiJf2tP0CBZWdrCStSDuhcpo2rsCz/Ek2LGq5J6SIQ==",
"license": "MIT",
"dependencies": {
"@amplitude/analytics-browser": "2.42.4",
"@amplitude/analytics-core": "2.48.2",
"@amplitude/engagement-browser": "^1.0.3",
"@amplitude/plugin-experiment-browser": "1.0.0-beta.28",
"@amplitude/plugin-session-replay-browser": "1.31.0"
}
},
"node_modules/@antfu/install-pkg": { "node_modules/@antfu/install-pkg": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
@@ -372,6 +619,12 @@
"import-meta-resolve": "^4.2.0" "import-meta-resolve": "^4.2.0"
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@lezer/common": { "node_modules/@lezer/common": {
"version": "1.5.2", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz",
@@ -765,6 +1018,49 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rollup/plugin-replace": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
"integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"magic-string": "^0.30.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@tiptap/core": { "node_modules/@tiptap/core": {
"version": "3.23.6", "version": "3.23.6",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.23.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.23.6.tgz",
@@ -1217,6 +1513,12 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@types/css-font-loading-module": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz",
"integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==",
"license": "MIT"
},
"node_modules/@types/d3": { "node_modules/@types/d3": {
"version": "7.4.3", "version": "7.4.3",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
@@ -1470,6 +1772,12 @@
"@types/d3-selection": "*" "@types/d3-selection": "*"
} }
}, },
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"license": "MIT"
},
"node_modules/@types/geojson": { "node_modules/@types/geojson": {
"version": "7946.0.16", "version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
@@ -1549,6 +1857,41 @@
} }
} }
}, },
"node_modules/@xstate/fsm": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz",
"integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==",
"license": "MIT"
},
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/commander": { "node_modules/commander": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
@@ -2135,6 +2478,12 @@
"benchmarks" "benchmarks"
] ]
}, },
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/fast-equals": { "node_modules/fast-equals": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
@@ -2162,6 +2511,12 @@
} }
} }
}, },
"node_modules/fflate": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
"license": "MIT"
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2195,6 +2550,18 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/idb": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz",
"integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==",
"license": "ISC"
},
"node_modules/idb-keyval": {
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.4.tgz",
"integrity": "sha512-D/NzHWUmYJGXi++z67aMSrnisb9A3621CyRK5G89JyTlN13C8xf0g04DLxUKMufPem3e3L2JAXR6Z00OWy183Q==",
"license": "Apache-2.0"
},
"node_modules/import-meta-resolve": { "node_modules/import-meta-resolve": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
@@ -2214,6 +2581,12 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/js-base64": {
"version": "3.7.8",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz",
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
"license": "BSD-3-Clause"
},
"node_modules/katex": { "node_modules/katex": {
"version": "0.16.47", "version": "0.16.47",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
@@ -2535,6 +2908,15 @@
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/marked": { "node_modules/marked": {
"version": "18.0.4", "version": "18.0.4",
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.4.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.4.tgz",
@@ -2588,11 +2970,16 @@
"node": ">= 20" "node": ">= 20"
} }
}, },
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.12", "version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -2629,14 +3016,12 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@@ -2665,7 +3050,6 @@
"version": "8.5.15", "version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -2970,7 +3354,6 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -3029,6 +3412,12 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/unfetch": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz",
"integrity": "sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==",
"license": "MIT"
},
"node_modules/use-sync-external-store": { "node_modules/use-sync-external-store": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+1 -1
View File
@@ -9,7 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@amplitude/analytics-browser": "^2.42.4", "@amplitude/unified": "^1.1.9",
"@codemirror/commands": "^6.10.3", "@codemirror/commands": "^6.10.3",
"@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.12.3", "@codemirror/language": "^6.12.3",
+53 -33
View File
@@ -1,9 +1,10 @@
// analytics.js — v0.15.0 / roadmap item #13. // analytics.js — v0.15.0 / roadmap item #13.
// //
// Wrapper around `@amplitude/analytics-browser` that gates SDK // Wrapper around `@amplitude/unified` (Amplitude Analytics +
// initialization on the user's cookie/privacy consent (v0.13.0, // Session Replay) that gates SDK initialization on the user's
// `frontend/src/lib/consent.js`, SPEC §14.5). The wrapper presents // cookie/privacy consent (v0.13.0, `frontend/src/lib/consent.js`,
// a stable surface to the rest of the app: // SPEC §14.5). The wrapper presents a stable surface to the rest
// of the app:
// //
// import { track, identify, anonymize } from './lib/analytics' // import { track, identify, anonymize } from './lib/analytics'
// //
@@ -15,17 +16,20 @@
// 1. Calls `bootstrap()` once, which reads `getConsent()` and // 1. Calls `bootstrap()` once, which reads `getConsent()` and
// subscribes to `onConsentChange()`. If consent.analytics is // subscribes to `onConsentChange()`. If consent.analytics is
// true, it lazily imports the Amplitude SDK and calls // true, it lazily imports the Amplitude SDK and calls
// `amplitude.init(API_KEY, { defaultTracking: false })`. // `amplitude.initAll(API_KEY, { analytics: { autocapture: true },
// sessionReplay: { sampleRate: 1 } })`.
// If consent.analytics is false (or undecided), the SDK is // If consent.analytics is false (or undecided), the SDK is
// not loaded — no network request, no cookies. A later // not loaded — no network request, no cookies, no session
// consent change to `true` triggers init at that moment. // replay recording. A later consent change to `true` triggers
// init at that moment.
// 2. The wrapper queues `track()` and `identify()` calls made // 2. The wrapper queues `track()` and `identify()` calls made
// before init finishes (lazy import + consent grant), and // before init finishes (lazy import + consent grant), and
// drains the queue when init completes. // drains the queue when init completes.
// 3. If the user later flips consent from granted → denied, the // 3. If the user later flips consent from granted → denied, the
// wrapper calls `amplitude.setOptOut(true)` so subsequent // wrapper calls `amplitude.setOptOut(true)` so subsequent
// events are dropped client-side (the SDK is still loaded — // events are dropped client-side and session replay stops
// we cannot unload a script — but it stops firing). // recording (the SDK is still loaded — we cannot unload a
// script — but it stops firing).
// //
// Consent precedence ladder: // Consent precedence ladder:
// //
@@ -35,6 +39,14 @@
// consent.recorded_at === null → treat as denied (banner is up; // consent.recorded_at === null → treat as denied (banner is up;
// the user has not yet chosen) // the user has not yet chosen)
// //
// Session replay scope: this release ships session replay at
// `sampleRate: 1` (100% of sessions are recorded for full-DOM
// playback). That is the vendor-recommended default for new
// Amplitude deployments. The v0.13.0 consent banner's single
// "analytics" toggle gates both events and session replay together —
// a separate consent category for session-replay specifically is a
// §19.2 follow-up.
//
// API key resolution: // API key resolution:
// //
// The build-time env var `VITE_AMPLITUDE_API_KEY` carries the // The build-time env var `VITE_AMPLITUDE_API_KEY` carries the
@@ -43,15 +55,11 @@
// function becomes a deterministic no-op so dev environments // function becomes a deterministic no-op so dev environments
// (and deployments that intentionally don't ship analytics) // (and deployments that intentionally don't ship analytics)
// keep working. The deploy gesture wires the key via flotilla's // keep working. The deploy gesture wires the key via flotilla's
// `secret set` verb (see CHANGELOG for the operator gesture). // `overlay set` verb (see CHANGELOG for the operator gesture):
// // Amplitude browser keys are bundle-embedded by design (visible
// Note on bundle visibility: Amplitude browser API keys are embedded // to anyone with dev tools, same nature as the v0.12.0
// in the frontend bundle and visible via dev tools — they are // `VITE_TURNSTILE_SITE_KEY`), so the binding is overlay, not
// conventionally treated as semi-sensitive, not truly secret. The // secret.
// roadmap binds the value through `flotilla secret set` rather than
// `flotilla overlay set` to keep all-keys-in-Secret-Manager
// regularity for the deployment. See CHANGELOG 0.15.0 for the
// caveat write-up.
// //
// PII discipline: // PII discipline:
// //
@@ -100,9 +108,9 @@ function warnNoKey() {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.warn( console.warn(
'[analytics] VITE_AMPLITUDE_API_KEY is unset; analytics events ' + '[analytics] VITE_AMPLITUDE_API_KEY is unset; analytics events ' +
'will not be sent. This is expected in dev; in production it ' + 'and session replay will not be sent. This is expected in dev; ' +
'means the operator has not yet run `flotilla secret set ' + 'in production it means the operator has not yet run ' +
'ohm-rfc-app AMPLITUDE_API_KEY`.', '`flotilla overlay set <deployment> VITE_AMPLITUDE_API_KEY=<key>`.',
) )
} }
@@ -148,19 +156,31 @@ async function initSdk() {
} }
_initPromise = (async () => { _initPromise = (async () => {
try { try {
const mod = await import('@amplitude/analytics-browser') const mod = await import('@amplitude/unified')
// The SDK exports `init`, `track`, `setUserId`, `reset`, // The unified package exposes `initAll`, `track`,
// `setOptOut` as named functions. We hold the module so the // `setUserId`, `reset`, `setOptOut` as named functions.
// queue drainer can call them by name. // We hold the module so the queue drainer can call them
// by name.
_amplitude = mod _amplitude = mod
// defaultTracking: false — we choose what to send, and we // initAll wires up both Analytics and Session Replay in one
// already gate on consent here. The SDK's own "default // call. Vendor-recommended init shape from the Amplitude
// tracking" would otherwise capture page-views, sessions, // installation wizard:
// and form interactions automatically; we want explicit // - analytics.autocapture: true — auto-instruments page
// `track('Page Viewed', …)` calls from the app instead. // views, session start/end, clicks, and form interactions.
await mod.init(API_KEY, undefined, { // Our explicit `track('Page Viewed', …)` etc. layer on top
defaultTracking: false, // for app-specific names that survive renames.
}).promise // - sessionReplay.sampleRate: 1 — record 100% of sessions
// for full-DOM playback. Gated by the v0.13.0 consent
// banner just like the rest of the SDK; never starts
// recording without explicit analytics opt-in.
const ret = mod.initAll(API_KEY, {
analytics: { autocapture: true },
sessionReplay: { sampleRate: 1 },
})
// initAll returns an AmplitudeReturn with a `.promise` accessor
// (consistent with the legacy `init`). Some unified builds
// resolve synchronously; await defensively.
if (ret && ret.promise) await ret.promise
_initialized = true _initialized = true
drainQueue() drainQueue()
} catch (err) { } catch (err) {