Repository navigation
Support inline events in the main v12 script - #106
Conversation
Requested: integrate documented inline events without overengineering, keep the script small and website behavior intact, and open a separate PR based on and targeting v12. Use delegated click/submit handlers and keepalive GETs with the existing image fallback. Preserve installed inline helpers and make confirmed 404 detection opt-in. Add focused tests and patch the vulnerable Handlebars build dependency.
| button.form === element && | ||
| (button.tagName === "BUTTON" || | ||
| (button.tagName === "INPUT" && button.type === "submit")) | ||
| ? button.value || button.textContent || "" |
There was a problem hiding this comment.
button.value shadows the label, and the text is neither trimmed nor capped: <button name="action" value="save">Save changes</button> reports button_text: "save", <button>\n Join now\n</button> reports "\n Join now\n ", and a long button pushes the simple.gif URL past 5 KB (verified against dist/latest/latest.dev.js).
| ? button.value || button.textContent || "" | |
| ? (button.textContent || button.value || "") | |
| .replace(/\s+/g, " ") | |
| .trim() | |
| .slice(0, 200) |
With this, <input type="submit" value="Join"> still falls through to value, and all 15 inline-event tests still pass.
There was a problem hiding this comment.
Fixed in 7f712bd with the shorter submitter-only path: prefer textContent, fall back to value for submit inputs, trim outer whitespace and cap the result at 200 characters. Added checks for a label differing from the button value, oversized labels, submit inputs, and missing submitters. All 32 unit tests pass.
| var detect404 = function () { | ||
| if ( | ||
| hasInlineHelper() || | ||
| !/[^0-9.,]404[^0-9.,]/.test(documentElement.innerHTML) |
There was a problem hiding this comment.
Matching 404 against the whole serialized DOM false-positives constantly, so opted-in sites send an extra HEAD request for their own URL on ordinary 200 pages — I confirmed a request fires for <div style="width: 404px">, <script src="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/assets/main-404abc.js">, <div class="w-404"> and <div data-id="404">. Narrow the scan to rendered text (e.g. doc.title plus doc.body.textContent) so the added request matches the "existing installs must opt in" cost the comment above promises.
There was a problem hiding this comment.
Changed the probe to title plus body text in 7f712bd. A test containing all four attribute-only examples now confirms that no HEAD request is issued. Confirmed 404 events still work before and after DOM ready in Chromium, and the response status must still be 404.
| run: pnpm run test:matrix | ||
|
|
||
| - name: Test inline events | ||
| run: pnpm run build && pnpm exec mocha test/unit/inline-events.test.js --exit |
There was a problem hiding this comment.
This runs only the new file, so the seven other test/unit/*.test.js suites that cover the same src/default.js this PR rewrites still never run in CI. Widening the glob picks them up (I ran all 31 locally against this branch and they pass):
| run: pnpm run build && pnpm exec mocha test/unit/inline-events.test.js --exit | |
| - name: Run unit tests | |
| run: pnpm run build && pnpm exec mocha "test/unit/*.test.js" --exit |
Worth also adding a test:unit script to package.json — AGENTS.md tells contributors to run npm run test:unit, which does not exist.
There was a problem hiding this comment.
Added test:unit and changed this CI step to run the complete test/unit/*.test.js suite in 7f712bd. All 32 tests pass locally, including the 16 inline-event tests.
Documentation update
|
Claude review checkpointReviewed commit |
|
SDLC label: |
| var hasInlineHelper = function () { | ||
| return doc.querySelector('script[src*="inline-events.js"]'); | ||
| }; |
There was a problem hiding this comment.
This was just a test helper, fine if this collides, it was beta anyway. Remove this check.
There was a problem hiding this comment.
Removed the beta-helper detection from both inline-event handling and 404 detection in 7f712bd. The built-in handlers now run even if the old helper tag is present; the updated PR description says to remove that beta script to avoid duplicate events.
| var detect404 = function () { | ||
| if ( | ||
| hasInlineHelper() || | ||
| !/[^0-9.,]404[^0-9.,]/.test(documentElement.innerHTML) |
There was a problem hiding this comment.
we cna't use a abbrivations of documentElement.innerHTML, right?
There was a problem hiding this comment.
The existing documentElement alias is safe: it holds the DOM object, and reading its .innerHTML later reads the current HTML. Caching the HTML string itself would be stale. In 7f712bd the 404 check instead reads the title and body text, which also prevents attributes such as width: 404px from triggering a HEAD request. The focused attribute test and before/after-DOM-ready Chromium checks pass.
| metadata.button_text = | ||
| button && | ||
| button.form === element && | ||
| (button.tagName === "BUTTON" || | ||
| (button.tagName === "INPUT" && button.type === "submit")) | ||
| ? button.value || button.textContent || "" | ||
| : ""; |
There was a problem hiding this comment.
can we make this less perfect, but a lot shorter?
There was a problem hiding this comment.
Shortened this in 7f712bd: use event.submitter directly and remove the active-element fallback plus the tag/form checks. If the browser provides no submitter, button text is simply empty and the form event is still recorded. The label is preferred over value, trimmed and capped at 200 characters. Form validation, native submitter overrides, and SPA submissions pass the browser checks.
Requested: remove compatibility checks for the beta helper, measure repeated attribute strings, clarify the DOM alias, and shorten submit-button metadata extraction. Keep literals because sharing them increases gzip size. Prefer trimmed, capped submitter labels and scan title/body text for 404s. Address the related bot feedback by running the complete unit suite in CI.
| if (isForm) { | ||
| var button = event.submitter; | ||
| metadata.button_text = button | ||
| ? (button.textContent || button.value || "").trim().slice(0, 200) |
There was a problem hiding this comment.
trim() only strips the ends, so the very common icon/span submit button keeps its markup indentation — I verified <button>\n <svg></svg>\n <span>Save</span>\n <span>changes</span>\n</button> sends button_text: "Save\n changes", and the same button reformatted produces a different value, fragmenting the metadata. Collapse internal whitespace too:
| ? (button.textContent || button.value || "").trim().slice(0, 200) | |
| ? (button.textContent || button.value || "") | |
| .replace(/\s+/g, " ") | |
| .trim() | |
| .slice(0, 200) |
There was a problem hiding this comment.
Fixed in f1b1428. Submit-button labels now collapse internal whitespace before the existing trim and 200-character limit, so compact and indented markup both record Save changes.
Local verification: the new regression fails against the previous compiled tracker; all 33 unit tests, the build, formatting, and diff checks pass after the fix. Remote BrowserStack CI and automated review both passed on this exact commit.
There was a problem hiding this comment.
Confirmed implemented in the current head, f1b1428: .replace(/\s+/g, " ") runs before trimming and the 200-character cap. The regression verifies that compact and indented icon/span button markup both produce Save changes. All 33 unit tests pass locally, and the remote unit-test step has also passed. This costs 20 bytes raw / 8 bytes gzip.
Documentation update
|
Requested: address scripts PR #106 review comment 4282258759 so formatting a button across multiple lines does not fragment button_text metadata. Collapse whitespace before trimming and applying the existing 200-character limit. Add a regression for equivalent compact and indented button markup, and regenerate the affected tracker bundles. Verification: regression fails on the previous compiled tracker; all 33 unit tests pass after the fix. Build, formatting, and diff checks pass.
|
In response to #106 (comment) and #106 (comment): Prepared a patch against current docs/master for the final v12 behavior: built-in inline events, removal of the beta helper, native click/form handling, normalized submit-button labels, and opt-in 404 detection using title/body text. The patch applies cleanly. There is no matching open docs PR to update. I have requested owner approval for a supporting docs PR because this automation is authorized to push to existing PR branches. Publication of these instructions must follow the relevant script PR and the v12 release; the patch remains local pending that approval. |
* Fix problem with solana extension It seems Solana from Phantom has this namespace for their browser extension. I added a check for prevent them being detected as bots. * Move from _phantom to phantom * Run prettier via GitHub Action * Fix bug in checking phantom.solana * Always expose sa_pageview * Check for Cloudflare under attack mode param * Run prettier via GitHub Action * Test for all Cloudflare params * Clean subdomain hostname, fix auto events for links with child elements, run prettier * Add acron and remove trailing comma * Run prettier via GitHub Action * Save local changes [no ci] * Add jsdom unit tests * Fix default test without Browserstack * Add unit test to job * Make sure all node js version are the same * Allow overwriting referrer * Rename testing file * Run prettier in dry mode [no ci] * Add AGENTS guidance * Improve AGENTS.md * Add .cursorrules [no ci] * Add modular unit tests and helpers * Run browserstack not every time, and add linting * Run browserstack not every time, for real * Require prettier for brwoserstack too * Rename prettier to linting * chore: fix eslint errors * feat(auto-events): skip automation when data-simple-event present * Add manual build step * Rename to Testing Suite * fix metadata reload in appendMetadata * Bind sendBeacon calls * Add callback support to sa_pageview * Checkout dist after test:unit * Reuse query param filter for manual pageviews * test: add metadata collector attr test * Run npm run build * Add tel link tracking to auto events * Add unit tests for auto-events * Warn when metadata collector function is not found * Run npm run build * Fix delayed phone link events * fix allowParams regex to be case insensitive * Fix merged BrowserStack test setup * Collect compact query parameter names via ignore metrics * Run BrowserStack tests on Node.js 22.16 * Use numeric BrowserStack test exit codes * Sync v12 conflict files with current main * Use querykeys for query parameter name collection * Fix v12 CI after main sync * Document tracker compatibility and size priorities for agents (#115) * Document tracker compatibility and size priorities for agents * Update AGENTS.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: Adriaan <1079135+adriaandotcom@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Support inline events in the main v12 script (#106) * Support inline event attributes in the main v12 tracker Requested: integrate documented inline events without overengineering, keep the script small and website behavior intact, and open a separate PR based on and targeting v12. Use delegated click/submit handlers and keepalive GETs with the existing image fallback. Preserve installed inline helpers and make confirmed 404 detection opt-in. Add focused tests and patch the vulnerable Handlebars build dependency. * Simplify inline events after review Requested: remove compatibility checks for the beta helper, measure repeated attribute strings, clarify the DOM alias, and shorten submit-button metadata extraction. Keep literals because sharing them increases gzip size. Prefer trimmed, capped submitter labels and scan title/body text for 404s. Address the related bot feedback by running the complete unit suite in CI. * Normalize inline submit button label whitespace Requested: address scripts PR #106 review comment 4282258759 so formatting a button across multiple lines does not fragment button_text metadata. Collapse whitespace before trimming and applying the existing 200-character limit. Add a regression for equivalent compact and indented button markup, and regenerate the affected tracker bundles. Verification: regression fails on the previous compiled tracker; all 33 unit tests pass after the fix. Build, formatting, and diff checks pass. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Preserve distinct manual pageviews in v12 (#108) * Preserve distinct manual pageviews in v12 Requested in the compatibility review: fix query-only manual pageview loss and preserve full path-overwriter input. Keep the filtered outgoing path and review campaign attribution separately. Refs #107 * Preserve ignore-pages and hash routes for manual query paths Address automated review of item 1: normalize the manual query after the path-overwriter callback and before ignore-pages/hash handling, while keeping its query in the duplicate key. Refs #107 * Keep non-string manual paths compatible during query normalization Address review of item 1: only split string path values, preserving numeric manual paths and numeric path-overwriter results without introducing an exception into the customer page. Refs #107 * Preserve pathname-only input for path overwriters Requested during review: keep the existing path-overwriter behavior without query strings, while retaining distinct manual-query pageviews. Restore query splitting before getPath and update the callback and duplicate checks. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Leave phone click navigation to the website and browser (#111) Requested in item 5: prevent unwanted telephone navigation from analytics callbacks while retaining default outbound, email, download, and phone collection when data-collect is omitted. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Respect the configured namespace for manual pageviews (#110) * Respect the configured namespace for manual pageviews Requested in item 3: keep namespaced trackers from replacing another tracker manual pageview function. Reuse existing namespace and pageview text variables to reduce minified bytes. Refs #107 * Retain the free legacy manual pageview alias Address review of item 3: older namespaced installations still call sa_pageview. Keep that alias when free, without overwriting another tracker, and cover both function names in the regression test. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Fix cmd-click for auto events (#78) * feat(auto-events): allow cmd-click to open in new tab * Preserve browser navigation for automatic link events User request: recheck whether PR #78 remains relevant, fix the current issue, and add the commits to that PR. Current v12 still cancels modified clicks and redirects from tracking callbacks. Use listeners without replacing onclick and reuse the existing keepalive transport; retain event names and metadata. Rebuild current bundles while preserving existing SRI files. Validated with 41 unit tests, 12 deployment tests, typecheck, and 48 local click/transport scenarios. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Count Cloudflare challenge arrivals as unique (#112) * Recognize Cloudflare challenge markers only in query names Requested in the compatibility review: avoid path false positives and inspect raw query-only markers. Reuse the referrer helper without sending raw query values or adding bytes to light builds. Refs #107 * Refresh the Cloudflare compatibility fix on current v12 Requested: make issue #107 mergeable while preserving manual-query overrides, browser-query fallback, and existing filtering. Resolve the older Cloudflare branch against current v12 source and regenerate its bundles, preserving inline events and the merged phone-click fix. All 45 unit tests and 24 additional query payload checks pass. Refs #107 * Use the requested simple Cloudflare marker check on latest v12 User asked to replace the parameter-name regex with a literal ?__cf_ check, omit the extra same-URL guard, and merge current v12 into #112. Preserve the latest modified-click, namespace, manual-query, and phone-click fixes. Rebuild matching tracker bundles while retaining unrelated pinned auto-events artifacts. All 53 unit tests, 33 payload checks, changed JavaScript formatting, and diff checks pass. Manual-query overrides and location.search fallback remain intact. Refs #107 * Keep the simple Cloudflare check working after campaign parameters User requested a simple marker check and current v12 merge. Address review by accepting both ?__cf_ and appended &__cf_ with the short /[?&]__cf_/ expression, retaining the requested broad matching and omitting the declined same-URL guard. Update the existing regression and matching bundles. All 53 unit tests and 36 payload checks pass. Current v12 remains included. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: simple-analytics-ai[bot] <313929673+simple-analytics-ai[bot]@users.noreply.github.com> * Accept current and legacy script deployment headers (#117) * Accept current and legacy script deployment headers Requested: make issue #107 ready to merge. Fix the proven mismatch between compile.js output and deployment header validation while retaining older pinned header support. Add tests against the real deployment manifest, legacy and malformed headers, and run the lightweight deployment suite in CI. All 15 deployment tests, type checking, and diff checks pass. Visitor-facing scripts are unchanged. Refs #107 * Check headers for the current SRI version in deployment tests Requested: make issue #107 mergeable and address review on #117. Reuse latestVersionFromNames with the dist directory so the manifest regression follows the current SRI version rather than only v12. All 15 deployment tests and type checking pass. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Remove retired Cloudflare app build from v12 (#121) Requested: open a PR into v12 removing Cloudflare app options while keeping GTM settings and a working client-side guard. Preserve sa_settings and data-attribute behavior, guard browser access, and rebuild current tracker artifacts. Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * fix: retain phone metadata with titled events (#122) User requested checking unresolved comments with a focus on CodeRabbit, separate reviewable changes, and minimal shipped bytes. Extract phone metadata before applying the existing title-derived event name and cover the regression. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * test: require current script version in SRI header checks (#123) User requested verifying unresolved CodeRabbit feedback with separate minimal PRs. Require the discovered SRI version to match latest.js so an obsolete version cannot make deployment validation pass. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Stop ignoring screen size when scrolled is ignored (#127) The screen size check matched the "sc" prefix, which also matches "scrolled". Match "scre" so each metric can be ignored on its own. Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Set the loaded flag for custom event globals (#129) Automated events wait for window[saGlobal + "_loaded"], but the main script only set sa_event_loaded and the namespace flag. With a custom namespace or sa-global (for example ba_event), automated events never fired. Set the flag for the configured event function once it replaces the queue stub, and cover both setups in the unit tests. Rebuild the affected event-enabled distributions (+18 bytes minified, +4 to +9 bytes gzip). light.js and auto-events.js are unchanged. The rebuilt files also include the src change from #127, which had not been rebuilt into dist yet. Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Count label clicks once in inline events (#133) * Run the pageview callback when the pageview is skipped (#132) * Send visible time on page when the page gets hidden (#134) * Send visible time on page when the page gets hidden Count only visible time and send the cumulative value on every hide and on pagehide, so durations survive mobile app switches, screen locks and browser kills, back/forward cache restores no longer send increments, and Firefox tab closes are no longer lost. Every page view, including manual ones, closes the previous one. Optional data-pings sends the same value at visible-time thresholds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Keep invalid scroll readings out of later appends Don't write position() back into the scroll maximum, so a NaN reading on a zero-height layout can't stick for the rest of the page view, and drop the unobservable push branch of the sent watermark. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Send time on page only when the page gets hidden Leaving a page also makes it hidden, so the pagehide listener, the duplicate-send guard, the back/forward cache fallback and the beacon try/catch are no longer needed. Old browsers that don't fire visibilitychange on navigation lose those durations; this keeps the script small. Appends without values are not sent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Ignore huge ping values and hides before the first page view A ping value too large for setTimeout fired immediately and rescheduled itself in a loop, and ignored pages or manual mode sent an append for a page view that was never sent on every hide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Ask agents to weigh script size against edge cases (#136) * Ask agents to weigh script size against edge cases Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Stop asking agents to commit generated distributions The Dist section says feature work never commits dist/, which item 6 contradicted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Send the embedded script URL with pageviews (#138) * Use the caddy destination in the deploy header test The merge from main renamed the custom destination to caddy, so the header test matched no custom files and typecheck failed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Test that automatic link clicks are not cancelled Runs auto-events.js on a same-tab outbound link and checks that it binds a click listener instead of an onclick attribute, leaves the click alone, and asks sa_event for keepalive. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Drop the placeholder callback in automatic link events sendEvent already uses a noop for a missing callback, so 0 is enough to reach the keepalive argument. Saves 11 bytes minified, 1 gzip. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fall back to a pixel when sendBeacon throws Other scripts can replace navigator.sendBeacon with a wrapper that throws "Illegal invocation" (#42). Without the fallback no time on page arrives, the remaining pings stop and every hide throws an error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Limit the Cloudflare unique check to this site's referrers Cloudflare challenges run on the site itself, so a challenge marker in a referrer from another non-unique hostname no longer makes the first pageview unique. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Drop the referrer on Cloudflare challenge arrivals The challenge page replaced the original referrer, so the pageview only sent our own hostname. The queue turns pageviews with our own hostname as referrer non-unique, which undid the unique arrival. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Detect Cloudflare arrivals from the raw referrer host The cleaned referrer has no www. and can carry an overwritten hostname, so www sites kept sending their own hostname as referrer (which the queue turns non-unique) and sites with a matching non-unique hostname sent unique=false. Check the raw referrer first, before sameSite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Compare real hostnames to detect same-site referrers The cleaned referrer has no www. and uses the defined hostname, so on www sites and sites with data-hostname internal page loads and manual pageviews were sent as unique, and SPA pageviews and events on data-hostname sites lost their referrer. Compare the raw referrer host with the location host, and treat later pageviews as same-site. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Mark appends sent by pings Appends from data-pings now include ping: true, so the backend can block them for plans without pings. Appends sent on hide or navigation are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: ironbrands16 <103500025+ironbrands16@users.noreply.github.com> Co-authored-by: Adriaan <1079135+adriaanvanrossum@users.noreply.github.com> Co-authored-by: adriaanvanrossum <adriaanvanrossum@users.noreply.github.com> Co-authored-by: adriaandotcom <adriaandotcom@users.noreply.github.com> Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: simple-analytics-ai[bot] <313929673+simple-analytics-ai[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Simple Analytics Agent <github-agent@adriaan.simpleanalytics.com> Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix problem with solana extension It seems Solana from Phantom has this namespace for their browser extension. I added a check for prevent them being detected as bots. * Move from _phantom to phantom * Run prettier via GitHub Action * Fix bug in checking phantom.solana * Always expose sa_pageview * Check for Cloudflare under attack mode param * Run prettier via GitHub Action * Test for all Cloudflare params * Clean subdomain hostname, fix auto events for links with child elements, run prettier * Add acron and remove trailing comma * Run prettier via GitHub Action * Save local changes [no ci] * Add jsdom unit tests * Fix default test without Browserstack * Add unit test to job * Make sure all node js version are the same * Allow overwriting referrer * Rename testing file * Run prettier in dry mode [no ci] * Add AGENTS guidance * Improve AGENTS.md * Add .cursorrules [no ci] * Add modular unit tests and helpers * Run browserstack not every time, and add linting * Run browserstack not every time, for real * Require prettier for brwoserstack too * Rename prettier to linting * chore: fix eslint errors * feat(auto-events): skip automation when data-simple-event present * Add manual build step * Rename to Testing Suite * fix metadata reload in appendMetadata * Bind sendBeacon calls * Add callback support to sa_pageview * Checkout dist after test:unit * Reuse query param filter for manual pageviews * test: add metadata collector attr test * Run npm run build * Add tel link tracking to auto events * Add unit tests for auto-events * Warn when metadata collector function is not found * Run npm run build * Fix delayed phone link events * fix allowParams regex to be case insensitive * Fix merged BrowserStack test setup * Collect compact query parameter names via ignore metrics * Run BrowserStack tests on Node.js 22.16 * Use numeric BrowserStack test exit codes * Sync v12 conflict files with current main * Use querykeys for query parameter name collection * Fix v12 CI after main sync * Document tracker compatibility and size priorities for agents (#115) * Document tracker compatibility and size priorities for agents * Update AGENTS.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: Adriaan <1079135+adriaandotcom@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Support inline events in the main v12 script (#106) * Support inline event attributes in the main v12 tracker Requested: integrate documented inline events without overengineering, keep the script small and website behavior intact, and open a separate PR based on and targeting v12. Use delegated click/submit handlers and keepalive GETs with the existing image fallback. Preserve installed inline helpers and make confirmed 404 detection opt-in. Add focused tests and patch the vulnerable Handlebars build dependency. * Simplify inline events after review Requested: remove compatibility checks for the beta helper, measure repeated attribute strings, clarify the DOM alias, and shorten submit-button metadata extraction. Keep literals because sharing them increases gzip size. Prefer trimmed, capped submitter labels and scan title/body text for 404s. Address the related bot feedback by running the complete unit suite in CI. * Normalize inline submit button label whitespace Requested: address scripts PR #106 review comment 4282258759 so formatting a button across multiple lines does not fragment button_text metadata. Collapse whitespace before trimming and applying the existing 200-character limit. Add a regression for equivalent compact and indented button markup, and regenerate the affected tracker bundles. Verification: regression fails on the previous compiled tracker; all 33 unit tests pass after the fix. Build, formatting, and diff checks pass. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Preserve distinct manual pageviews in v12 (#108) * Preserve distinct manual pageviews in v12 Requested in the compatibility review: fix query-only manual pageview loss and preserve full path-overwriter input. Keep the filtered outgoing path and review campaign attribution separately. Refs #107 * Preserve ignore-pages and hash routes for manual query paths Address automated review of item 1: normalize the manual query after the path-overwriter callback and before ignore-pages/hash handling, while keeping its query in the duplicate key. Refs #107 * Keep non-string manual paths compatible during query normalization Address review of item 1: only split string path values, preserving numeric manual paths and numeric path-overwriter results without introducing an exception into the customer page. Refs #107 * Preserve pathname-only input for path overwriters Requested during review: keep the existing path-overwriter behavior without query strings, while retaining distinct manual-query pageviews. Restore query splitting before getPath and update the callback and duplicate checks. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Leave phone click navigation to the website and browser (#111) Requested in item 5: prevent unwanted telephone navigation from analytics callbacks while retaining default outbound, email, download, and phone collection when data-collect is omitted. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Respect the configured namespace for manual pageviews (#110) * Respect the configured namespace for manual pageviews Requested in item 3: keep namespaced trackers from replacing another tracker manual pageview function. Reuse existing namespace and pageview text variables to reduce minified bytes. Refs #107 * Retain the free legacy manual pageview alias Address review of item 3: older namespaced installations still call sa_pageview. Keep that alias when free, without overwriting another tracker, and cover both function names in the regression test. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Fix cmd-click for auto events (#78) * feat(auto-events): allow cmd-click to open in new tab * Preserve browser navigation for automatic link events User request: recheck whether PR #78 remains relevant, fix the current issue, and add the commits to that PR. Current v12 still cancels modified clicks and redirects from tracking callbacks. Use listeners without replacing onclick and reuse the existing keepalive transport; retain event names and metadata. Rebuild current bundles while preserving existing SRI files. Validated with 41 unit tests, 12 deployment tests, typecheck, and 48 local click/transport scenarios. --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Count Cloudflare challenge arrivals as unique (#112) * Recognize Cloudflare challenge markers only in query names Requested in the compatibility review: avoid path false positives and inspect raw query-only markers. Reuse the referrer helper without sending raw query values or adding bytes to light builds. Refs #107 * Refresh the Cloudflare compatibility fix on current v12 Requested: make issue #107 mergeable while preserving manual-query overrides, browser-query fallback, and existing filtering. Resolve the older Cloudflare branch against current v12 source and regenerate its bundles, preserving inline events and the merged phone-click fix. All 45 unit tests and 24 additional query payload checks pass. Refs #107 * Use the requested simple Cloudflare marker check on latest v12 User asked to replace the parameter-name regex with a literal ?__cf_ check, omit the extra same-URL guard, and merge current v12 into #112. Preserve the latest modified-click, namespace, manual-query, and phone-click fixes. Rebuild matching tracker bundles while retaining unrelated pinned auto-events artifacts. All 53 unit tests, 33 payload checks, changed JavaScript formatting, and diff checks pass. Manual-query overrides and location.search fallback remain intact. Refs #107 * Keep the simple Cloudflare check working after campaign parameters User requested a simple marker check and current v12 merge. Address review by accepting both ?__cf_ and appended &__cf_ with the short /[?&]__cf_/ expression, retaining the requested broad matching and omitting the declined same-URL guard. Update the existing regression and matching bundles. All 53 unit tests and 36 payload checks pass. Current v12 remains included. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: simple-analytics-ai[bot] <313929673+simple-analytics-ai[bot]@users.noreply.github.com> * Accept current and legacy script deployment headers (#117) * Accept current and legacy script deployment headers Requested: make issue #107 ready to merge. Fix the proven mismatch between compile.js output and deployment header validation while retaining older pinned header support. Add tests against the real deployment manifest, legacy and malformed headers, and run the lightweight deployment suite in CI. All 15 deployment tests, type checking, and diff checks pass. Visitor-facing scripts are unchanged. Refs #107 * Check headers for the current SRI version in deployment tests Requested: make issue #107 mergeable and address review on #117. Reuse latestVersionFromNames with the dist directory so the manifest regression follows the current SRI version rather than only v12. All 15 deployment tests and type checking pass. Refs #107 --------- Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Remove retired Cloudflare app build from v12 (#121) Requested: open a PR into v12 removing Cloudflare app options while keeping GTM settings and a working client-side guard. Preserve sa_settings and data-attribute behavior, guard browser access, and rebuild current tracker artifacts. Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * fix: retain phone metadata with titled events (#122) User requested checking unresolved comments with a focus on CodeRabbit, separate reviewable changes, and minimal shipped bytes. Extract phone metadata before applying the existing title-derived event name and cover the regression. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * test: require current script version in SRI header checks (#123) User requested verifying unresolved CodeRabbit feedback with separate minimal PRs. Require the discovered SRI version to match latest.js so an obsolete version cannot make deployment validation pass. Refs #107 Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> * Stop ignoring screen size when scrolled is ignored (#127) The screen size check matched the "sc" prefix, which also matches "scrolled". Match "scre" so each metric can be ignored on its own. Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Set the loaded flag for custom event globals (#129) Automated events wait for window[saGlobal + "_loaded"], but the main script only set sa_event_loaded and the namespace flag. With a custom namespace or sa-global (for example ba_event), automated events never fired. Set the flag for the configured event function once it replaces the queue stub, and cover both setups in the unit tests. Rebuild the affected event-enabled distributions (+18 bytes minified, +4 to +9 bytes gzip). light.js and auto-events.js are unchanged. The rebuilt files also include the src change from #127, which had not been rebuilt into dist yet. Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Count label clicks once in inline events (#133) * Run the pageview callback when the pageview is skipped (#132) * Send visible time on page when the page gets hidden (#134) * Send visible time on page when the page gets hidden Count only visible time and send the cumulative value on every hide and on pagehide, so durations survive mobile app switches, screen locks and browser kills, back/forward cache restores no longer send increments, and Firefox tab closes are no longer lost. Every page view, including manual ones, closes the previous one. Optional data-pings sends the same value at visible-time thresholds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Keep invalid scroll readings out of later appends Don't write position() back into the scroll maximum, so a NaN reading on a zero-height layout can't stick for the rest of the page view, and drop the unobservable push branch of the sent watermark. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Send time on page only when the page gets hidden Leaving a page also makes it hidden, so the pagehide listener, the duplicate-send guard, the back/forward cache fallback and the beacon try/catch are no longer needed. Old browsers that don't fire visibilitychange on navigation lose those durations; this keeps the script small. Appends without values are not sent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Ignore huge ping values and hides before the first page view A ping value too large for setTimeout fired immediately and rescheduled itself in a loop, and ignored pages or manual mode sent an append for a page view that was never sent on every hide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Ask agents to weigh script size against edge cases (#136) * Ask agents to weigh script size against edge cases Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Stop asking agents to commit generated distributions The Dist section says feature work never commits dist/, which item 6 contradicted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Send the embedded script URL with pageviews (#138) * Use the caddy destination in the deploy header test The merge from main renamed the custom destination to caddy, so the header test matched no custom files and typecheck failed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Test that automatic link clicks are not cancelled Runs auto-events.js on a same-tab outbound link and checks that it binds a click listener instead of an onclick attribute, leaves the click alone, and asks sa_event for keepalive. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Drop the placeholder callback in automatic link events sendEvent already uses a noop for a missing callback, so 0 is enough to reach the keepalive argument. Saves 11 bytes minified, 1 gzip. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fall back to a pixel when sendBeacon throws Other scripts can replace navigator.sendBeacon with a wrapper that throws "Illegal invocation" (#42). Without the fallback no time on page arrives, the remaining pings stop and every hide throws an error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Limit the Cloudflare unique check to this site's referrers Cloudflare challenges run on the site itself, so a challenge marker in a referrer from another non-unique hostname no longer makes the first pageview unique. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Drop the referrer on Cloudflare challenge arrivals The challenge page replaced the original referrer, so the pageview only sent our own hostname. The queue turns pageviews with our own hostname as referrer non-unique, which undid the unique arrival. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Detect Cloudflare arrivals from the raw referrer host The cleaned referrer has no www. and can carry an overwritten hostname, so www sites kept sending their own hostname as referrer (which the queue turns non-unique) and sites with a matching non-unique hostname sent unique=false. Check the raw referrer first, before sameSite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Compare real hostnames to detect same-site referrers The cleaned referrer has no www. and uses the defined hostname, so on www sites and sites with data-hostname internal page loads and manual pageviews were sent as unique, and SPA pageviews and events on data-hostname sites lost their referrer. Compare the raw referrer host with the location host, and treat later pageviews as same-site. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Release tested script builds and record deployments Stop committing dist/. Bumping VERSION in compile.js on main now publishes GitHub Release vN with the BrowserStack-tested build as scripts-vN.tar.gz. pnpm run deploy picks a release, verifies its digest, shows what each destination runs now, and records a GitHub deployment per destination. Fixes #139 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Record deployments per destination and release only from main - Deploy and record one destination at a time, so a failure on Caddy no longer records the CDN as failed (and the other way around). - Record a release only when latest.js is part of the deployment. - Show the newest successful deployment as what a destination runs, and list newer failed attempts. - Run the release workflow's version job only on main. - Build before the unit and deploy tests, now that dist/ is not committed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Address release and deployment review feedback - Only skip a release when a published release has its archive, so a failed publish after the draft can be retried. - List up to 100 releases, and only those with a recorded digest. - Say how many deployments were searched when none succeeded. - README: only deployments with latest.js are recorded. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Reuse an existing draft when publishing a release If an earlier run created the draft but failed to publish it, upload the archive to that draft and publish it at the tested commit, instead of creating a second draft for the same tag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Treat any published release as released Only drafts are retried, so a retry never attaches a new archive to a published release whose tag points at another commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: ironbrands16 <103500025+ironbrands16@users.noreply.github.com> Co-authored-by: Adriaan <1079135+adriaanvanrossum@users.noreply.github.com> Co-authored-by: adriaanvanrossum <adriaanvanrossum@users.noreply.github.com> Co-authored-by: adriaandotcom <adriaandotcom@users.noreply.github.com> Co-authored-by: Adriaan <1079135+adriaandotcom@users.noreply.github.com> Co-authored-by: Simple Analytics Codex <codex@simpleanalytics.invalid> Co-authored-by: simple-analytics-ai[bot] <313929673+simple-analytics-ai[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conductor Adriaan <conductor@adriaan.simpleanalytics.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
Closes #105
Add the documented inline event features to the main tracker (
src/default.js) while keeping the implementation small and preserving website behaviour:document, so dynamically inserted elements and currentdata-simple-event-*metadata are picked up without observers or polling. Every event carries atagfield; form events also carry the submit button label. Form field values are never read.event.submitter, preferringtextContentand falling back tovaluefor<input type="submit">. Internal whitespace is collapsed to single spaces before trimming and capping at 200 characters, so an icon/span button keeps a stablebutton_textwhen its markup is reindented.fetchGET (mode: "no-cors",credentials: "omit") so they survive navigation, falling back to the existing image pixel whenfetchis missing, throws, or rejects. Native navigation, form validation,formactionoverrides and SPA handlers stay in control.inline-events.jshelper is not detected or supported. Remove that extra script when using the built-in handlers, otherwise events are counted twice.data-detect-404="true"orsa_settings.detect404 = true. The tracker scans the page title and body text for "404", confirms with aHEADrequest, and only then sends apage_404event with the query string and fragment stripped from the URL metadata. Existing installations send no extra requests by default.light.jsis unaffected apart from the shared transport refactor.test/unit/inline-events.test.jsplus atest:unitscript, and run the full unit suite in the BrowserStack workflow afterpnpm run build.handlebarsbuild dependency from 4.7.6 to 4.7.10 and addjsdom26.1.0 for the unit tests.Changes:
Security implications
Has security impact - described as: the production tracker starts collecting new end-user interaction data on every install that supports events — event names, element tag,
data-simple-event-*attribute values and submit-button text — all of which are opt-in per element through HTML attributes, and form field values are explicitly excluded. Opt-in 404 detection additionally issues aHEADrequest to the customer's own origin for the current URL; the resultingpage_404metadata strips query strings and fragments. Inline events are sent withcredentials: "omit", and thehandlebarsbuild dependency is upgraded to a patched release.Testing
Run by Claude on this branch (
f1b1428) with Node 22.23.3:npm install— passed.npm run build— passed; the only regenerated-output difference versus the committeddist/is the build date stamp and local minifier output in the untouchedauto-events.jsbundle, so the committeddist/latest/latest.dev.jsis in sync withsrc/.npm run test:unit— 33 passing, 0 failing, including the new submit-label whitespace test.Reported by the author and not re-run here: local Chromium navigation checks, formatting, and bundle-size measurements.
Checklist